1#@gmic
2#
3#  File        : gmic_stdlib.gmic
4#                ( G'MIC command file )
5#
6#  Description : GREYC's Magic for Image Computing - Standard library
7#                ( https://gmic.eu )
8#
9#  Copyright   : David Tschumperlé
10#                ( https://tschumperle.users.greyc.fr/ )
11#
12#  Licenses    : This file is 'dual-licensed', you have to choose one
13#                of the two licenses below to apply.
14#
15#                CeCILL-C
16#                The CeCILL-C license is close to the GNU LGPL.
17#                ( http://cecill.info/licences/Licence_CeCILL-C_V1-en.html )
18#
19#            or  CeCILL v2.1
20#                The CeCILL license is compatible with the GNU GPL.
21#                ( http://cecill.info/licences/Licence_CeCILL_V2.1-en.html )
22#
23#  This software is governed either by the CeCILL or the CeCILL-C license
24#  under French law and abiding by the rules of distribution of free software.
25#  You can  use, modify and or redistribute the software under the terms of
26#  the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
27#  at the following URL: "http://cecill.info".
28#
29#  As a counterpart to the access to the source code and  rights to copy,
30#  modify and redistribute granted by the license, users are provided only
31#  with a limited warranty  and the software's author,  the holder of the
32#  economic rights,  and the successive licensors  have only  limited
33#  liability.
34#
35#  In this respect, the user's attention is drawn to the risks associated
36#  with loading,  using,  modifying and/or developing or reproducing the
37#  software by the user in light of its specific status of free software,
38#  that may mean  that it is complicated to manipulate,  and  that  also
39#  therefore means  that it is reserved for developers  and  experienced
40#  professionals having in-depth computer knowledge. Users are therefore
41#  encouraged to load and test the software's suitability as regards their
42#  requirements in conditions enabling the security of their systems and/or
43#  data to be ensured and,  more generally, to use and operate it in the
44#  same conditions as regards security.
45#
46#  The fact that you are presently reading this means that you have had
47#  knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
48#
49
50#------ Syntax rules for a G'MIC command file :
51#
52#*** General syntax :
53#
54# - Each line starting with 'command_name :' starts a new definition of the G'MIC custom command 'command_name'.
55# - Each line starting with '#' is a comment line.
56# - Any other line is considered as the continuation of a previously started G'MIC custom command.
57#
58#*** Specific rules for the command-line interface 'gmic':
59#
60# - A comment line starting with '#@cli' will be parsed by 'gmic' to print help for
61#    G'MIC custom commands (when invoked with option 'h'). More precisely :
62#
63#      _ '#@cli :: subsection' defines a new command subsection in the displayed help.
64#      _ '#@cli command_name : arguments_format1 : arguments_format2 : ... : (qualifier)'
65#        starts a new command description.
66#      _ '#@cli : description' add a new description line to the current command description.
67#      _ '#@cli : $ command_line' defines a new example of use of the current command.
68#      _ '#@cli : $$ _pagename' tells the command has a dedicated page in the web tutorial.
69#
70#*** Specific rules for the universal plug-in 'gmic-qt':
71#
72# - A comment line starting with '#@gui' will be parsed by the plug-in to define the filters tree.
73# - A comment line starting with '#@gui_xx' will define a filter only for a specific language 'xx'
74#    (e.g. 'en','fr'...).
75# - A comment line starting with '#@gui_xx hide(/Filter or folder name)' will hide the existing
76#    filter of folder for the locale 'xx'.
77# - More precisely, the syntax of a '#@gui' comment line is :
78#
79#    '#@gui Folder name'
80#
81# or
82#
83#    '#@gui Command name : command, preview_command (zoom_factor)[+] [: default_input_mode]
84#    '#@gui : parameter1 = typedef(arguments1...), parameter2 = typedef(arguments2...)'
85#    '#@gui : parameter3 = typedef(arguments3...),
86#
87#   where :
88#
89#      'command' is the G'MIC command name called to process the image.
90#
91#      'preview_command' is the G'MIC command name called to process the preview.
92#
93#           Note that you can optionally specify a float-valued factor>=0 between parentheses at the end of
94#           the 'preview_command' to force the default zoom factor used by the preview for this filter.
95#           Use (0) for a 1:1 preview, (1) for previewing the whole image, (2) for 1/2 image and so on...
96#           You can also put an additional '+' sign after the parenthesis to specify the rendered preview
97#           is still accurate for different zoom factors.
98#
99#      'default_input_mode' set the default input mode for that filter. It can be
100#        { x=none | .=active (default) | *=all | +=active & below | -=active & above | v=all visible | i=all invisible }
101#
102#      'parameter = typedef' tells about the names, types and default values of the filter parameters.
103#
104#           'typedef' can be :
105#
106#      _ 'bool(default_value={ 0 | 1 | false | true })':
107#          Add a boolean parameter (0 or 1) (as a checkbutton).
108#
109#      _ 'button(_alignment)':
110#          Add a boolean parameter (0 or 1) (as a button).
111#
112#      _ 'choice(_default_index,Choice0,..,ChoiceN)':
113#          Add a integer parameter (as a combobox).
114#
115#      _ 'color(R,_G,_B,_A)':
116#          Add R,G,B[,A] parameters (as a colorchooser).
117#
118#      _ 'point(_X,_Y,_removable={ -1 | 0 | 1 },_burst={ 0 | 1 },_R,_G,_B,_[-]A,_radius[%])':
119#          Add X,Y parameters (as a moveable point over the preview).
120#
121#      _ 'file[_in,_out](_default_filename)':
122#          Add a filename parameter (as a filechooser).
123#
124#      _ 'float(default_value,min_value,max_value)':
125#          Add a float-valued parameter (as a float slider).
126#
127#      _ 'folder(_default_foldername)':
128#          Add a foldername parameter (as a folderchooser).
129#
130#      _ 'int(default_value,min_value,max_value)':
131#          Add a integer parameter (as an integer slider).
132#
133#      _ 'link(_alignment,_label,URL)':
134#          Display a URL (do not add a parameter).
135#
136#      _ 'note(_label)':
137#          Display a label (do not add a parameter).
138#
139#      _ 'text(_is_multiline={ 0 | 1 },_default text)':
140#          Add a single or multi-line text parameter (as a text entry).
141#
142#      _ 'separator()':
143#          Display an horizontal separator (do not add a parameter).
144#
145#      _ 'value(value)':
146#          Add a pre-defined value parameter (not displayed).
147#
148#   Type separators '()' can be replaced by '[]' or '{}' if necessary (for instance if parentheses are required in
149#   an argument of the typedef, e.g in a text). You can also replace 'typedef' by '_typedef' to tell the plug-in not
150#   being responsive, i.e not to update the image preview when the corresponding parameter is modified.
151#   After the closing separator, you may specify a 'visibility state' character for the parameter, which can be
152#   { _0=Hidden | _1=Grayed-out | _2=Visible (default) }, opt. followed by a propagation character that tells
153#   if this visibility state must be propagated to neighboring non-valued interface widgets
154#   (s.a. separator(), link() or note()).
155#   This propagation character can be:
156#   { '+'=propagate forward | '-'=propagate backward | '*'=propagate in both directions }.
157#
158#   Use '_none_' as a special command or preview_command to tell the plug-in that the entry requires no G'MIC call.
159#
160#   A G'MIC command can set new values for each filter parameter, through the status (see command ''status'').
161#   To do so, the returned status must follow the syntax :
162#   '{params1}{params2}{..}{paramsN}' where N must be exactly equal to the number of parameters
163#   for the current filter. Optionnally, you can append to each {param} its visibility state suffix ( e.g: {param}_1 ).
164#
165#   A G'MIC command can also specify the output blending mode, the opacity and the position of each of the output image
166#   (i.e. layer in the plug-in). To do so, set the image name to something like:
167#   'mode(grainmerge),opacity(50),pos(30,50),name(name)'.
168#
169#     - Blending mode name should be the same as the argument of the 'blend' command.
170#     - Opacity is a float number in [0,100].
171#     - X and Y positions are integers.
172#     - 'name' is the layer name.
173#
174#-----------------------------------------------------------------------------------------------------------------------
175
176#---------------------------------
177#
178#@cli :: Global Options
179#
180#---------------------------------
181
182# This command is run when the cli tool 'gmic' is invoked without arguments on the command line.
183cli_noarg :
184  v 0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r _cli_noarg=1 version
185  +e[] "\n[gmic] No commands, options or data provided."
186  if {*,u}>0
187    +e[] "[gmic] (type "${c}"'gmic help'"$n" to print help, "${c}"'gmic demos'"$n" to launch demos)."
188  else
189    +e[] "[gmic] (type "${c}"'gmic help'"$n" to print help)."
190  fi
191  file_update=${_path_rc}update$_version.gmic
192  need_update={"Y = date(0); M = date(1); D = date(2); date_current = Y*365 + M*31 + D;
193                Y = date(0,'"{/$file_update}"'); M = date(1,'"{/$file_update}"');
194                D = date(2,'"{/$file_update}"'); date_file = Y*365 + M*31 + D;
195                date_current - date_file>=7"}
196  if $need_update
197    +e[] "[gmic] Updating commands..."
198    l[] update
199      +e[] "\r[gmic] Updating commands: "${g}"Succeeded!"$n
200    onfail
201      +e[] "\r[gmic] Updating commands: "${r}"Failed!"$n
202    endl
203  fi
204  +e[] "\n"
205
206# cli_start
207# This command is called each time the cli interface 'gmic' starts.
208# Overload it in your local user command file if necessary.
209cli_start :
210
211#@cli debug : (+)
212#@cli : Activate debug mode.
213#@cli : When activated, the G'MIC interpreter becomes very verbose and outputs additional log
214#@cli : messages about its internal state on the standard output (stdout).
215#@cli : This option is useful for developers or to report possible bugs of the interpreter.
216
217#@cli h : eq. to 'help'.
218h :
219  help $"*"
220
221#@cli help : command : (no arg)
222#@cli : Display help (optionally for specified command only) and exit.
223#@cli : (eq. to 'h').
224help : skip ${1=""}
225  use_vt100
226  if $!!=1" || w!=1 || d!=1 || s!=1" rm fi
227  if ['$1']==0
228
229    # Display global help.
230    _no_examples,_no_default_values,_no_tutorial_link=1
231    reference ascii
232
233  else
234
235    # Display help for a single command.
236    if narg("$1")" && "isfile(['{/$_path_user}'])
237      l[] it[] $_path_user onfail endl
238    fi
239    if isfile(['{/$_path_rc/update$_version.gmic}'])
240      l[] cimgz:$_path_rc/update$_version.gmic
241      onfail l[] it[] $_path_rc/update$_version.gmic onfail endl
242      endl
243    fi
244    if narg("$1")" && "narg($_path_commands) l[]
245      $_path_commands repeat $! l[$>] it[] {n} k. onfail rm 0 endl done
246    onfail
247    endl fi
248    if !$! return fi
249    a y
250
251    # Check that requested command exist.
252    ('"$1"') autocrop. {'-'}
253    if {i[-1,2]}==_']'" && "i!=_'[' l. s -,{'['} k[0] endl fi
254    command={t} rm.
255    s +,{'"#@cli "$command" :"'} s +,{'"#@cli "$command":"'} s +,{'"#@cli "$command"\n"'}
256    if $!==1
257      l[] m "foo : "$command # Detect command misspelling.
258        repeat 16 um $command done # Be sure the specified command does not exist anymore !
259        foo um foo
260      onfail ('${}') s -,{'"; did you mean "'} if $!>1 s[1] -,39 k[1] misspelling={t} fi rm
261      endl
262      if narg($misspelling) misspelling="; did you mean '"$_vt100_g$misspelling$_vt100_n"' ?" fi
263      +e[] "\n[gmic] No help available for command '"$_vt100_r$command$_vt100_n"'"$misspelling". "\
264           "\n       Try '"${_vt100_c}"gmic -h"$_vt100_n"' for global help."
265    else
266      a y
267      _no_categories=1
268      +parse_cli ascii,$command
269      if narg(${}) parse_cli ascii,${} fi # In case of shortcut, display also help for shortcut command.
270    fi
271  fi
272  +e[] "\n" rm q
273
274# Command to write reference documentation with various output modes.
275# $1 = output mode, can be { ascii | html | man }.
276# $2 = name of the folder containing additional .gmd pages (optional).
277# Reference and command documentation is written using the G'MIC-markdown syntax.
278reference : skip "${2=}"
279  m "_section : reference_section_$1 \"$""*\""
280  m "_text : reference_text_$1 \"$""*\""
281  l reference_begin_$1 reference_header_$1 onfail endl
282  _section "Usage"
283  _text \
284"~~~\ngmic [command1 [arg1_1,arg1_2,..]] .. [commandN [argN_1,argN_2,..]]\n~~~"\n\n\
285"`gmic` is the open-source interpreter of the \\\G'MIC language, a script-based programming "\
286"language dedicated to the design of possibly complex image processing pipelines and operators."\n\
287"It can be used to convert, manipulate, filter and visualize image datasets made of one or "\
288"several 1D/2D or 3D multi-spectral images."\n\
289\n\
290"This reference documentation describes all the technical aspects of the G'MIC framework, "\
291"in its current version ___"${-strver}"___."\n\
292\n\
293"As a starting point, you may want to visit our detailed tutorial pages, at: <https://gmic.eu/tutorial/>"
294
295  _section "Overall Context"
296  _text \
297"* At any time, \\G'MIC manages one list of numbered (and optionally named) pixel-based images, "\
298"entirely stored in computer memory (uncompressed)."\n\
299"* The first image of the list has index '0' and is denoted by '[0]'. The second image of the "\
300"list is denoted by '[1]', the third by '[2]' and so on."\n\
301"* Negative indices are treated in a periodic way: '[-1]' refers to the last image of the list, '[-2]' to the "\
302"penultimate one, etc. Thus, if the list has 4 images, '[1]' and '[-3]' both designate the second image of the list."\n\
303"* A named image may be also indicated by '[name]', if 'name' uses the character set `[a-zA-Z0-9_]` and does not "\
304"start with a number. Image names can be set or reassigned at any moment during the processing pipeline "\
305"(see command ''name'' for this purpose)."\n\
306"* G'MIC defines a set of various commands and substitution mechanisms to allow the design of complex "\
307"pipelines and operators managing this list of images, in a very flexible way: You can insert or remove images "\
308"in the list, rearrange image order, process images (individually or grouped), merge image data together, "\
309"display and output image files, etc."\n\
310"* Such a pipeline can define a new custom G'MIC command (stored in a user command file), and re-used "\
311"afterwards as a regular command, in a larger pipeline if necessary."
312
313  _section "Image Definition and Terminology"
314  _text \
315"* In \\G'MIC, each image is modeled as a 1D, 2D, 3D or 4D array of scalar values, uniformly "\
316"discretized on a rectangular/parallelepipedic domain."\n\
317"* The four dimensions of this array are respectively denoted by:"\n\
318"  - `width`, the number of image columns (size along the `x-axis`)."\n\
319"  - `height`, the number of image rows (size along the `y-axis`)."\n\
320"  - `depth`, the number of image slices (size along the `z-axis`). "\
321"The depth is equal to '1' for usual color or grayscale 2D images."\n\
322"  - `spectrum`, the number of image channels (size along the `c-axis`). "\
323"The spectrum is respectively equal to '3' and '4' for usual `RGB` and `RGBA` color images."\n\
324\n\
325"* There are no hard limitations on the size of the image along each dimension. For instance, the number of image "\
326"slices or channels can be of arbitrary size within the limits of the available memory."\n\
327"* The `width`, `height` and `depth` of an image are considered as spatial dimensions, while the `spectrum` has a "\
328"multi-spectral meaning. Thus, a 4D image in G'MIC should be most often regarded as a 3D dataset of multi-spectral "\
329"voxels. Most of the G'MIC commands will stick with this idea (e.g. command ''blur'' blurs images only along the "\
330"spatial `xyz`-axes)."\n\
331"* G'MIC stores all the image data as buffers of `float` values (32 bits, value range '[-3.4E38,+3.4E38]'. "\
332"It performs all its image processing operations with floating point numbers. Each image pixel takes "\
333"then 32bits/channel (except if double-precision buffers have been enabled during the compilation of the software, "\
334"in which case 64bits/channel can be the default)."\n\
335"* Considering `float`-valued pixels ensure to keep the numerical precision when executing image processing "\
336"pipelines. For image input/output operations, you may want to prescribe the image datatype to be different than "\
337"`float` (like `bool`, `char`, `int`, etc.). This is possible by specifying it as a file option when using "\
338"I/O commands. (see section ''Input/Output Properties'' to learn more about file options)."
339
340  _section "Items of a Processing Pipeline"
341  _text \
342"* In \\G'MIC, an image processing pipeline is described as a sequence of items separated by the "\
343"space character. Such items are interpreted and executed from the left to the right. For instance, the expression:"\n\
344"~~~\nfilename.jpg blur 3,0 sharpen 10 resize 200%,200% output file_out.jpg\n~~~\n"\
345"defines a valid pipeline composed of nine G'MIC items."\n\n\
346"* Each G'MIC item is a string that is either a __command__, a list of command __arguments__, "\
347"a __filename__ or a special __input string__."\n\
348"* Escape characters '\\' and double quotes '\"' can be used to define items containing spaces or "\
349"other special characters. For instance, the two strings `single\\ item` and `\"single item\"` "\
350"both define the same single item, with a space in it."
351
352  _section "Input Data Items"
353  _text \
354"* If a specified \\G'MIC item appears to be an existing filename, the corresponding image data "\
355"are loaded and inserted at the end of the image list (which is equivalent to the use of `input filename`). "\n\
356"* Special filenames `-` and `-.ext` stand for the standard input/output streams, optionally "\
357"forced to be in a specific 'ext' file format (e.g. `-.jpg` or `-.png`). "\n\
358"* The following special input strings may be used as G'MIC items to create and insert new "\
359"images with prescribed values, at the end of the image list:"\n\
360"  - '[selection]' or '[selection]xN': Insert 1 or N copies of already existing images. "\
361"'selection' may represent one or several images (see section ''Command Items and Selections'' to learn more "\
362"about selections)."\n\
363"  - 'width[%],_height[%],_depth[%],_spectrum[%],_values[xN]': Insert one or N images with specified "\
364"size and values (adding '%' to a dimension means __\"percentage of the size along the same axis\"__, "\
365"taken from the last image '[-1]'). Any specified dimension can be also written as "\
366"'[image]', and is then set to the size (along the same axis) of the existing specified image "\
367"'[image]'. 'values' can be either a sequence of numbers separated by commas ',', "\
368"or a mathematical expression, as e.g. in input item '256,256,1,3,[x,y,128]' which "\
369"creates a `256x256` RGB color image with a spatial shading on the red and green channels. "\
370"(see section ''Mathematical Expressions'' to learn more about mathematical expressions). "\n\
371"  - '(v1,v2,..)[xN]': Insert one or `N` new images from specified prescribed values. Value separator "\
372"inside parentheses can be ',' (column separator), ';' (row separator), '/' (slice separator) or "\
373"'^' (channel separator). For instance, expression '(1,2,3;4,5,6;7,8,9)' creates a 3x3 matrix (scalar image), "\
374"with values running from 1 to 9. "\n\
375"  - '(\\'string\\'[:delimiter])[xN]': Insert one or N new images from specified string, by filling "\
376"the images with the character codes composing the string. When specified, 'delimiter' tells about "\
377"the main orientation of the image. Delimiter can be 'x' (eq. to ',' which is the default), "\
378"'y' (eq. to ';'), 'z' (eq. to '/') or 'c' (eq. to '^'). "\
379"When specified delimiter is ',', ';', '/' or '^', the expression is actually equivalent to "\
380"'({\\'string\\'[:delimiter]})[xN]' (see section ''Substitution Rules'' for more information on the syntax)."\n\
381"  - '0[xN]': Insert one or N new `empty` images, containing no pixel data. "\
382"Empty images are used only in rare occasions."\n\
383\n\
384"* Input item 'name=value' declares a new variable 'name', or assign a new string value to an existing variable. "\
385"Variable names must use the character set `[a-zA-Z0-9_]` and cannot start with a number. "\n\
386"* A variable definition is always local to the current command except when it starts by the underscore "\
387"character '_'. In that case, it becomes also accessible by any command invoked outside the current command "\
388"scope (global variable)."\n\
389"* If a variable name starts with two underscores `__`, the global variable is also shared among different threads "\
390"and can be read/set by commands running in parallel (see command ''parallel'' for this purpose). "\
391"Otherwise, it remains local to the thread that defined it."\n\
392"* Numerical variables can be updated with the use of these special operators: "\
393"'+=' (addition), '-=' (subtraction), '*=' (multiplication), '/=' (division), '%=' (modulo), '&=' (bitwise and), "\
394"'|=' (bitwise or), '^=' (power), '<<=' and '>>' (bitwise left and right shifts). For instance, 'foo=1' 'foo+=3'."\n\
395"* Input item 'name.=string' appends specified `string` at the end of variable 'name'."\n\
396"* Input item 'name..=string' prepends specified `string` at the beginning of variable 'name'."\n\
397"* Multiple variable assignments and updates are allowed, with expressions: 'name1,name2,...,nameN=value' or "\
398"'name1,name2,...,nameN=value1,value2,...,valueN' where assignment operator '=' can be replaced by one of the "\
399"allowed operators (e.g. '+=')."\n\
400"* Variables usually store numbers or strings. Use command ''store'' to assign variables from image data "\
401"(and syntax `input $variable` to bring them back on the image list afterwards)."
402
403  _section "Command Items and Selections"
404  _text \
405"* A \\G'MIC item that is not a filename nor a special input string designates a 'command' "\
406"most of the time. Generally, commands perform image processing operations on one or several available images "\
407"of the list."\n\
408"* Reccurent commands have two equivalent names ('regular' and 'short'). For instance, command names "\
409"'resize' and 'r' refer to the same image resizing action."\n\
410"* A G'MIC command may have mandatory or optional __arguments__. Command arguments must be specified "\
411"in the next item on the command line. Commas ',' are used to separate multiple arguments of a single command, "\
412"when required."\n\
413"* The execution of a G'MIC command may be restricted only to a __subset__ of the image list, by "\
414"appending '[selection]' to the command name. Examples of valid syntaxes for 'selection' are: "\n\
415"  - 'command[-2]': Apply command only on the penultimate image '[-2]' of the list."\n\
416"  - 'command[0,1,3]': Apply command only on images '[0]', '[1]' and '[3]'."\n\
417"  - 'command[3-6]': Apply command only on images '[3]' to '[6]' (i.e, '[3]', '[4]', '[5]' and '[6]')."\n\
418"  - 'command[50%-100%]': Apply command only on the second half of the image list."\n\
419"  - 'command[0,-4--1]': Apply command only on the first image and the last four images."\n\
420"  - 'command[0-9:3]': Apply command only on images '[0]' to '[9]', with a step of 3 "\
421"(i.e. on images '[0]', '[3]', '[6]' and '[9]')."\n\
422"  - 'command[0-9:25%]': Apply command only on images '[0]' to '[9]', with a step of 25% "\
423"(i.e. on images '[0]', '[3]', '[6]' and '[9]')."\n\
424"  - 'command[0--1:2]': Apply command only on images of the list with even indices. "\n\
425"  - 'command[0,2-4,50%--1]': Apply command on images '[0]', '[2]', '[3]', '[4]' and on the second half of "\
426"the image list."\n\
427"  - 'command[^0,1]': Apply command on all images except the first two."\n\
428"  - 'command[name1,name2]': Apply command on named images 'name1' and 'name2'."\n\
429\n\
430"* Indices in selections are always sorted in increasing order, and duplicate indices are "\
431"discarded. For instance, selections '[3-1,1-3]' and '[1,1,1,3,2]' are both equivalent to "\
432"'[1-3]'. If you want to repeat a single command multiple times on an image, use a "\
433"'repeat..done' loop instead. Inverting the order of images for a command is achieved by "\
434"explicitly inverting the order of the images in the list, with command 'reverse[selection]'."\n\
435"* Command selections '[-1]', '[-2]' and '[-3]' are so often used they have their own shortcuts, respectively "\
436"'.', '..' and '...'. For instance, command 'blur..' is equivalent to 'blur[-2]'. "\
437"These shortcuts work also when specifying command arguments."\n\
438"* G'MIC commands invoked without '[selection]' are applied on all images of the list, i.e. the "\
439"default selection is '[0--1]' (except for command ''input'' whose default selection is '[-1]'')."\n\
440"* Prepending a single hyphen '-' to a G'MIC command is allowed. This may be useful to recognize "\
441"command items more easily in a one-liner pipeline (typically invoked from a shell). "\n\
442"* A G'MIC command prepended with a plus sign '+' does not act __in-place__ but inserts its result as one or "\
443"several new images at the end of the image list."\n\
444"* There are two different types of commands that can be run by the G'MIC interpreter:"\n\
445"  - __Built-in commands__ are the hard-coded functionalities in the interpreter core. They are thus compiled as "\
446"binary code and run fast, most of the time. Omitting an argument when invoking a built-in command is not permitted, "\
447"except if all following arguments are also omitted. "\
448"For instance, invoking 'plasma 10,,5' is invalid but 'plasma 10' is correct. "\n\
449"  - __Custom commands__, are defined as G'MIC pipelines of built-in or other custom commands. "\
450"They are parsed by the G'MIC interpreter, and thus run a bit slower than built-in commands. "\
451"Omitting arguments when invoking a custom command is permitted. For instance, expressions "\
452"`flower ,,,100,,2` or `flower ,` are correct. "\n\
453\n\
454"* Most of the existing commands in G'MIC are actually defined as __custom commands__. "\n\
455"* A user can easily add its own custom commands to the G'MIC interpreter (see section "\
456" ''Adding Custom Commands'' for more details). New built-in commands cannot be added (unless you modify the "\
457"G'MIC interpreter source code and recompile it)."
458
459  _section "Input/Output Properties"
460  _text \
461"* \\G'MIC is able to read/write most of the classical image file formats, including:"\n\
462"  - 2D grayscale/color files: `.png`, `.jpeg`, `.gif`, `.pnm`, `.tif`, `.bmp`, ..."\n\
463"  - 3D volumetric files: `.dcm`, `.hdr`, `.nii`, `.cube`, `.pan`, `.inr`, `.pnk`, ..."\n\
464"  - Video files: `.mpeg`, `.avi`, `.mp4`, `.mov`, `.ogg`, `.flv`, ..."\n\
465"  - Generic text or binary data files: `.gmz`, `.cimg`, `.cimgz`, `flo`, `ggr`, `gpl`, `.dlm`, `.asc`, "\
466"`.pfm`, `.raw`, `.txt`, `.h`."\n\
467"  - 3D mesh files: `.off`, `.obj` (output only)"\n\
468\n\
469"* When dealing with color images, G'MIC generally reads, writes and displays data using the usual "\
470"sRGB color space."\n\
471"* When loading a `.png` and `.tiff` file, the bit-depth of the input image(s) is returned to the status."\n\
472"* G'MIC is able to manage __3D objects__ that may be read from files or generated by G'MIC commands. "\
473"A 3D object is stored as a one-column scalar image containing the object data, in the "\
474"following order: { magic_number; sizes; vertices; primitives; colors; opacities }. "\
475"These 3D representations can be then processed as regular images (see command ''split3d'' for accessing "\
476"each of these 3D object data separately)."\n\
477"* Be aware that usual file formats may be sometimes not adapted to store all the available image "\
478"data, since G'MIC uses float-valued image buffers. For instance, saving an image that was "\
479"initially loaded as a 16bits/channel image, as a `.jpg` file will result in a loss of "\
480"information. Use the G'MIC-specific file extension `.gmz` to ensure that all data "\
481"precision is preserved when saving images."\n\
482"* Sometimes, file options may/must be set for file formats:"\n\
483"  - __Video files:__ Only sub-frames of an image sequence may be loaded, using the input expression "\
484"'filename.ext,[first_frame[,last_frame[,step]]]'. Set 'last_frame==-1' to tell it must be "\
485"the last frame of the video. Set 'step' to '0' to force an opened video file to be "\
486"opened/closed. Output framerate and codec can be also set by using the output expression "\
487"'filename.avi,_fps,_codec,_keep_open' where 'keep_open' can be { 0 | 1 }. 'codec' is a 4-char string "\
488"(see <http://www.fourcc.org/codecs.php> ) or '0' for the default codec. 'keep_open' "\
489"tells if the output video file must be kept open for appending new frames afterwards."\n\
490"  - `.cimg[z]` __files:__ Only crops and sub-images of .cimg files can be loaded, using the input "\
491"expressions 'filename.cimg,N0,N1', 'filename.cimg,N0,N1,x0,x1', "\
492"'filename.cimg,N0,N1,x0,y0,x1,y1', 'filename.cimg,N0,N1,x0,y0,z0,x1,y1,z1' or "\
493"'filename.cimg,N0,N1,x0,y0,z0,c0,x1,y1,z1,c1'. "\
494"Specifying '-1' for one coordinates stands for the maximum possible value. Output expression "\
495"'filename.cimg[z][,datatype]' can be used to force the output pixel type. 'datatype' can be "\
496"{ auto | bool | uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }. "\n\
497"  - `.raw` __binary files:__ Image dimensions and input pixel type may be specified when loading `.raw` "\
498"files with input expression 'filename.raw[,datatype][,width][,height[,depth[,dim[,offset]]]]]'. If no dimensions are "\
499"specified, the resulting image is a one-column vector with maximum possible height. Pixel "\
500"type can also be specified with the output expression 'filename.raw[,datatype]'. "\
501"'datatype' can be the same as for `.cimg[z]` files. "\n\
502"  - `.yuv` __files:__ Image dimensions must be specified when loading, and only sub-frames of an image "\
503"sequence may be loaded, using the input expression "\
504"'filename.yuv,width,height[,chroma_subsampling[,first_frame[,last_frame[,step]]]'. "\
505"'chroma_subsampling' can be { 420 | 422 | 444 }. "\
506"When saving, chroma subsampling mode can be specified with output expression "\
507"'filename.yuv[,chroma_subsampling]'. "\n\
508"  - `.tiff` __files:__ Only sub-images of multi-pages tiff files can be loaded, using the input "\
509"expression 'filename.tif,_first_frame,_last_frame,_step'. "\
510"Output expression 'filename.tiff,_datatype,_compression,_force_multipage,_use_bigtiff' can be used "\
511"to specify the output pixel type, as well as the compression method. "\
512"'datatype' can be the same as for `.cimg[z]` files. 'compression' can be "\
513" { none (default) | lzw | jpeg }. 'force_multipage' can be { 0=no (default) | 1=yes }. "\
514"'use_bigtiff' can be { 0=no | 1=yes (default) }."\n\
515"  - `.pdf` __files:__ When loading a file, the rendering resolution can be specified using the input expression "\
516"'filename.pdf,resolution', where 'resolution' is an unsigned integer value."\n\
517"  - `.gif` __files:__ Animated gif files can be saved, using the input expression "\
518"'filename.gif,fps>0,nb_loops'. Specify 'nb_loops=0' to get an infinite number of animation "\
519"loops (this is the default behavior)."\n\
520"  - `.jpeg` __files:__ The output quality may be specified (in %), using the output expression "\
521"'filename.jpg,30' (here, to get a 30% quality output). '100' is the default."\n\
522"  - `.mnc` __files:__ The output header can set from another file, using the output expression "\
523"'filename.mnc,header_template.mnc'. "\n\
524"  - `.pan`, `.cpp`, `.hpp`, `.c` and `.h` __files:__ The output datatype can be selected with output expression "\
525"'filename[,datatype]'. 'datatype' can be the same as for `.cimg[z]` files."\n\
526"  - `.gmic` __files:__ These filenames are assumed to be G'MIC custom commands files. Loading such a "\
527"file will add the commands it defines to the interpreter. Debug information can be "\
528"enabled/disabled by the input expression 'filename.gmic[,add_debug_info' where 'debug_info' can be "\
529"{ 0=false | 1=true }. "\n\
530"  - Inserting 'ext:' on the beginning of a filename (e.g. 'jpg:filename') forces G'MIC to "\
531"read/write the file as it would have been done if it had the specified extension `.ext`."\n\
532\n\
533"* Some input/output formats and options may not be supported, depending on the configuration "\
534"flags that have been set during the build of the G'MIC software."
535
536  _section "Substitution Rules"
537  _text \
538"* \\G'MIC items containing '$' or '{}' are substituted before being interpreted. Use these "\
539"substituting expressions to access various data from the interpreter environment."\n\
540"* '$name' and '${name}' are both substituted by the value of the specified named variable "\
541"(set previously by the item 'name=value'). If this variable has not been already set, the "\
542"expression is substituted by the highest positive index of the named image '[name]'. If no "\
543"image has this name, the expression is substituted by the value of the OS environment variable "\
544"with same name (it may be thus an empty string if it is not defined). "\n\
545"* The following reserved variables are predefined by the G'MIC interpreter: "\n\
546"  - '$!': The current number of images in the list."\n\
547"  - '$>' and '$<': The increasing/decreasing index of the latest (currently running) "\
548"'repeat...done' loop. '$>' goes from `0` (first loop iteration) to `nb_iterations - 1` (last iteration). "\
549"'$<' does the opposite. "\n\
550"  - '$/': The current call stack. Stack items are separated by slashes '/'."\n\
551"  - '$|': The current value (expressed in seconds) of a millisecond precision timer."\n\
552"  - '$^': The current verbosity level."\n\
553"  - '$_cpus': The number of computation cores available on your machine."\n\
554"  - '$_flags': The list of enabled flags when G'MIC interpreter has been compiled."\n\
555"  - '$_host': A string telling about the host running the G'MIC interpreter (e.g. `cli` or `gimp`)."\n\
556"  - '$_os': A string describing the running operating system."\n\
557"  - '$_path_rc': The path to the G'MIC folder used to store configuration files (its value is OS-dependent)."\n\
558"  - '$_path_user': The path to the G'MIC user file `.gmic` or `user.gmic` (its value is OS-dependent)."\n\
559"  - '$_path_commands': A list of all imported command files (stored as a list-valued variable)."\n\
560"  - '$_pid': The current process identifier, as an integer."\n\
561"  - '$_pixeltype': The type of image pixels (default: 'float')."\n\
562"  - '$_prerelease': For pre-releases, the date of the pre-release as `yymmdd`. "\
563"For stable releases, this variable is set to `0`."\n\
564"  - '$_version': A 3-digits number telling about the current version of the G'MIC interpreter "\
565" (e.g. '"$_version"'). "\n\
566"  - '$_vt100': Set to `1` if colored text output is allowed on the console. Otherwise, set to `0`."\n\
567\n\
568"* '$$name' and '$${name}' are both substituted by the G'MIC script code of the specified named "\
569"`custom command`, or by an empty string if no custom command with specified name exists. "\n\
570"* '${\"-pipeline\"}' is substituted by the __status value__ after the execution of the specified "\
571"G'MIC pipeline (see command ''status''). Expression '${}' thus stands for the current status value."\n\
572"* '{``string}' (starting with two backquotes) is substituted by a double-quoted version of the specified string."\n\
573"* '{/string}' is substituted by the escaped version of the specified string."\n\
574"* '{\\'string\\'[:delimiter]}' (between single quotes) is substituted by the sequence of character codes "\
575"that composes the specified string, separated by specified delimiter. Possible delimiters are "\
576"',' (default), ';', '/', '^' or ' '. For instance, item '{'foo'}' is substituted by '102,111,111' and "\
577"'{'foo':;}' by '102;111;111'."\n\
578"* '{image,feature[:delimiter]}' is substituted by a specific feature of the image '[image]'. "\
579"'image' can be either an image number or an image name. It can be also eluded, in which case, "\
580"the last image '[-1]' of the list is considered for the requested feature. "\
581"Specified 'feature' can be one of:"\n\
582"  - 'b': The image basename (i.e. filename without the folder path nor extension)."\n\
583"  - 'f': The image folder name."\n\
584"  - 'n': The image name or filename (if the image has been read from a file)."\n\
585"  - 't': The text string from the image values regarded as character codes."\n\
586"  - 'x': The image extension (i.e the characters after the last `.` in the image name)."\n\
587"  - '^': The sequence of all image values, separated by commas `,`."\n\
588"  - '@subset': The sequence of image values corresponding to the specified subset, and separated by commas `,`. "\n\
589"  - Any other 'feature' is considered as a __mathematical expression__ associated to the image '[image]' and is "\
590"substituted by the result of its evaluation (float value). For instance, expression '{0,w+h}' is substituted by "\
591"the sum of the width and height of the first image (see section ''Mathematical Expressions'' for more details). "\
592"If a mathematical expression starts with an underscore `_`, the resulting value is truncated to a readable format. "\
593"For instance, item '{_pi}' is substituted by '3.14159' (while '{pi}' is substituted by '3.141592653589793')."\n\
594"  - A 'feature' delimited by backquotes is replaced by a string whose character codes correspond to the list of "\
595"values resulting from the evaluation of the specified mathematical expression. For instance, item "\
596"'{`[102,111,111]`}' is substituted by 'foo' and item '{`vector8(65)`}' by 'AAAAAAAA'."\n\
597\n\
598"* '{*}' is substituted by the visibility state of the instant display window '#0' "\
599"(can be { 0=closed | 1=visible }."\n\
600"* '{*[index],feature1,...,featureN[:delimiter]}' is substituted by a specific set of features of the instant display "\
601"window '#0' (or '#index', if specified). Requested 'features' can be:"\n\
602"  - 'w': display width (i.e. width of the display area managed by the window)."\n\
603"  - 'h': display height (i.e. height of the display area managed by the window)."\n\
604"  - 'wh': display width x display height."\n\
605"  - 'd': window width (i.e. width of the window widget)."\n\
606"  - 'e': window height (i.e. height of the window widget)."\n\
607"  - 'de': window width x window height."\n\
608"  - 'u': screen width (actually independent on the window size)."\n\
609"  - 'v': screen height (actually independent on the window size)."\n\
610"  - 'uv': screen width x screen height."\n\
611"  - 'n': current normalization type of the instant display."\n\
612"  - 't': window title of the instant display."\n\
613"  - 'x': X-coordinate of the mouse position (or -1, if outside the display area)."\n\
614"  - 'y': Y-coordinate of the mouse position (or -1, if outside the display area)."\n\
615"  - 'b': state of the mouse buttons { 1=left-but. | 2=right-but. | 4=middle-but. }."\n\
616"  - 'o': state of the mouse wheel."\n\
617"  - 'k': decimal code of the pressed key if any, 0 otherwise."\n\
618"  - 'c': boolean (0 or 1) telling if the instant display has been closed recently."\n\
619"  - 'r': boolean telling if the instant display has been resized recently."\n\
620"  - 'm': boolean telling if the instant display has been moved recently."\n\
621"  - Any other 'feature' stands for a keycode name (in capital letters), and is substituted "\
622"by a boolean describing the current key state { 0=pressed | 1=released }."\n\
623"  - You can also prepend a hyphen '-' to a 'feature' (that supports it) to flush the "\
624"corresponding event immediately after reading its state (works for keys, mouse and window events)."\n\
625\n\
626"* Item substitution is __never__ performed in items between double quotes. One must break the quotes "\
627"to enable substitution if needed, as in '\"3+8 kg = \"{3+8}\" kg\"'. Using double quotes is then "\
628"a convenient way to disable the substitutions mechanism in items, when necessary."\n\
629"* One can also disable the substitution mechanism on items outside double quotes, by escaping the "\
630"`{`, `}` or `$` characters, as in `\\{3+4\\}\\ doesn\47t\\ evaluate`."
631
632  _section "Mathematical Expressions"
633  _text \
634"* \\G'MIC has an embedded __mathematical parser__, used to evaluate (possibly complex) math expressions "\
635"specified inside braces '{}', or formulas in commands that may take one as an argument (e.g. ''fill'' or ''eval'')."\n\
636"* When the context allows it, a formula is evaluated __for each pixel__ of the selected images "\
637"(e.g. ''fill'' or ''eval'')."\n\
638"* A math expression may return a __scalar__ or a __vector-valued__ result (with a fixed number of components)."\n\
639"The mathematical parser understands the following set of functions, operators and variables:"\n\
640"## Usual operators:"\n\
641"'||' (logical or), '&&' (logical and), '|' (bitwise or), '&' (bitwise and), "\
642"'!=', '==', '<=', '>=', '<', '>', '<<' (left bitwise shift), '>>' (right bitwise shift), '-', '+', '*', '/', "\
643"'%' (modulo), '^' (power), '!' (logical not), '~' (bitwise not), '++', '--', '+=', '-=', '*=', '/=', '%=', "\
644"'&=', '|=', '^=', '>>', '<<=' (in-place operators)."\n\
645"## Usual math functions:"\n\
646"'abs()', 'acos()', 'acosh()', 'arg()', 'arg0()', 'argkth()', 'argmax()', 'argmaxabs()', "\
647"'argmin()', 'argminabs()', 'asin()', 'asinh()', 'atan()', 'atan2()', 'atanh()', 'avg()', 'bool()', 'cbrt()', "\
648"'ceil()', 'cos()', 'cosh()', 'cut()', 'deg2rad()', 'erf()', 'erfinv()', 'exp()', 'fact()', 'fibo()', 'floor()', "\
649"'gauss()', 'gcd()', 'int()', 'isnan()', 'isnum()', 'isinf()', 'isint()', 'isbool()', 'isexpr()', 'isfile()', "\
650"'isdir()', 'isin()', 'kth()', 'log()', 'log2()', 'log10()', 'max()', 'maxabs()', 'med()', 'min()', 'minabs()', "\
651"'narg()', 'prod()', 'rad2deg()', 'rol()' (left bit rotation), 'ror()' (right bit rotation), 'round()', 'sign()', "\
652"'sin()', 'sinc()', 'sinh()', 'sqrt()', 'std()', 'srand(_seed)', 'sum()', 'tan()', 'tanh()', 'var()', 'xor()'."\n\
653\n\
654"* 'atan2(y,x)' is the version of 'atan()' with two arguments 'y' and 'x' (as in C/C++)."\n\
655"* 'permut(k,n,with_order)' computes the number of permutations of 'k' objects from a set of 'n' objects."\n\
656"* 'gauss(x,_sigma,_is_normalized)' returns `exp(-x^2/(2*s^2))/(is_normalized?sqrt(2*pi*sigma^2):1)`."\n\
657"* 'cut(value,min,max)' returns 'value' if it is in range '[min,max]', or 'min' or 'max' otherwise."\n\
658"* 'narg(a_1,...,a_N)' returns the number of specified arguments (here, 'N')."\n\
659"* 'arg(i,a_1,..,a_N)' returns the `i`-th argument 'a_i'."\n\
660"* 'isnum()', 'isnan()', 'isinf()', 'isint()', 'isbool()' test the type of the given number or expression, "\
661"and return '0' (false) or '1' (true)."\n\
662"* 'isfile('path')' (resp. 'isdir('path')') returns '0' (false) or '1' (true) whether its string argument is a "\
663"path to an existing file (resp. to a directory) or not."\n\
664"* 'isin(v,a_1,...,a_n)' returns '0' (false) or '1' (true) whether the first value 'v' appears in the set of other "\
665"values 'a_i'."\n\
666"* 'inrange(value,m,M,include_m,include_M)' returns '0' (false) or '1' (true) whether the specified value lies in "\
667"range '[m,M]' or not ('include_m' and 'includeM' tells how boundaries 'm' and 'M' are considered)."\n\
668"* 'argkth()', 'argmin()', 'argmax()', 'argminabs()', 'argmaxabs()'', 'avg()', 'kth()', 'min()', 'max()', 'minabs()', "\
669"'maxabs()', 'med()', 'prod()', 'std()', 'sum()' and 'var()' can be called with an arbitrary number of scalar/vector "\
670"arguments."\n\
671"* 'vargkth()', 'vargmin()', 'vargmax()', 'vargminabs()', 'vargmaxabs()', 'vavg()', 'vkth()', 'vmin()', "\
672"'vmax()', 'vminabs()', 'vmaxabs()', 'vmed()', 'vprod()', 'vstd()', 'vsum()' and 'vvar()' are the versions of the "\
673"previous function with vector-valued arguments."\n\
674"* 'round(value,rounding_value,direction)' returns a rounded value. 'direction' can be "\
675"{ -1=to-lowest | 0=to-nearest | 1=to-highest }."\n\
676"* 'lerp(a,b,t)' returns 'a*(1-t)+b*t'."\n\
677"* 'swap(a,b)' swaps the values of the given arguments."\n\
678"## Variable names:"\n\
679"Variable names below are pre-defined. They can be overridden."\n\
680"* 'l': length of the associated list of images."\n\
681"* 'k': index of the associated image, in '[0,l-1]'."\n\
682"* 'w': width of the associated image, if any ('0' otherwise)."\n\
683"* 'h': height of the associated image, if any ('0' otherwise)."\n\
684"* 'd': depth of the associated image, if any ('0' otherwise)."\n\
685"* 's': spectrum of the associated image, if any ('0' otherwise)."\n\
686"* 'r': shared state of the associated image, if any ('0' otherwise)."\n\
687"* 'wh': shortcut for width x height."\n\
688"* 'whd': shortcut for width x height x depth."\n\
689"* 'whds': shortcut for width x height x depth x spectrum (i.e. number of image values)."\n\
690"* 'im', 'iM', 'ia', 'iv', 'is', 'ip', 'ic', 'in': Respectively the minimum, maximum, average, variance, sum, "\
691"product, median value and L2-norm of the associated image, if any ('0' otherwise)."\n\
692"* 'xm', 'ym', 'zm', 'cm': The pixel coordinates of the minimum value in the associated image, "\
693"if any ('0' otherwise)."\n\
694"* 'xM', 'yM', 'zM', 'cM': The pixel coordinates of the maximum value in the associated image, "\
695"if any ('0' otherwise)."\n\
696"* All these variables are considered as __constant values__ by the math parser (for optimization purposes) "\
697"which is indeed the case most of the time. Anyway, this might not be the case, if function 'resize(#ind,..)' "\
698"is used in the math expression. If so, it is safer to invoke functions 'l()', 'w(_#ind)', 'h(_#ind)', ... 's(_#ind)' "\
699"and 'in(_#ind)' instead of the corresponding named variables."\n\
700"* 'i': current processed pixel value (i.e. value located at `(x,y,z,c)`) in the associated image, "\
701"if any ('0' otherwise)."\n\
702"* 'iN': N-th channel value of current processed pixel (i.e. value located at `(x,y,z,N)` in the associated image, "\
703"if any ('0' otherwise). 'N' must be an integer in range '[0,9]'."\n\
704"* 'R', 'G', 'B' and 'A' are equivalent to 'i0', 'i1', 'i2' and 'i3' respectively."\n\
705"* 'I': current vector-valued processed pixel in the associated image, if any ('0' otherwise). "\
706"The number of vector components is equal to the number of image channels (e.g. 'I' = `[ R,G,B ]` for a "\
707"`RGB` image)."\n\
708"* You may add '#ind' to any of the variable name above to retrieve the information for any "\
709"numbered image '[ind]' of the list (when this makes sense). For instance 'ia#0' denotes the average value of the "\
710"first image of the list)."\n\
711"* 'x': current processed column of the associated image, if any ('0' otherwise)."\n\
712"* 'y': current processed row of the associated image, if any ('0' otherwise)."\n\
713"* 'z': current processed slice of the associated image, if any ('0' otherwise)."\n\
714"* 'c': current processed channel of the associated image, if any ('0' otherwise)."\n\
715"* 't': thread id when an expression is evaluated with multiple threads ('0' means __master thread__)."\n\
716"* 'n': maximum number of threads when expression is evaluated in parallel (so that 't' goes from '0' to 'n-1')."\n\
717"* 'e': value of e, i.e. `2.71828...`."\n\
718"* 'pi': value of pi, i.e. `3.1415926...`."\n\
719"* 'u': a random value between '[0,1]', following a uniform distribution."\n\
720"* 'g': a random value, following a gaussian distribution of variance 1 (roughly in '[-6,6]')."\n\
721"* 'interpolation': value of the default interpolation mode used when reading pixel values with the pixel access "\
722"operators (i.e. when the interpolation argument is not explicitly specified, see below for more details on pixel "\
723"access operators). Its initial default value is '0'."\n\
724"* 'boundary': value of the default boundary conditions used when reading pixel values with the pixel access "\
725"operators (i.e. when the boundary condition argument is not explicitly specified, see below for more details "\
726"on pixel access operators). Its initial default value is '0'."\n\
727"* The last image of the list is always associated to the evaluations of 'expressions', e.g. G'MIC sequence "\
728"\n~~~\n256,128 fill {w}\n~~~\n will create a 256x128 image filled with value 256."\n\
729"## Vector calculus:"\n\
730"Most operators are also able to work with vector-valued elements."\n\
731"* '[a0,a1,...,aN-1]' defines a 'N'-dimensional vector with scalar coefficients 'ak'."\n\
732"* 'vectorN(a0,a1,,...,aN-1)' does the same, with the 'ak' being repeated periodically if only a few are specified."\n\
733"* 'vector(#N,a0,a1,,...,aN-1)' does the same, and can be used for any constant expression 'N'."\n\
734"* In previous expressions, the 'ak' can be vectors themselves, to be concatenated into a single vector."\n\
735"* The scalar element 'ak' of a vector 'X' is retrieved by 'X[k]'."\n\
736"* The sub-vector '[X[p],X[p+s]...X[p+s*(q-1)]]' (of size 'q') of a vector 'X' is retrieved by 'X[p,q,s]'."\n\
737"* 'expr(formula,_w,_h,_d,_s)' outputs a vector of size 'w*h*d*s' with values generated from "\
738"the specified formula, as if one were filling an image with dimensions '(w,h,d,s)'."\n\
739"* Equality/inequality comparisons between two vectors is done with operators '==' and '!='."\n\
740"* Some vector-specific functions can be used on vector values: "\
741"'cross(X,Y)' (cross product), 'dot(X,Y)' (dot product), 'size(X)' (vector dimension), "\
742"'sort(X,_is_increasing,_nb_elts,_size_elt)' (sorted values), 'reverse(A)' (reverse order of components), "\
743"'shift(A,_length,_boundary_conditions)' and 'same(A,B,_nb_vals,_is_case_sensitive)' (vector equality test)."\n\
744"* Function 'normP(u1,...,un)' computes the LP-norm of the specified vector ('P' being an `unsigned integer` constant "\
745"or 'inf'). If 'P' is omitted, the L2 norm is calculated."\n\
746"* Function 'resize(A,size,_interpolation,_boundary_conditions)' returns a resized version of a vector 'A' with "\
747"specified interpolation mode. 'interpolation' can be "\
748"{ -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos }, and "\
749"'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }."\n\
750"* Function 'find(A,B,_starting_index,_search_step)' returns the index where sub-vector 'B' appears in vector 'A', "\
751"(or '-1' if 'B' is not contained in 'A'). Argument 'A' can be also replaced by an image index '#ind'."\n\
752"* A `2`-dimensional vector may be seen as a complex number and used in those particular functions/operators: "\
753"'**' (complex multiplication), '//' (complex division), '^^' (complex exponentiation), "\
754"'**=' (complex self-multiplication), '//=' (complex self-division), '^^=' (complex self-exponentiation), "\
755"'cabs()' (complex modulus), 'carg()' (complex argument), 'cconj()' (complex conjugate), "\
756"'cexp()' (complex exponential), 'clog()' (complex logarithm),  'ccos()' (complex cosine), "\
757"'csin()' (complex sine), 'ctan()' (complex tangent), 'ccosh()' (complex hyperpolic cosine), "\
758"'csinh()' (complex hyperbolic sine) and 'ctanh()' (complex hyperbolic tangent)."\n\
759"* A `MN`-dimensional vector may be seen as a `M` x `N` matrix and used in those particular functions/operators: "\
760"'*' (matrix-vector multiplication), 'det(A)' (determinant), 'diag(V)' (diagonal matrix from a vector), "\
761"'eig(A)' (eigenvalues/eigenvectors), 'eye(n)' (n x n identity matrix), 'invert(A,_solver)' (matrix inverse), "\
762"'mul(A,B,_nb_colsB)' (matrix-matrix multiplication), 'pseudoinvert(A,_nb_colsA,_solver)', "\
763"'rot(u,v,w,angle)' (3D rotation matrix), 'rot(angle)' (2D rotation matrix), "\
764"'solve(A,B,_nb_colsB)' (solver of linear system A.X = B), 'svd(A,_nb_colsA)' (singular value decomposition), "\
765"'trace(A)' (matrix trace) and 'transpose(A,nb_colsA)' (matrix transpose). Argument 'nb_colsB' may be omitted if "\
766"it is equal to `1`".\n\
767"* 'mproj(S,nb_colsS,D,nb_colsD,method,max_iter,max_residual)' projects a matrix 'S' onto a dictionary (matrix) "\
768"'D'. Equivalent to command ''mproj'' but inside the math evaluator."\n\
769"* Specifying a vector-valued math expression as an argument of a command that operates on image values "\
770"(e.g. 'fill') modifies the whole spectrum range of the processed image(s), for each spatial coordinates `(x,y,z)`. "\
771"The command does not loop over the `c`-axis in this case."\n\
772"## String manipulation:"\n\
773"Character strings are defined and managed as vectors objects. "\
774"Dedicated functions and initializers to manage strings are:"\n\
775"* `['string']` and `'string'` define a vector whose values are the character codes of the "\
776"specified `character string` (e.g. `'foo'` is equal to `[ 102,111,111 ]`)."\n\
777"* `_'character'` returns the (scalar) byte code of the specified character (e.g. `_'A'` is equal to '65')."\n\
778"* A special case happens for __empty__ strings: Values of both expressions `['']` and `''` are '0'."\n\
779"* Functions 'lowercase()' and 'uppercase()' return string with all string characters lowercased or uppercased."\n\
780"* Function 'stov(str,_starting_index,_is_strict)' parses specified string 'str' and returns the value contained "\
781"in it."\n\
782"* Function 'vtos(expr,_nb_digits,_siz)' returns a vector of size 'siz' which contains the character representation "\
783"of values described by expression 'expr'. "\
784"'nb_digits' can be { -1=auto-reduced | 0=all | >0=max number of digits }."\n\
785"* Function 'echo(str1,str2,...,strN)' prints the concatenation of given string arguments on the console."\n\
786"* Function 'string(_#siz,str1,str2,...,strN)' generates a vector corresponding to the concatenation of given "\
787"string/number arguments."\n\
788"## Special operators:"\n\
789"* ';': expression separator. The returned value is always the last encountered expression. "\
790"For instance expression '1;2;pi' is evaluated as 'pi'."\n\
791"* '=': variable assignment. Variables in mathematical parser can only refer to numerical "\
792"values (vectors or scalars). Variable names are case-sensitive. Use this operator in conjunction with ';' to define "\
793"more complex evaluable expressions, such as \n~~~\nt = cos(x); 3*t^2 + 2*t + 1\n~~~\n"\
794"These variables remain __local__ to the mathematical parser and cannot be accessed outside the evaluated "\
795"expression."\n\
796"* Variables defined in math parser may have a __constant__ property, by specifying keyword 'const' before the "\
797"variable name (e.g. 'const foo = pi/4;'). The value set to such a variable must be indeed a __constant scalar__. "\
798"Constant variables allows certain types of optimizations in the math JIT compiler."\n\
799\
800"## Specific functions:"\n\
801"* 'addr(expr)': return the pointer address to the specified expression 'expr'. "\n\
802"* 'fill(target,expr)' or 'fill(target,index_name,expr)' fill the content of the specified target "\
803"(often vector-valued) using a given expression, e.g. `V = vector16(); fill(V,k,k^2 + k + 1);`. "\
804"For a vector-valued target, it is basically equivalent to: "\
805"`for (index_name = 0, index_name<size(target), ++index_name, target[index_name] = expr);`."\n\
806"* 'u(max)' or 'u(min,max)': return a random value between '[0,max]' or '[min,max]', following a uniform "\
807"distribution."\n\
808"* 'f2ui(value)' and 'ui2f(value)': Convert a large unsigned integer as a negative floating point value "\
809"(and vice-versa), so that 32bits floats can be used to store large integers while keeping a unitary precision."\n\
810"* 'i(_a,_b,_c,_d,_interpolation_type,_boundary_conditions)': return the value of the pixel located at position "\
811"`(a,b,c,d)` in the associated image, if any ('0' otherwise). "\
812"'interpolation_type' can be { 0=nearest neighbor | 1=linear | 2=cubic }. "\
813"'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. "\
814"Omitted coordinates are replaced by their default values which are respectively 'x', 'y', 'z', 'c', 'interpolation' "\
815"and 'boundary'. For instance command \n~~~\nfill 0.5*(i(x+1)-i(x-1))\n~~~\n will estimate the X-derivative of an "\
816"image with a classical finite difference scheme."\n\
817"* 'j(_dx,_dy,_dz,_dc,_interpolation_type,_boundary_conditions)' does the same for the pixel located at position "\
818"`(x+dx,y+dy,z+dz,c+dc)` (pixel access relative to the current coordinates)."\n\
819"* 'i[offset,_boundary_conditions]' returns the value of the pixel located at specified 'offset' in the associated "\
820"image buffer (or '0' if offset is out-of-bounds)."\n\
821"* 'j[offset,_boundary_conditions]' does the same for an offset relative to the current pixel coordinates "\
822"`(x,y,z,c)`."\n\
823"* 'i(#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)', "\
824"'j(#ind,_dx,_dy,_dz,_dc,_interpolation,_boundary_conditions)', 'i[#ind,offset,_boundary_conditions]' and "\
825"'i[offset,_boundary_conditions]' are similar expressions used to access pixel values for any numbered image '[ind]' "\
826"of the list."\n\
827"* 'I/J[offset,_boundary_conditions]' and 'I/J(#ind,_x,_y,_z,_interpolation,_boundary_conditions)' do the same as "\
828"'i/j[offset,_boundary_conditions]' and 'i/j(#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)' but return a "\
829"vector instead of a scalar (e.g. a vector `[ R,G,B ]` for a pixel at `(a,b,c)` in a color image)."\n\
830"* 'crop(_#ind,_x,_y,_z,_c,_dx,_dy,_dz,_dc,_boundary_conditions)' returns a vector whose values come from the "\
831"cropped region of image '[ind]' (or from default image selected if 'ind' is not specified). Cropped region starts "\
832"from point `(x,y,z,c)` and has a size of `dx x dy x dz x dc`. Arguments for coordinates and sizes can be omitted "\
833"if they are not ambiguous (e.g. 'crop(#ind,x,y,dx,dy)' is a valid invocation of this function)."\n\
834"* 'draw(_#ind,S,x,y,z,c,dx,_dy,_dz,_dc,_opacity,_M,_max_M)' draws a sprite 'S' in image '[ind]' "\
835"(or in default image selected if 'ind' is not specified) at coordinates `(x,y,z,c)`. "\
836"The size of the sprite `dx x dy x dz x dc` must be specified. You can also specify a corresponding opacity mask "\
837"'M' if its size matches 'S'."\n\
838"* 'polygon(_#ind,nb_vertices,coords,_opacity,_color)' draws a filled polygon in image '[ind]' (or in default image "\
839"selected if 'ind' is not specified) at specified coordinates. It draws a single line if 'nb_vertices' is set to 2."\n\
840"* 'polygon(_#ind,-nb_vertices,coords,_opacity,_pattern,_color)' draws a outlined polygon in image '[ind]' (or in "\
841"default image selected if 'ind' is not specified) at specified coordinates and with specified line pattern. "\
842"It draws a single line if 'nb_vertices' is set to 2."\n\
843"* 'ellipse(_#ind,xc,yc,radius1,_radius2,_angle,_opacity,_color)' draws a filled ellipse in image '[ind]' "\
844"(or in default image selected if 'ind' is not specified) with specified coordinates."\n\
845"* 'ellipse(_#ind,xc,yc,-radius1,-_radius2,_angle,_opacity,_pattern,_color)' draws an outlined ellipse in image "\
846"'[ind]' (or in default image selected if 'ind' is not specified)."\n\
847"* 'resize(#ind,w,_h,_d,_s,_interp,_boundary_conditions,_cx,_cy,_cz,_cc)' resizes an image of the associated list "\
848"with specified dimension and interpolation method. When using this function, you should consider retrieving the "\
849"(non-constant) image dimensions using the dynamic functions 'w(_#ind)', 'h(_#ind)', 'd(_#ind)', 's(_#ind)', "\
850"'wh(_#ind)', 'whd(_#ind)' and 'whds(_#ind)' instead of the corresponding constant variables."\n\
851"* 'if(condition,expr_then,_expr_else)': return value of 'expr_then' or 'expr_else', depending on the value of "\
852"'condition' { 0=false | other=true }. 'expr_else' can be omitted in which case '0' is returned if the condition "\
853"does not hold. Using the ternary operator 'condition?expr_then[:expr_else]' gives an equivalent expression. "\
854"For instance, G'MIC commands \n~~~\nfill if(x%10==0,255,i)\n~~~\n and \n~~~\nfill x%10?i:255\n~~~\n both draw blank "\
855"vertical lines on every 10th column of an image."\n\
856"* 'do(expression,_condition)' repeats the evaluation of 'expression' until 'condition' vanishes "\
857"(or until 'expression' vanishes if no 'condition' is specified). For instance, the expression: "\
858"\n~~~\nif(N<2,N,n=N-1;F0=0;F1=1;do(F2=F0+F1;F0=F1;F1=F2,n=n-1))\n~~~\n returns the N-th value of the Fibonacci "\
859"sequence, for 'N>=0' (e.g., '46368' for 'N=24'). 'do(expression,condition)' always evaluates the specified "\
860"expression at least once, then check for the loop condition. When done, it returns the last value of 'expression'."\n\
861"* 'for(init,condition,_procedure,body)' first evaluates the expression 'init', then iteratively evaluates 'body' "\
862"(followed by 'procedure' if specified) while 'condition' holds (i.e. not zero). It may happen that no iterations are "\
863"done, in which case the function returns 'nan'. Otherwise, it returns the last value of 'body'. "\
864"For instance, the expression: \n~~~\nif(N<2,N,for(n=N;F0=0;F1=1,n=n-1,F2=F0+F1;F0=F1;F1=F2))\n~~~\n "\
865"returns the 'N'-th value of the Fibonacci sequence, for 'N>=0' (e.g., '46368' for 'N=24')."\n\
866"* 'while(condition,expression)' is exactly the same as 'for(init,condition,expression)' without the specification of "\
867"an initializing expression."\n\
868"* 'repeat(nb_iters,expr)' or 'fill(nb_iters,iter_name,expr)' run 'nb_iters' iterations of the specified expression "\
869"'expr', e.g. `V = vector16(); repeat(16,k,V[k] = k^2 + k + 1);`. "\
870"It is basically equivalent to: "\
871"`for (iter_name = 0, iter_name<nb_iters, ++iter_name, expr);`."\n\
872"* 'break()' and 'continue()' respectively breaks and continues the current running bloc "\
873"(loop, init or main environment)."\n\
874"* 'fsize('filename')' returns the size of the specified 'filename' (or '-1' if file does not exist)."\n\
875"* 'date(attr,'path')' returns the date attribute for the given 'path' (file or directory), "\
876"with 'attr' being { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }, or a vector "\
877"of those values."\n\
878"* 'date(_attr)' returns the specified attribute for the current (locale) date (attributes being "\
879"{ 0...6=same meaning as above | 7=milliseconds })."\n\
880"* 'print(expr1,expr2,...)' or 'print(#ind)' prints the value of the specified expressions (or image information) "\
881"on the console, and returns the value of the last expression (or 'nan' in case of an image). "\
882"Function 'prints(expr)' also prints the string composed of the character codes defined by the vector-valued "\
883"expression (e.g. 'prints('Hello')')."\n\
884"* 'debug(expression)' prints detailed debug info about the sequence of operations done by the math parser to "\
885"evaluate the expression (and returns its value)."\n\
886"* 'display(_X,_w,_h,_d,_s)' or 'display(#ind)' display the contents of the vector 'X' (or specified image) and "\
887"wait for user events. if no arguments are provided, a memory snapshot of the math parser environment is displayed "\
888"instead."\n\
889"* 'begin(expression)' and 'end(expression)' evaluates the specified expressions only once, respectively at the "\
890"beginning and end of the evaluation procedure, and this, even when multiple evaluations are required "\
891"(e.g. in 'fill \">begin(foo = 0); ++foo\"')."\n\
892"* 'copy(dest,src,_nb_elts,_inc_d,_inc_s,_opacity)' copies an entire memory block of 'nb_elts' elements starting "\
893"from a source value 'src' to a specified destination 'dest', with increments defined by 'inc_d' and 'inc_s' "\
894"respectively for the destination and source pointers."\n\
895"* 'stats(_#ind)' returns the statistics vector of the running image '[ind]', i.e the vector "\
896"`[ im,iM,ia,iv,xm,ym,zm,cm,xM,yM,zM,cM,is,ip ]` (14 values)."\n\
897"* 'ref(expr,a)' references specified expression 'expr' as variable name 'a'."\n\
898"* 'unref(a,b,...)' destroys references to the named variable given as arguments."\n\
899"* 'breakpoint()' inserts a possible computation breakpoint (useless with the cli interface)."\n\
900"* '_(comment) expr' just returns expression 'expr' (useful for inserting inline comments in math expressions)."\n\
901"* 'run('pipeline')' executes the specified G'MIC pipeline as if it was called outside the currently evaluated "\
902"expression."\n\
903"* 'set(A,\\'variable_name\\')' set the G'MIC variable '$variable_name' with the value of expression 'A'. If 'A' is"\
904" a vector-valued variable, it is assumed to encode a string."\n\
905"* 'store(A,\\'variable_name\\',_w,_h,_d,_s,_is_compressed)' transfers the data of vector 'A' as a "\
906"`w x h x d x s` image to the G'MIC variable '$variable_name'. Thus, the data becomes available outside the math "\
907"expression (that is equivalent to using the regular command ''store'', but directly in the math expression)."\n\
908"* 'get(\\'variable_name\\',_size,_return_as_string)' returns the value of the specified variable, as a vector of "\
909"'size' values, or as a scalar (if 'size' is zero or not specified)."\n\
910"* 'name(_#ind,size)' returns a vector of size 'size', whose values are the characters codes of the name of image "\
911"'[ind]' (or default image selected if 'ind' is not specified)."\n\
912"* 'correlate(I,wI,hI,dI,sI,K,wK,hK,dK,sK,_boundary_conditions,_is_normalized,_channel_mode,_xcenter,_ycenter,"\
913"_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,_xdilation,_ydilation,_zdilation,"\
914"_interpolation_type)' returns the correlation, unrolled as a vector, of the `wI x hI x dI x sI`-sized image 'I' "\
915"with the `wK x hK x dK x sK`-sized kernel 'K' (the meaning of the other arguments are the same as in command "\
916"'correlate'). Similar function 'convolve(...)' is also defined for computing the convolution between 'I' and 'K'."\n\
917\
918"## User-defined macros:"\n\
919"* Custom macro functions can be defined in a math expression, using the assignment operator "\
920"'=', e.g. \n~~~\nfoo(x,y) = cos(x + y); result = foo(1,2) + foo(2,3)\n~~~\n"\n\
921"* Trying to override a built-in function (e.g. 'abs()') has no effect."\n\
922"* Overloading macros with different number of arguments is possible. Re-defining a previously defined macro with "\
923"the same number of arguments discards its previous definition."\n\
924"* Macro functions are indeed processed as __macros__ by the mathematical evaluator. You should avoid invoking them "\
925"with arguments that are themselves results of assignments or self-operations. "\
926"For instance, \n~~~\nfoo(x) = x + x; z = 0; foo(++z)\n~~~\n returns '4' rather than expected value '2'."\n\
927"* When substituted, macro arguments are placed inside parentheses, except if a number sign "\
928"'#' is located just before or after the argument name. For instance, expression \n"\
929"~~~\nfoo(x,y) = x*y; foo(1+2,3)\n~~~\n "\
930"returns '9' (being substituted as '(1+2)*(3)'), while expression \n~~~\nfoo(x,y) = x#*y#; foo(1+2,3)\n~~~\n "\
931"returns '7' (being substituted as '1+2*3')."\n\
932"* Number signs appearing between macro arguments function actually count for __empty__ separators. They may be used "\
933"to force the substitution of macro arguments in unusual places, e.g. as in \n~~~\nstr(N) = ['I like N#'];\n~~~\n"\
934\
935"## Multi-threaded and in-place evaluation:"\n\
936"* If your image data are large enough and you have several CPUs available, it is likely that the math expression "\
937"passed to a 'fill', 'eval' or 'input' commands is evaluated in parallel, using multiple computation threads."\n\
938"* Starting an expression with ':' or '*' forces the evaluations required for an image to be run in parallel, "\
939"even if the amount of data to process is small (beware, it may be slower to evaluate in this case!). "\
940"Specify ':' (rather than '*') to avoid possible image copy done before evaluating the expression "\
941"(this saves memory, but do this only if you are sure this step is not required!)"\n\
942"* If the specified expression starts with '>' or '<', the pixel access operators 'i()', 'i[]', 'j()' and 'j[]' "\
943"return values of the image being currently modified, in forward ('>') or backward ('<') order. "\
944"The multi-threading evaluation of the expression is disabled in this case."\n\
945"* Function 'critical(expr)' forces the execution of the given expression in a single thread at a time."\n\
946"* 'begin_t(expr)' and 'end_t(expr)' evaluates the specified expression once for each running thread "\
947"(so possibly several times) at the beginning and the end of the evaluation procedure."\n\
948"* 'merge(variable,operator)' tells to merge the local variable value computed by threads, with the specified "\
949"operator, when all threads have finished computing."\n\
950"* Expressions 'i(_#ind,x,_y,_z,_c)=value', 'j(_#ind,x,_y,_z,_c)=value', 'i[_#ind,offset]=value' and "\
951"'j[_#ind,offset]=value' set a pixel value at a different location than the running one in the image '[ind]' "\
952"(or in the associated image if argument '#ind' is omitted), either with global coordinates/offsets "\
953"(with 'i(...)' and 'i[...]'), or relatively to the current position `(x,y,z,c)` (with 'j(...)' and 'j[...]'). "\
954"These expressions always return 'value'."
955
956  _section "Image and Data Viewers"
957  _text \
958"* \\G'MIC has some very handy embedded __visualization modules__, for 1D signals (command ''plot''), "\
959"1D/2D/3D images (command ''display'') and 3D vector objects (command ''display3d''). It manages interactive views "\
960"of the selected image data."\n\
961"* The following actions are available in the interactive viewers:"\n\
962"  - `(mousewheel)`: Zoom in/out."\n\
963"  - `ESC`: Close window."\n\
964"  - `CTRL+D`: Increase window size."\n\
965"  - `CTRL+C`: Decrease window size."\n\
966"  - `CTRL+R`: Reset window size."\n\
967"  - `CTRL+F`: Toggle fullscreen mode."\n\
968"  - `CTRL+S`: Save current view as a numbered file 'gmic_xxxx.ext'."\n\
969"  - `CTRL+O`: Save copy of the viewed data, as a numbered file 'gmic_xxxx.ext'."\n\
970\n\
971"* Actions specific to the __1D/2D image viewer__ (command ''display'') are:"\n\
972"  - `Left mouse button`: Create an image selection and zoom into it."\n\
973"  - `Middle mouse button`, or `CTRL+left mouse button`: Move image."\n\
974"  - `Mouse wheel` or `PADD+/-`: Zoom in/out."\n\
975"  - `Arrow keys`: Move image left/right/up/down."\n\
976"  - `CTRL+A`: Enable/disable transparency (show alpha channel)."\n\
977"  - `CTRL+N`: Change normalization mode (can be { none | normal | channel-by-channel })."\n\
978"  - `CTRL+SPACE`: Reset view."\n\
979"  - `CTRL+X`: Show/hide axes."\n\
980"  - `CTRL+Z`: Hold/release aspect ratio."\n\
981\n\
982"* Actions specific to the __3D volumetric image viewer__ (command ''display'') are:"\n\
983"  - `CTRL+P`: Play z-stack of frames as a movie."\n\
984"  - `CTRL+V`: Show/hide 3D view on bottom right zone."\n\
985"  - `CTRL+X`: Show/hide axes."\n\
986"  - `CTRL+(mousewheel)`: Go up/down."\n\
987"  - `SHIFT+(mousewheel)`: Go left/right."\n\
988"  - `Numeric PAD`: Zoom in/out (`+`/`-`) and move through zoomed image (digits)."\n\
989"  - `BACKSPACE`: Reset zoom scale."\n\
990\n\
991"* Actions specific to the __3D object viewer__ (command ''display3d'') are:"\n\
992"  - `(mouse)+(left mouse button)`: Rotate 3D object."\n\
993"  - `(mouse)+(right mouse button)`: Zoom 3D object."\n\
994"  - `(mouse)+(middle mouse button)`: Shift 3D object."\n\
995"  - `F1 ... F6`: Toggle between different 3D rendering modes."\n\
996"  - `F7/F8`: Decrease/increase focale."\n\
997"  - `F9`: Select animation mode."\n\
998"  - `F10`: Select animation speed."\n\
999"  - `SPACE`: Start/stop animation."\n\
1000"  - `CTRL+A`: Show/hide 3D axes."\n\
1001"  - `CTRL+B`: Switch between available background."\n\
1002"  - `CTRL+G`: Save 3D object, as numbered file 'gmic_xxxx.obj'."\n\
1003"  - `CTRL+L`: Show/hide outline."\n\
1004"  - `CTRL+P`: Print current 3D pose on stderr."\n\
1005"  - `CTRL+T`: Switch between single/double-sided 3D modes."\n\
1006"  - `CTRL+V`: Start animation with video output."\n\
1007"  - `CTRL+X`: Show/hide 3D bounding box."\n\
1008"  - `CTRL+Z`: Enable/disable z-buffered rendering."
1009
1010  _section "Adding Custom Commands"
1011  _text \
1012"* New custom commands can be added by the user, through the use of \\G'MIC __custom commands files__."\n\
1013"* A command file is a simple text file, where each line starts either by "\
1014"\n~~~\ncommand_name: command_definition\n~~~\n or \n~~~\ncommand_definition (continuation)\n~~~\n"\n\
1015"* At startup, G'MIC automatically includes user's command file '$HOME/.gmic' (on __Unix__) or "\
1016"'%APPDATA%/user.gmic' (on __Windows__). The CLI tool 'gmic' automatically runs the command "\
1017"'cli_start' if defined."\n\
1018"* Custom command names must use character set `[a-zA-Z0-9_]` and cannot start with a number."\n\
1019"* Any `# comment` expression found in a custom commands file is discarded by the G'MIC parser, "\
1020"wherever it is located in a line."\n\
1021"* In a custom command, the following '$-expressions' are recognized and substituted:"\n\
1022"  - '$""\*' is substituted by a copy of the specified string of arguments."\n\
1023"  - '$\"*\"' is substituted by a copy of the specified string of arguments, each being double-quoted."\n\
1024"  - '$""#' is substituted by the maximum index of known arguments (either specified by the user or set to a default "\
1025"value in the custom command)."\n\
1026"  - '$""[]' is substituted by the list of selected image indices that have been specified in the command "\
1027"invocation."\n\
1028"  - '$""?' is substituted by a printable version of '$""[]' to be used in command descriptions."\n\
1029"  - '$i' and '${i}' are both substituted by the `i`-th specified argument. Negative indices such as '${-j}' are "\
1030"allowed and refer to the `j`-th latest argument. '$""0' is substituted by the custom command name."\n\
1031"  - '${i=default}' is substituted by the value of '$i' (if defined) or by its new value set to 'default' otherwise "\
1032"('default' may be a `$-expression` as well)."\n\
1033"  - '${subset}' is substituted by the argument values (separated by commas ',') of a specified argument subset. "\
1034"For instance expression '$""{2--2}' is substituted by all specified command arguments except the first and the last "\
1035"one. Expression '$""{^0}' is then substituted by all arguments of the invoked command (eq. to '$""*' if all "\
1036"arguments have been indeed specified)."\n\
1037"  - '$""=var' is substituted by the set of instructions that will assign each argument '$i' to the named variable "\
1038"'var$i' (for i in '[0...$""#]'. This is particularly useful when a custom command want to manage variable numbers "\
1039"of arguments. Variables names must use character set `[a-zA-Z0-9_]` and cannot start with a number."\n\
1040\n\
1041"* These particular `$-expressions` for custom commands are __always substituted__, even in "\
1042"double-quoted items or when the dollar sign '$' is escaped with a backslash '\\$'. To avoid substitution, place an "\
1043"empty double quoted string just after the '$' (as in '$\"\"1')."\n\
1044"* Specifying arguments may be skipped when invoking a custom command, by replacing them by commas ',' as in "\
1045"expression \n~~~\nflower ,,3\n~~~\n Omitted arguments are set to their default values, which must be thus explicitly "\
1046"defined in the code of the corresponding custom command (using default argument expressions as '$""{1=default}')."\n\
1047"* If one numbered argument required by a custom command misses a value, an error is thrown by the G'MIC interpreter."
1048
1049  _section "List of Commands"
1050  _text \
1051"All available \\G'MIC commands are listed below, by categories. An argument specified between '[]' "\
1052"or starting by '_' is optional except when standing for an existing image '[image]', where 'image' "\
1053"can be either an index number or an image name. In this case, the '[]' characters are mandatory when writing the "\
1054"item. Note that all images that serve as illustrations in this reference documentation are normalized in "\
1055"range `[0,255]` before being displayed. You may need to do this explicitly (command 'normalize 0,255') if you want "\
1056"to save and view images with the same aspect than those illustrated in the example codes."
1057
1058  # Insert list of commands.
1059  l reference_list_of_commands_$1 onfail endl
1060
1061  # Insert additional sections if specified.
1062  xfolder="$2"
1063  if "['$1']=='html' && ['$2']==0" xfolder=$HOME/work/src/gmic-community/reference fi
1064
1065  if ['$xfolder']!=0
1066    files $xfolder/*.gmd files=${}
1067    repeat narg({/$files})
1068      arg 1+$>,$files file=${}
1069      l[] it $file s={b} t={t} rm endl
1070      _section {/$s}
1071      _text {/$t}
1072    done
1073  fi
1074
1075  _section "Examples of Use"
1076  _text \
1077"`gmic` is a generic image processing tool which can be used in a wide variety of situations. "\
1078"The few examples below illustrate possible uses of this tool:"\n\
1079"### View a list of images: "\n\
1080"\n~~~\n$ gmic file1.bmp file2.jpeg\n~~~"\n\n\
1081"### Convert an image file: "\n\
1082"\n~~~\n$ gmic input.bmp output output.jpg\n~~~"\n\n\
1083"### Create a volumetric image from a movie sequence: "\n\
1084"\n~~~\n$ gmic input.mpg append z output output.hdr\n~~~"\n\n\
1085"### Compute image gradient norm: "\n\
1086"\n~~~\n$ gmic input.bmp gradient_norm\n~~~"\n\n\
1087"### Denoise a color image: "\n\
1088"\n~~~\n$ gmic image.jpg denoise 30,10 output denoised.jpg\n~~~"\n\n\
1089"### Compose two images using overlay layer blending: "\n\
1090"\n~~~\n$ gmic image1.jpg image2.jpg blend overlay output blended.jpg\n~~~"\n\n\
1091"### Evaluate a mathematical expression: "\n\
1092"\n~~~\n$ gmic echo \"cos(pi/4)^2+sin(pi/4)^2={cos(pi/4)^2+sin(pi/4)^2}\"\n~~~"\n\n\
1093"### Plot a 2D function: "\n\
1094"\n~~~\n$ gmic 1000,1,1,2 fill \"X=3*(x-500)/500;X^2*sin(3*X^2)+if(c==0,u(0,-1),cos(X*10))\" plot\n~~~"\n\
1095"===\n![](../img/example_plot.png)\n==="\n\n\
1096"### Plot a 3D elevated function in random colors: "\n\
1097"\n~~~\n$ gmic 128,128,1,3,\"u(0,255)\" plasma 10,3 blur 4 sharpen 10000 n 0,255 "\
1098"elevation3d[-1] \"'X=(x-64)/6;Y=(y-64)/6;100*exp(-(X^2+Y^2)/30)*abs(cos(X)*sin(Y))'\"\n~~~"\n\n\
1099"===\n![](../img/example_elevation3d.png)\n==="\n\n\
1100"### Plot the isosurface of a 3D volume: "\n\
1101"\n~~~\n$ gmic mode3d 5 moded3d 5 double3d 0 isosurface3d \"'x^2+y^2+abs(z)^abs(4*cos(x*y*z*3))'\",3\n~~~"\n\
1102"===\n![](../img/example_isosurface3d.png)\n==="\n\n\
1103"### Render a G'MIC 3D logo: "\n\
1104"\n~~~\n$ gmic 0 text G\\\47MIC,0,0,53,1,1,1,1 expand_xy 10,0 blur 1 normalize 0,100 +plasma 0.4 add blur 1 "\
1105"elevation3d -0.1 moded3d 4\n~~~"\n\
1106"===\n![](../img/example_logo.png)\n==="\n\n\
1107"### Generate a 3D ring of torii: "\n\
1108"\n~~~\n$ gmic repeat 20 torus3d 15,2 color3d[-1] \"{u(60,255)},{u(60,255)},{u(60,255)}\" *3d[-1] 0.5,1 if \"{$>%2}\" "\
1109"rotate3d[-1] 0,1,0,90 fi add3d[-1] 70 add3d rotate3d 0,0,1,18 done moded3d 3 mode3d 5 double3d 0\n~~~"\n\
1110"===\n![](../img/example_torii.png)\n==="\n\n\
1111"### Create a vase from a 3D isosurface: "\n\
1112"\n~~~\n$ gmic moded3d 4 isosurface3d \"'x^2+2*abs(y/2)*sin(2*y)^2+z^2-3',0\" sphere3d 1.5 sub3d[-1] 0,5 "\
1113"plane3d 15,15 rotate3d[-1] 1,0,0,90 center3d[-1] add3d[-1] 0,3.2 color3d[-1] 180,150,255 color3d[-2] 128,255,0 "\
1114"color3d[-3] 255,128,0 add3d\n~~~"\n\
1115"===\n![](../img/example_vase.png)\n==="\n\n\
1116"### Launch a set of interactive demos: "\n\
1117"\n~~~\n$ gmic demos\n~~~\n"
1118
1119  l reference_footer_$1 reference_end_$1 onfail endl
1120  um _section,_text
1121  rm
1122
1123#
1124# Implement output mode 'ascii' for command 'reference'.
1125#
1126reference_begin_ascii :
1127  use_vt100
1128  if !narg($_shell_cols) _shell_cols={${-shell_cols}-5} fi
1129  _section=0
1130  +e[] ""
1131
1132reference_header_ascii :
1133  if $_prerelease strprerelease=" (pre-release ""#"$_prerelease")" else strprerelease="" fi
1134  str=\n\
1135      "  "${_vt100_b}"gmic: GREYC\'s Magic for Image Computing:"$_vt100_n" command-line interface"\n\
1136      "        "${_vt100_c}${_vt100_b}"Version "${-strver}$strprerelease$_vt100_n\n\
1137      "        "$_vt100_g$_vt100_u"(https://gmic.eu)"$_vt100_n\n\
1138      \n\
1139      "        Copyright (c) Since 2008, David Tschumperlé / GREYC / CNRS."\n\
1140      "        "$_vt100_g$_vt100_u"(https://www.greyc.fr)"$_vt100_n
1141  +e[] $str
1142
1143reference_section_ascii :
1144  _section+=1
1145  +e[] ""
1146  ('$_section." "') ('"$*"') +f.. {'" "'} +f.. {'-'} a[-4,-3] x a[-2,-1] x
1147  +e[] "  "$_vt100_r$_vt100_b{-2,t}$_vt100_n
1148  +e[] "  "$_vt100_r{t}$_vt100_n\n
1149  rm[-2,-1]
1150
1151reference_text_ascii :
1152  l[]
1153    ('"$*"')
1154    gmd2ascii $_shell_cols,1
1155
1156    # Ensure output text contains no more than two consecutive newlines.
1157    # Also add a 2-chars left margin on each line.
1158    s +,{'\n'}
1159    eval "repeat (l,p,
1160            i(#p)==_'\n' && h(#p)>2?resize(#p,1,2,1,1,0):
1161            (resize(#p,1,h#p + 2,1,1,0,0,0,1); copy(i[#p,0],_' ',2,1,0)))"
1162    a y
1163    +e[] {/{t}}
1164    rm
1165  endl
1166
1167reference_list_of_commands_ascii :
1168  l
1169    if !$! it $_path_rc/update$_version.gmic fi
1170    parse_cli ascii
1171  onfail
1172    rm
1173    +e[] \n"  "$_vt100_r${_vt100_b}"No command descriptions available!"$_vt100_n
1174    +e[] "  "${_vt100_r}"Try updating your command files, with command "$_vt100_b"'update'."$_vt100_n
1175  endl
1176
1177reference_footer_ascii :
1178  +e[] \n"  "$_vt100_r$_vt100_b"** G\47MIC comes with ABSOLUTELY NO WARRANTY; "\
1179       "for details visit: https://gmic.eu **"$_vt100_n
1180
1181#
1182# Implement output mode 'html' for command 'reference'.
1183#
1184reference_section_html :
1185  name="$*"
1186  reference_end_section_html
1187  ('"<!DOCTYPE html>"\n\
1188"<html lang=\"en\">"\n\
1189"  <head>"\n\
1190"    <meta charset=\"utf-8\">"\n\
1191"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
1192"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
1193"- "$name"</title>"\n\
1194"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
1195"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
1196"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
1197"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\
1198"  </head>"\n\n\
1199"  <body>"\n\
1200"    <div id=\"include_header\"></div>"\n\n\
1201"    <div class=\"section_title\"><a href=\"index.html\"><p>Reference</p></a></div><div class=\"section_content\">"\n\
1202"    <a name=\"top\"></a>"\n\
1203"<!-- ref_navigation_top -->"\n\n\
1204"<!-- begin_content -->"\n\n\
1205"      <h1 class=\"ref_h1\">"$name"</h1>"\n':y)
1206  nm. $name
1207
1208reference_end_section_html :
1209  if $!   # End previous section
1210    ('"    <br/>"\n\n\
1211      "<!-- end_content -->"\n\n\
1212      "<!-- ref_navigation_bottom -->"\n\
1213      "    </div><div class=\"section_end\"></div>"\n\
1214      "    <div id=\"include_footer\"></div>"\n\
1215      "  </body>"':y)
1216    a[-2,-1] y
1217  fi
1218
1219reference_text_html :
1220  ('"$*"':y) gmd2html. 0
1221  if $!>1 a[-2,-1] y fi
1222
1223reference_footer_html :
1224  reference_end_section_html
1225
1226reference_finalize_html :
1227
1228  # Generate table of contents.
1229  if $_prerelease strprerelease=" (pre-release ""#"$_prerelease")" else strprerelease="" fi
1230  html="<!DOCTYPE html>"\n\
1231"<html lang=\"en\">"\n\
1232"  <head>"\n\
1233"    <meta charset=\"utf-8\">"\n\
1234"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
1235"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
1236"- Table of Contents</title>"\n\
1237"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
1238"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
1239"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
1240"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
1241"  </head>"\n\n\
1242"  <body>"\n\
1243"    <div id=\"include_header\"></div>"\n\n\
1244"    <div class=\"section_title\"><a href=\"index.html\"><p>Reference</p></a></div><div class=\"section_content\">"\n\n\
1245"    <a name=\"top\"></a>"\n\
1246"<!-- ref_navigation_top -->"\n\n\
1247"<!-- begin_content -->"\n\n\
1248"      <h1 class=\"ref_h1\">Preamble</h1>"\n\
1249"        <ul><li>This document is distributed under the "\
1250"<a target=\"_blank\" href=\"http://www.gnu.org/licenses/fdl-1.3.txt\">GNU Free Documentation License</a>, "\
1251"version 1.3.</li>"\n\
1252"        <li>A <a href=\"gmic_reference.pdf\">.pdf version</a> of this document is available.</li>"\n
1253
1254  if !0$_pdf_output
1255     html.="        <li>Quick access to the <a class=\"ref_loc\" "\
1256           "href=\"https://gmic.eu/reference/list_of_commands.html\">"\
1257           "List of Commands</a>.</li>"\n
1258  fi
1259
1260  html.="</ul>"\n\
1261"      <h1 class=\"ref_h1\">Version</h1>"\n\
1262"      <div class=\"ref_header\">"\n\
1263"        <span class=\"gmd_gmic\">G&apos;MIC</span>: "\
1264"<span class=\"gmd_monospace\">GREYC's Magic for Image Computing</span><br/>"\n\
1265"        <a href=\"https://gmic.eu\">https://gmic.eu</a><br/>"\n\
1266"        Version <b>"${-strver}"</b>"$strprerelease"<br/><br/>"\n\
1267"        Copyright &copy; Since 2008, "\
1268"<a target=\"_blank\" href=\"https://tschumperle.users.greyc.fr/\">David Tschumperlé</a> / "\
1269"<a target=\"_blank\" href=\"https://www.greyc.fr\">GREYC</a> / "\
1270"<a target=\"_blank\" href=\"http://www.cnrs.fr/en\">CNRS</a><br/>"\n\
1271"        <a target=\"_blank\" href=\"https://www.greyc.fr\">https://www.greyc.fr</a><br/>"\n\
1272"      </div>"\n\
1273"      <h1 class=\"ref_h1\">Table of Contents</h1>"\n\
1274"      <ul>"\n
1275
1276  ind_loc=${"-nmd 1,\"List of Commands\""}
1277  if narg($ind_loc)
1278    repeat $! l[$>]
1279      name={n} strvar $name url=${}.html
1280      if $>==$ind_loc
1281         html.="<li><a class=\"ref_loc\" href=\""$url"#top\">"$name"</a></li>"
1282      else
1283        html.="<li><a href=\""$url"#top\">"$name"</a></li>"
1284      fi
1285    endl done
1286    html.="\n    </ul>"\n\n\
1287          "<!-- end_content -->"\n\n\
1288          "<!-- ref_navigation_bottom -->"\n\
1289          "    </div><div class=\"section_end\"></div>"\n\
1290          "    <div id=\"include_footer\"></div>"\n\
1291          "  </body>"
1292
1293    i[0] ({'$html'}:y) nm[0] "Table of Contents"
1294    ind_loc+=1
1295    if narg($ind_loc)" && "isfile('list_of_commands.html') # Merge with existing 'List of commands' page if it exists
1296      it list_of_commands.html
1297      if find(crop(),'"<!-- merged_content -->"')<0 l[$ind_loc,-1]
1298        s[0] -,{'"<!-- end_content -->"'}
1299        s. -,{'"<!-- begin_content -->"'}
1300        i[1] ('"<!-- merged_content -->"':y)
1301        k[0,1,-1] a y
1302      endl else nm. "List of Commands" rv[$ind_loc,-1] rm. fi
1303    fi
1304  fi
1305
1306  # Insert top and bottom navigation bar into pages.
1307  if !0$_pdf_output
1308    repeat $!
1309      current={$>,n} strvar[] $current url_current=${}.html
1310      if $>>1 previous={{$>-1},n} strvar[] $previous url_previous=${}.html else previous= fi
1311      if $< next={{$>+1},n} strvar[] $next url_next=${}.html else next= fi
1312      html_top="    <table class=\"ref_navigation_top\"><tr><td>"\
1313                "<a href=\"index.html\">Table of Contents</a>"
1314      if $> html_top.="&nbsp;&nbsp;&#9656;&nbsp;&nbsp;<a href=\""$url_current"#top\">"$current"</a>" fi
1315      html_top.="</td><td>"
1316      if ['$previous']!=0 html_top.="<a href=\""$url_previous"#top\">&#9664;&nbsp;&nbsp;"$previous"</a>" fi
1317      if ['$previous']!=0" && "['$next']!=0 html_top.="&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;" fi
1318      if ['$next']!=0 html_top.="<a href=\""$url_next"#top\">"$next"&nbsp;&nbsp;&#9654;</a>" fi
1319      html_top.="</td></tr></table>"
1320
1321      html_bottom="<div class=\"ref_navigation_bottom\">"
1322      if ['$previous']!=0 html_bottom.="<a href=\""$url_previous"#top\">&#9664;&nbsp;&nbsp;"$previous"</a>" fi
1323      if ['$previous']!=0" && "['$next']!=0 html_bottom.="&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;" fi
1324      if ['$next']!=0 html_bottom.="<a href=\""$url_next"#top\">"$next"&nbsp;&nbsp;&#9654;</a>" fi
1325      html_bottom.="</div>"
1326
1327      replace_str[$>] "<!-- ref_navigation_top -->",$html_top
1328      replace_str[$>] "<!-- ref_navigation_bottom -->",$html_bottom
1329    done
1330  fi
1331
1332reference_end_html :
1333  reference_finalize_html
1334
1335  # Save html pages.
1336  repeat $! l[$>] strvar {n} ot ${}.html endl done
1337  if isfile('table_of_contents.html') x "ln -fs table_of_contents.html index.html" fi
1338  rm
1339
1340#
1341# Implement output mode 'man' for command 'reference'.
1342# (It is based on the 'ascii' output with some tricks to generate a roff file).
1343#
1344reference_begin_man :
1345  _section=0
1346  +e[] ".TH G\47MIC 1\n\
1347        .SH NAME\n\
1348        gmic \\- Perform image processing operations using the G\47MIC framework.\n\
1349        \n\
1350        .SH HELP\n"
1351
1352reference_header_man :
1353  _vt100_b="\\fB"
1354  _vt100_c="\\fB"
1355  _vt100_g="\\fB"
1356  _vt100_m=
1357  _vt100_n="\\fR"
1358  _vt100_r="\\fB"
1359  _vt100_u="\\fI"
1360  _prerelease=
1361  reference_header_ascii
1362
1363reference_section_man :
1364  reference_section_ascii "$*"
1365
1366reference_text_man :
1367  reference_text_ascii "$*"
1368
1369reference_list_of_commands_man :
1370  _vt100_c=
1371  reference_list_of_commands_ascii
1372  _vt100_m="\\fB"
1373  _vt100_b=
1374
1375reference_footer_man :
1376  reference_footer_ascii "$*"
1377
1378#
1379# Implement output mode 'pdf' for command 'reference'.
1380# (It is based on the 'html' output with some tricks to generate a pdf file with 'wkhtmltopdf').
1381#
1382reference_begin_pdf :
1383  _pdf_output=1
1384
1385reference_section_pdf :
1386  reference_section_html "$*"
1387
1388reference_text_pdf :
1389  reference_text_html "$*"
1390
1391reference_footer_pdf :
1392  reference_end_section_html
1393
1394reference_end_pdf :
1395  1024,4,1,3 o. reference_pdf.png rm.
1396  reference_finalize_html
1397
1398  # Load existing html page for each command.
1399  l[]
1400    it ../../src/gmic_stdlib.gmic +parse_cli. list loc=${}
1401    parse_cli html
1402    repeat narg($loc)
1403      command=${"arg "1+$>","$loc}
1404      l[] if ['$command'][0]!=_'_' it $command.html fi onfail endl
1405    done
1406    sort_list +,n
1407  endl
1408  s +,{'"<!-- begin_content -->"'}
1409  s +,{'"<!-- end_content -->"'}
1410  k[2--1:5]
1411
1412  # Add specific header and footer.
1413  if $_prerelease strprerelease=" (pre-release ""#"$_prerelease")" else strprerelease="" fi
1414  i[0] ('"<!DOCTYPE html>"\n\
1415"<html lang=\"en\">"\n\
1416"  <head>"\n\
1417"    <meta charset=\"utf-8\">"\n\
1418"    <link rel=\"stylesheet\" href=\"style.css\">"\n\
1419"    <title>reference_pdf</title>"\n\
1420"  <style>"\n\
1421"    body { background-color: white; font-size: 22px; }"\n\
1422"    .gmd_code_block { font-size: 1em; }"\n\
1423"  </style>"\n\
1424"  </head>"\n\n\
1425"  <body>"\n\
1426"    <div style=\"border: 3px solid black; text-align: center; width: 100%; margin-left: auto; "\
1427"margin-right: auto; margin-top: 9cm; break-after: page\">"\n\
1428"      <img style=\"width: 98%\" src=\"../img/gmic_banner.jpg\" />"\n\
1429"      <h1 class=\"ref_h1\">The Handbook</h1>"\n\
1430"      <h3>Version "${strver[]}$strprerelease"</h3>"\n\
1431"      <p>&copy; David Tschumperlé / GREYC / CNRS</p>"\n\
1432"      <p>"{date(0)}"/"{date(1)}"/"{date(2)}"</p>"\n\
1433"    </div>"':y)
1434  ('"<img style=\"margin-top: 2em; width: 100%;\" src=\"reference_pdf.png\"/>"\n\
1435    "<p style=\"text-align: center;\">&squ; End of document</p>"\n\
1436    "</body>"':y) a y
1437  ot reference_pdf.html
1438  rm
1439
1440  # Copy css to current folder.
1441  it ../style.css ot. style.css rm
1442
1443  # Use 'wkhtmltopdf' to convert html to a pdf file.
1444  # (There is a bug in wkhtmltopdf that prevents all images to appear in the generated document!
1445  #  I have to print the .pdf manually 'in a file' first).
1446  delete reference_pdf.pdf
1447  v 0 e[] "  > Waiting for file 'reference_pdf.pdf'."
1448  for !isfile('reference_pdf.pdf') wait 5000 done
1449  e[] "  > Removing links in file 'reference_pdf.pdf'."
1450  x "pdfjam reference_pdf.pdf"
1451  e[] "  > Compressing file 'reference_pdf.pdf' to 'gmic_reference.pdf'."
1452  x "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=gmic_reference.pdf reference_pdf-pdfjam.pdf"
1453  e[] "  > Clean temporary files."
1454  delete style.css,reference_pdf.html,reference_pdf.png,reference_pdf.pdf,reference_pdf-pdfjam.pdf
1455  e[] "  > Upload file 'gmic_reference.pdf' to G'MIC server."
1456  x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"put -O /www/gmic/reference gmic_reference.pdf; "\
1457    "quit\" >/dev/null"
1458
1459# use_vt100
1460# This command defines some global variables used to output colored text on VT100 terminals.
1461use_vt100 :
1462  if !0$_vt100" || "['$_vt100_n']!=0 return fi
1463  _vt100_b="\33[1m"         # Bold
1464  _vt100_c="\33[0;36;59m"   # Cyan
1465  _vt100_g="\33[0;32;59m"   # Green
1466  _vt100_i="\33[3m"         # Italic
1467  _vt100_m="\33[0;35;59m"   # Magenta
1468  _vt100_n="\33[0;0;0m"     # Normal
1469  _vt100_r="\33[0;31;59m"   # Red
1470  _vt100_s="\33[9m"         # Strikethrough
1471  _vt100_u="\33[4m"         # Underline
1472
1473#@cli version
1474#@cli : Display current version number on stdout.
1475version :
1476  use_vt100
1477  reference_header_ascii[]
1478  if !0$_cli_noarg +e[] "\n" fi
1479
1480v : # Allow 'gmic +v' to get the version number.
1481  version
1482
1483#-------------------------------
1484#
1485#@cli :: Input / Output
1486#
1487#-------------------------------
1488
1489#@cli camera : _camera_index>=0,_nb_frames>0,_skip_frames>=0,_capture_width>=0,_capture_height>=0 : (+)
1490#@cli : Insert one or several frames from specified camera.
1491#@cli : When 'nb_frames==0', the camera stream is released instead of capturing new images.
1492#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
1493#@cli : Default values: 'camera_index=0' (default camera), 'nb_frames=1', 'skip_frames=0' and \
1494# 'capture_width=capture_height=0' (default size).
1495
1496#@cli clut : "clut_name",_resolution>0,_cut_and_round={ 0=no | 1=yes }
1497#@cli : Insert one of the 958 pre-defined CLUTs at the end of the image list.\n
1498#@cli : 'clut_name' can be { 2-strip-process | 60s | 60s_faded | 60s_faded_alt | 7drk_21 | action_magenta_01 | \
1499# action_red_01 | adventure_1453 | agfa_apx_100 | agfa_apx_25 | agfa_precisa_100 | agfa_ultra_color_100 | \
1500# agfa_vista_200 | agressive_highligjtes_recovery_5 | alberto_street | alien_green | amstragram | amstragram+ | \
1501# analog_film_1 | analogfx_anno_1870_color | analogfx_old_style_i | analogfx_old_style_ii | analogfx_old_style_iii | \
1502# analogfx_sepia_color | analogfx_soft_sepia_i | analogfx_soft_sepia_ii | anime | apocalypse_this_very_moment | \
1503# aqua | aqua_and_orange_dark | arabica_12 | atomic_pink | autumn | ava_614 | avalanche | azrael_93 | bboyz_2 | \
1504# bc_darkum | beach_aqua_orange | beach_faded_analog | berlin_sky | black_and_white | black_star | black_white_01 | \
1505# black_white_02 | black_white_03 | black_white_04 | black_white_05 | black_white_06 | blade_runner | bleach_bypass | \
1506# bleachbypass_1 | bleachbypass_2 | bleachbypass_3 | bleachbypass_4 | bleech_bypass_green | bleech_bypass_yellow_01 | \
1507# blue_cold_fade | blue_dark | blue_house | blue_ice | blue_mono | blue_shadows_01 | blues | bob_ford | bourbon_64 | \
1508# bright_green_01 | bright_teal_orange | bright_warm | brightgreen | brown_mobster | brownbm | brownish | bw_1 | \
1509# bw_10 | bw_2 | bw_3 | bw_4 | bw_5 | bw_6 | bw_7 | bw_8 | bw_9 | bw_but_yellow | byers_11 | candlelight | caribe | \
1510# chemical_168 | chrome_01 | cineblue | cinebm_4k | cinema | cinema_2 | cinema_3 | cinema_4 | cinema_5 | \
1511# cinema_noir | cinematic-1 | cinematic-10 | cinematic-2 | cinematic-3 | cinematic-4 | cinematic-5 | cinematic-6 | \
1512# cinematic-7 | cinematic-8 | cinematic-9 | cinematic_01 | cinematic_02 | cinematic_03 | cinematic_for_flog | \
1513# cinematic_forest | cinematic_lady_bird | cinematic_mexico | city | city_7 | city_dust | classic_films_01 | \
1514# classic_films_02 | classic_films_03 | classic_films_04 | classic_films_05 | classic_teal_and_orange | clayton_33 | \
1515# clear_teal_fade | clouseau_54 | cobi_3 | coffee_44 | cold_clear_blue | cold_clear_blue_1 | cold_ice | \
1516# cold_simplicity_2 | color_rich | colorful_0209 | colornegative | conflict_01 | contrail_35 | \
1517# contrast_with_highlights_protection | contrasty_afternoon | contrasty_green | crispromance | crispwarm | \
1518# crispwinter | cross_process_cp_130 | cross_process_cp_14 | cross_process_cp_15 | cross_process_cp_16 | \
1519# cross_process_cp_18 | cross_process_cp_3 | cross_process_cp_4 | cross_process_cp_6 | crushin | cubicle_99 | d_o_1 | \
1520# dark_blues_in_sunlight | dark_green_02 | dark_green_1 | dark_man_x | dark_orange_teal | dark_place_01 | darkness | \
1521# date_39 | day_4nite | day_for_night | day_to_night_kings_blue | deep | deep_blue | deep_dark_warm | \
1522# deep_high_contrast | deep_teal_fade | deep_warm_fade | deepskintones_2 | deepskintones_3 | delicatessen | \
1523# denoiser_simple_40 | desert_gold_37 | dimension | directions_23 | django_25 | domingo_145 | dream_1 | dream_85 | \
1524# drop_green_tint_14 | dropblues | duotone_blue_red | earth_tone_boost | edgyember | elegance_38 | enchanted | \
1525# eterna_for_flog | expired_69 | expired_fade | expired_polaroid | extreme | fade | fade_to_green | faded | \
1526# faded_47 | faded_alt | faded_analog | faded_extreme | faded_green | faded_pink-ish | faded_print | faded_retro_01 | \
1527# faded_retro_02 | faded_vivid | fadedlook | fallcolors | faux_infrared | faux_infrared_bw_1 | \
1528# faux_infrared_color_p_2 | faux_infrared_color_p_3 | faux_infrared_color_r_0a | faux_infrared_color_r_0b | \
1529# faux_infrared_color_yp_1 | fgcinebasic | fgcinebright | fgcinecold | fgcinedrama | fgcinetealorange_1 | \
1530# fgcinetealorange_2 | fgcinevibrant | fgcinewarm | film_0987 | film_9879 | film_gb-19 | film_high_contrast | \
1531# film_print_01 | film_print_02 | filmic | flat_30 | flat_blue_moon | flavin | foggynight | folger_50 | formula_b | \
1532# french_comedy | frosted | frostedbeachpicnic | fuji_160c | fuji_160c_+ | fuji_160c_++ | fuji_160c_- | \
1533# fuji_3510_constlclip | fuji_3510_constlmap | fuji_3510_cuspclip | fuji_3513_constlclip | fuji_3513_constlmap | \
1534# fuji_3513_cuspclip | fuji_400h | fuji_400h_+ | fuji_400h_++ | fuji_400h_- | fuji_800z | fuji_800z_+ | \
1535# fuji_800z_++ | fuji_800z_- | fuji_astia_100_generic | fuji_astia_100f | fuji_fp-100c | fuji_fp-100c_+ | \
1536# fuji_fp-100c_++ | fuji_fp-100c_+++ | fuji_fp-100c_++_alt | fuji_fp-100c_- | fuji_fp-100c_-- | fuji_fp-100c_alt | \
1537# fuji_fp-100c_cool | fuji_fp-100c_cool_+ | fuji_fp-100c_cool_++ | fuji_fp-100c_cool_- | fuji_fp-100c_cool_-- | \
1538# fuji_fp-100c_negative | fuji_fp-100c_negative_+ | fuji_fp-100c_negative_++ | fuji_fp-100c_negative_+++ | \
1539# fuji_fp-100c_negative_++_alt | fuji_fp-100c_negative_- | fuji_fp-100c_negative_-- | fuji_fp-3000b | \
1540# fuji_fp-3000b_+ | fuji_fp-3000b_++ | fuji_fp-3000b_+++ | fuji_fp-3000b_- | fuji_fp-3000b_-- | fuji_fp-3000b_hc | \
1541# fuji_fp-3000b_negative | fuji_fp-3000b_negative_+ | fuji_fp-3000b_negative_++ | fuji_fp-3000b_negative_+++ | \
1542# fuji_fp-3000b_negative_- | fuji_fp-3000b_negative_-- | fuji_fp-3000b_negative_early | fuji_fp_100c | fuji_hdr | \
1543# fuji_neopan_1600 | fuji_neopan_1600_+ | fuji_neopan_1600_++ | fuji_neopan_1600_- | fuji_neopan_acros_100 | \
1544# fuji_provia_100_generic | fuji_provia_100f | fuji_provia_400f | fuji_provia_400x | fuji_sensia_100 | \
1545# fuji_superia_100 | fuji_superia_100_+ | fuji_superia_100_++ | fuji_superia_100_- | fuji_superia_1600 | \
1546# fuji_superia_1600_+ | fuji_superia_1600_++ | fuji_superia_1600_- | fuji_superia_200 | fuji_superia_200_xpro | \
1547# fuji_superia_400 | fuji_superia_400_+ | fuji_superia_400_++ | fuji_superia_400_- | fuji_superia_800 | \
1548# fuji_superia_800_+ | fuji_superia_800_++ | fuji_superia_800_- | fuji_superia_hg_1600 | fuji_superia_reala_100 | \
1549# fuji_superia_x-tra_800 | fuji_velvia_100_generic | fuji_velvia_50 | fuji_xtrans_iii_acros | \
1550# fuji_xtrans_iii_acros+g | fuji_xtrans_iii_acros+r | fuji_xtrans_iii_acros+ye | fuji_xtrans_iii_astia | \
1551# fuji_xtrans_iii_classic_chrome | fuji_xtrans_iii_mono | fuji_xtrans_iii_mono+g | fuji_xtrans_iii_mono+r | \
1552# fuji_xtrans_iii_mono+ye | fuji_xtrans_iii_pro_neg_hi | fuji_xtrans_iii_pro_neg_std | fuji_xtrans_iii_provia | \
1553# fuji_xtrans_iii_sepia | fuji_xtrans_iii_velvia | fusion_88 | futuristicbleak_1 | futuristicbleak_2 | \
1554# futuristicbleak_3 | futuristicbleak_4 | going_for_a_walk | golden | golden_bright | golden_fade | golden_mono | \
1555# golden_night_softner_43 | golden_sony_37 | golden_vibrant | goldengate | goldentime | goldfx_bright_spring_breeze | \
1556# goldfx_bright_summer_heat | goldfx_hot_summer_heat | goldfx_perfect_sunset_01min | goldfx_perfect_sunset_05min | \
1557# goldfx_perfect_sunset_10min | goldfx_spring_breeze | goldfx_summer_heat | good_morning | green_15 | green_2025 | \
1558# green_action | green_afternoon | green_and_orange | green_blues | green_conflict | green_day_01 | green_day_02 | \
1559# green_g_09 | green_indoor | green_light | green_mono | green_yellow | greenish_contrasty | greenish_fade | \
1560# greenish_fade_1 | gremerta | hackmanite | hallowen_dark | happyness_133 | hard_teal_orange | harsh_day | \
1561# harsh_sunset | helios | herderite | heulandite | hiddenite | highlights_protection | hilutite | hitman | hlg_1_1 | \
1562# honey_light | hong_kong | horrorblue | howlite | hydracore | hyla_68 | hypersthene | hypnosis | hypressen | \
1563# ilford_delta_100 | ilford_delta_3200 | ilford_delta_3200_+ | ilford_delta_3200_++ | ilford_delta_3200_- | \
1564# ilford_delta_400 | ilford_fp_4_plus_125 | ilford_hp_5 | ilford_hp_5_+ | ilford_hp_5_++ | ilford_hp_5_- | \
1565# ilford_hp_5_plus_400 | ilford_hps_800 | ilford_pan_f_plus_50 | ilford_xp_2 | indoor_blue | industrial_33 | \
1566# infrared_-_dust_pink | instantc | justpeachy | jwick_21 | k_tone_vintage_kodachrome | kh_1 | kh_10 | kh_2 | kh_3 | \
1567# kh_4 | kh_5 | kh_6 | kh_7 | kh_8 | kh_9 | killstreak | kodak_2383_constlclip | kodak_2383_constlmap | \
1568# kodak_2383_cuspclip | kodak_2393_constlclip | kodak_2393_constlmap | kodak_2393_cuspclip | kodak_bw_400_cn | \
1569# kodak_e-100_gx_ektachrome_100 | kodak_ektachrome_100_vs | kodak_ektachrome_100_vs_generic | kodak_ektar_100 | \
1570# kodak_elite_100_xpro | kodak_elite_chrome_200 | kodak_elite_chrome_400 | kodak_elite_color_200 | \
1571# kodak_elite_color_400 | kodak_elite_extracolor_100 | kodak_hie_hs_infra | kodak_kodachrome_200 | \
1572# kodak_kodachrome_25 | kodak_kodachrome_64 | kodak_kodachrome_64_generic | kodak_portra_160 | kodak_portra_160_+ | \
1573# kodak_portra_160_++ | kodak_portra_160_- | kodak_portra_160_nc | kodak_portra_160_nc_+ | kodak_portra_160_nc_++ | \
1574# kodak_portra_160_nc_- | kodak_portra_160_vc | kodak_portra_160_vc_+ | kodak_portra_160_vc_++ | \
1575# kodak_portra_160_vc_- | kodak_portra_400 | kodak_portra_400_+ | kodak_portra_400_++ | kodak_portra_400_- | \
1576# kodak_portra_400_nc | kodak_portra_400_nc_+ | kodak_portra_400_nc_++ | kodak_portra_400_nc_- | \
1577# kodak_portra_400_uc | kodak_portra_400_uc_+ | kodak_portra_400_uc_++ | kodak_portra_400_uc_- | \
1578# kodak_portra_400_vc | kodak_portra_400_vc_+ | kodak_portra_400_vc_++ | kodak_portra_400_vc_- | kodak_portra_800 | \
1579# kodak_portra_800_+ | kodak_portra_800_++ | kodak_portra_800_- | kodak_portra_800_hc | kodak_t-max_100 | \
1580# kodak_t-max_3200 | kodak_t-max_400 | kodak_tmax_3200 | kodak_tmax_3200_+ | kodak_tmax_3200_++ | kodak_tmax_3200_- | \
1581# kodak_tmax_3200_alt | kodak_tri-x_400 | kodak_tri-x_400_+ | kodak_tri-x_400_++ | kodak_tri-x_400_- | \
1582# kodak_tri-x_400_alt | korben_214 | landscape_01 | landscape_02 | landscape_03 | landscape_04 | landscape_05 | \
1583# landscape_1 | landscape_10 | landscape_2 | landscape_3 | landscape_4 | landscape_5 | landscape_6 | landscape_7 | \
1584# landscape_8 | landscape_9 | lateafternoonwanderlust | latesunset | lc_1 | lc_10 | lc_2 | lc_3 | lc_4 | lc_5 | \
1585# lc_6 | lc_7 | lc_8 | lc_9 | lenox_340 | life_giving_tree | light_blown | lomo | lomography_redscale_100 | \
1586# lomography_x-pro_slide_200 | london_nights | louetta | low_contrast_blue | low_key_01 | lucky_64 | \
1587# lushgreensummer | magenta_day | magenta_day_01 | magenta_dream | magenta_yellow | magentacoffee | matrix | \
1588# mckinnon_75 | memories | metropolis | milo_5 | minimalistcaffeination | modern_film | modern_films_01 | \
1589# modern_films_02 | modern_films_03 | modern_films_04 | modern_films_05 | modern_films_06 | modern_films_07 | \
1590# mono_tinted | monochrome | monochrome_1 | monochrome_2 | moody_1 | moody_10 | moody_2 | moody_3 | moody_4 | \
1591# moody_5 | moody_6 | moody_7 | moody_8 | moody_9 | moonlight | moonlight_01 | moonrise | morning_6 | morroco_16 | \
1592# mostly_blue | moviz_1 | moviz_10 | moviz_11 | moviz_12 | moviz_13 | moviz_14 | moviz_15 | moviz_16 | moviz_17 | \
1593# moviz_18 | moviz_19 | moviz_2 | moviz_20 | moviz_21 | moviz_22 | moviz_23 | moviz_24 | moviz_25 | moviz_26 | \
1594# moviz_27 | moviz_28 | moviz_29 | moviz_3 | moviz_30 | moviz_31 | moviz_32 | moviz_33 | moviz_34 | moviz_35 | \
1595# moviz_36 | moviz_37 | moviz_38 | moviz_39 | moviz_4 | moviz_40 | moviz_41 | moviz_42 | moviz_43 | moviz_44 | \
1596# moviz_45 | moviz_46 | moviz_47 | moviz_48 | moviz_5 | moviz_6 | moviz_7 | moviz_8 | moviz_9 | mute_shift | \
1597# muted_01 | muted_fade | mysticpurplesunset | nah | natural_vivid | nemesis | neon_770 | neutral_pump | \
1598# neutral_teal_orange | neutral_warm_fade | newspaper | night_01 | night_blade_4 | night_king_141 | night_spy | \
1599# nightfromday | nightlife | nostalgiahoney | nostalgic | nw-1 | nw-10 | nw-2 | nw-3 | nw-4 | nw-5 | nw-6 | nw-7 | \
1600# nw-8 | nw-9 | old_west | once_upon_a_time | only_red | only_red_and_blue | operation_yellow | orange_dark_4 | \
1601# orange_dark_7 | orange_dark_look | orange_tone | orange_underexposed | oranges | paladin | paladin_1875 | \
1602# pasadena_21 | passing_by | pink_fade | pitaya_15 | pmcinematic_01 | pmcinematic_02 | pmcinematic_03 | \
1603# pmcinematic_04 | pmcinematic_05 | pmcinematic_06 | pmcinematic_07 | pmnight_01 | pmnight_02 | pmnight_03 | \
1604# pmnight_04 | pmnight_05 | polaroid_664 | polaroid_665 | polaroid_665_+ | polaroid_665_++ | polaroid_665_- | \
1605# polaroid_665_-- | polaroid_665_negative | polaroid_665_negative_+ | polaroid_665_negative_- | \
1606# polaroid_665_negative_hc | polaroid_667 | polaroid_669 | polaroid_669_+ | polaroid_669_++ | polaroid_669_+++ | \
1607# polaroid_669_- | polaroid_669_-- | polaroid_669_cold | polaroid_669_cold_+ | polaroid_669_cold_- | \
1608# polaroid_669_cold_-- | polaroid_672 | polaroid_690 | polaroid_690_+ | polaroid_690_++ | polaroid_690_- | \
1609# polaroid_690_-- | polaroid_690_cold | polaroid_690_cold_+ | polaroid_690_cold_++ | polaroid_690_cold_- | \
1610# polaroid_690_cold_-- | polaroid_690_warm | polaroid_690_warm_+ | polaroid_690_warm_++ | polaroid_690_warm_- | \
1611# polaroid_690_warm_-- | polaroid_polachrome | polaroid_px-100uv+_cold | polaroid_px-100uv+_cold_+ | \
1612# polaroid_px-100uv+_cold_++ | polaroid_px-100uv+_cold_+++ | polaroid_px-100uv+_cold_- | polaroid_px-100uv+_cold_-- | \
1613# polaroid_px-100uv+_warm | polaroid_px-100uv+_warm_+ | polaroid_px-100uv+_warm_++ | polaroid_px-100uv+_warm_+++ | \
1614# polaroid_px-100uv+_warm_- | polaroid_px-100uv+_warm_-- | polaroid_px-680 | polaroid_px-680_+ | polaroid_px-680_++ | \
1615# polaroid_px-680_- | polaroid_px-680_-- | polaroid_px-680_cold | polaroid_px-680_cold_+ | polaroid_px-680_cold_++ | \
1616# polaroid_px-680_cold_++_alt | polaroid_px-680_cold_- | polaroid_px-680_cold_-- | polaroid_px-680_warm | \
1617# polaroid_px-680_warm_+ | polaroid_px-680_warm_++ | polaroid_px-680_warm_- | polaroid_px-680_warm_-- | \
1618# polaroid_px-70 | polaroid_px-70_+ | polaroid_px-70_++ | polaroid_px-70_+++ | polaroid_px-70_- | polaroid_px-70_-- | \
1619# polaroid_px-70_cold | polaroid_px-70_cold_+ | polaroid_px-70_cold_++ | polaroid_px-70_cold_- | \
1620# polaroid_px-70_cold_-- | polaroid_px-70_warm | polaroid_px-70_warm_+ | polaroid_px-70_warm_++ | \
1621# polaroid_px-70_warm_- | polaroid_px-70_warm_-- | polaroid_time_zero_expired | polaroid_time_zero_expired_+ | \
1622# polaroid_time_zero_expired_++ | polaroid_time_zero_expired_- | polaroid_time_zero_expired_-- | \
1623# polaroid_time_zero_expired_--- | polaroid_time_zero_expired_cold | polaroid_time_zero_expired_cold_- | \
1624# polaroid_time_zero_expired_cold_-- | polaroid_time_zero_expired_cold_--- | portrait_1 | portrait_10 | portrait_2 | \
1625# portrait_3 | portrait_4 | portrait_5 | portrait_6 | portrait_7 | portrait_8 | portrait_9 | progressen | \
1626# protect_highlights_01 | prussian_blue | pseudogrey | purple | purple_2 | red_afternoon_01 | red_day_01 | \
1627# red_dream_01 | redblueyellow | reds | reds_oranges_yellows | reeve_38 | remy_24 | rest_33 | retro | \
1628# retro_brown_01 | retro_magenta_01 | retro_summer_3 | retro_yellow_01 | rollei_ir_400 | rollei_ortho_25 | \
1629# rollei_retro_100_tonal | rollei_retro_80s | rotate_muted | rotate_vibrant | rotated | rotated_crush | \
1630# saturated_blue | saving_private_damon | science_fiction | sea | serenity | seringe_4 | serpent | \
1631# seventies_magazine | sevsuz | shade_kings_ink | shadow_king_39 | shine | skin_tones | smart_contrast | smokey | \
1632# smooth_clear | smooth_cromeish | smooth_fade | smooth_green_orange | smooth_sailing | smooth_teal_orange | \
1633# soft_fade | softwarming | solarized_color | solarized_color_2 | springmorning | sprocket_231 | spy_29 | street | \
1634# studio_skin_tone_shaper | subtle_blue | subtle_green | subtle_yellow | summer | summer_alt | sunlightlove | sunny | \
1635# sunny_alt | sunny_rich | sunny_warm | sunset_aqua_orange | sunset_intense_violet_blue | sunset_violet_mood | \
1636# super_warm | super_warm_rich | sutro_fx | sweet_bubblegum | sweet_gelatto | taiga | tarraco | teal_fade | \
1637# teal_moonlight | tealmagentagold | tealorange | tealorange_1 | tealorange_2 | tealorange_3 | \
1638# technicalfx_backlight_filter | teigen_28 | tensiongreen_1 | tensiongreen_2 | tensiongreen_3 | tensiongreen_4 | \
1639# terra_4 | the_matrices | thriller_2 | toastedgarden | trent_18 | true_colors_8 | turkiest_42 | tweed_71 | \
1640# ultra_water | undeniable | undeniable_2 | unknown | urban_01 | urban_02 | urban_03 | urban_04 | urban_05 | \
1641# urban_cowboy | uzbek_bukhara | uzbek_marriage | uzbek_samarcande | velvetia | very_warm_greenish | vfb_21 | \
1642# vibrant | vibrant_alien | vibrant_contrast | vibrant_cromeish | victory | vintage | vintage_01 | vintage_02 | \
1643# vintage_03 | vintage_04 | vintage_05 | vintage_163 | vintage_alt | vintage_brighter | vintage_chrome | \
1644# vintage_mob | vintage_warmth_1 | violet_taste | vireo_37 | warm | warm_dark_contrasty | warm_fade | warm_fade_1 | \
1645# warm_highlight | warm_neutral | warm_sunset_red | warm_teal | warm_vintage | warm_yellow | well_see | western | \
1646# westernlut_2 | whiter_whites | winterlighthouse | wipe | wooden_gold_20 | yellow_55b | yellow_film_01 | \
1647# yellowstone | you_can_do_it | zed_32 | zeke_39 | zilverfx_bw_solarization | zilverfx_infrared | \
1648# zilverfx_vintage_bw }#@cli : Default values: 'resolution=33' and 'cut_and_round=1'.
1649#@cli : $ clut summer clut alien_green,17 clut orange_dark4,48
1650clut : check "isnum(${2=33}) && $2>0 && isbool(${3=1})"
1651  to_clutname "$1" name=${} l[]
1652  e[^-1] "Input CLUT '"$name"' with resolution $2."
1653    path_clut=${-path_cache}
1654    if isfile(['{/${path_clut}clut_$name.cimgz}']) i ${path_clut}clut_$name.cimgz fi
1655    if $!"!=1 || w<$2 || h<$2 || d<$2" # Decompression needed
1656      rm
1657      if narg($GMIC_SYSTEM_PATH) g_path_unix=$GMIC_SYSTEM_PATH
1658      else g_path_unix=/usr/lib/gimp/2.0/plug-ins/
1659      fi
1660      path_test0=$path_clut
1661      path_test1=$_path_rc
1662      path_test2=${-path_gimp}plug-ins/
1663      path_test3=${-path_gimp}plug-ins/gmic_gimp_qt/
1664      path_test4=$g_path_unix
1665      repeat 5 file_clut=${path_test$>}/gmic_cluts.gmz
1666        l[] $file_clut onfail endl if $! break fi
1667      done
1668      if !$! # Download from G'MIC server
1669        i https://gmic.eu/gmic_cluts.gmz o ${path_clut}gmic_cluts.gmz
1670      fi
1671
1672      k[${"nmd 1,"$name}]
1673      if $!!=1
1674        rm i https://gmic.eu/gmic_cluts.gmz o ${path_clut}gmic_cluts.gmz # Try getting newest version of the CLUTs file
1675        repeat $! if ['{$>,n}']==['$name'] k[$>] break fi done
1676        if $!!=1
1677          error[0--5] "Command '$0': Unknown CLUT name '"$name"'."
1678        fi
1679      fi
1680      decompress_clut $2,$2,$2
1681      if $3 round c 0,255 to_rgb fi
1682      o. ${path_clut}clut_$name.cimgz
1683    elif "w>$2 || h>$2 || d>$2" r $2,$2,$2,3,2 # Downsize from higher resolution
1684    fi
1685    nm $name k.
1686  endl
1687
1688# [Internal] Use this command to 'clean' a .gmz file that represents CLUT keypoints.
1689# What it does is:
1690#
1691# - Standardize CLUT name.
1692# - Convert RGB CLUTs to Grayscale when possible.
1693# - Remove duplicates and sort by lexicographic order.
1694# - Display list of CLUTs to ease documentation update of command 'clut'.
1695#
1696clean_cluts :
1697  e[^-1] "Clean CLUT dataset.\n"
1698  round c 0,255
1699
1700  # Standardize names.
1701  repeat $! l[$>]
1702    nm={n}
1703
1704    # Standardize names for 'Moviz'.
1705    if "str = lowercase(['"$nm"*']);
1706        find(str,'tpf_-_cinematica_')==0" l[]
1707       if !narg($tpf) tpf=1 fi
1708       ('moviz_$tpf')
1709       tpf+=1
1710       nm={t} rm
1711    endl fi
1712    if "str = lowercase(['"$nm"*']);
1713        find(str,'_-_standard-vk')>=0" l[]
1714       if !narg($tpf) tpf=1 fi
1715       ('moviz_$tpf')
1716       tpf+=1
1717       nm={t} rm
1718    endl fi
1719
1720    # Standardize names for 'SmallHD MovieLook'.
1721    if "str = lowercase(['"$nm"*']);
1722        find(str,'_-_rec_709*')>=0" l[]
1723      ('$nm') z. 0,{w-11}
1724      nm={t} rm
1725    endl fi
1726
1727    # Standardize names for 'SmallHD MovieLook'.
1728    if "str = lowercase(['"$nm"']);
1729        find(str,'smallhd_movielook_')==0" l[]
1730      ('$nm') z. 18,100%
1731      replace_str "apocalypsethisverymoment","apocalypse_this_very_moment"
1732      replace_str "bobford","bob_ford"
1733      replace_str "lifegivingtree","life_giving_tree"
1734      replace_str "savingprivatedamon","saving_private_damon"
1735      replace_str "thematrices","the_matrices"
1736      nm={t} rm
1737    endl fi
1738
1739    # Standardize names for 'Fuji XTrans III'.
1740    if "str = lowercase(['"$nm"']);
1741        find(str,'fuji_xtrans_iii')==0" l[]
1742      ('$nm')
1743      replace_str "_-_","_" nm={t} rm
1744    endl fi
1745
1746    # Standardize names for 'RawTherapee'.
1747    if "str = lowercase(['"$nm"']);
1748        find(str,'kodak')==0 ||
1749        find(str,'polaroid')==0 ||
1750        find(str,'fuji')==0 ||
1751        find(str,'ilford')==0" l[]
1752      ({'$nm'},{'*'})
1753      replace_str " ","_"
1754      replace_str "xp_2","xp2"
1755      replace_str "hp_5","hp5"
1756      repeat 8 n={1+$>}
1757        replace_str "_"${n}"_+","_+"
1758        replace_str "_"${n}"_-","_-"
1759        replace_str "_"${n}"_alt","_alt"
1760        replace_str "_"${n}"_Alt","_alt"
1761        replace_str "_"${n}"*","*"
1762      done
1763      = 0,0,100% nm={t} rm
1764    endl fi
1765
1766    to_clutname $nm nm=${}
1767
1768    # Standardize names for 'PictureFX'.
1769    if "str = lowercase(['"$nm"']);
1770        find(str,'technicalfx')==0 ||
1771        find(str,'picturefx')==0 ||
1772        find(str,'analogfx')==0 ||
1773        find(str,'goldfx')==0 ||
1774        find(str,'zilverfx')==0" l[]
1775      ('$nm')
1776      replace_str "-","_" nm={t} rm
1777    endl fi
1778
1779    # Other name changes.
1780    l[]
1781      ({'$nm'},{'*'})
1782      replace_str "_v_2*","*"
1783      replace_str "_v_1*","*"
1784      replace_str "_*","*"
1785      replace_str "_b_w","_bw"
1786      replace_str "&",""
1787      replace_str "rec_709_-_","rec709_"
1788      replace_str "s-log","slog"
1789      replace_str "__","_"
1790      replace_str "action_-_","action_"
1791      replace_str "-version-",""
1792      replace_str "picturefx_",""
1793      = 0,0,100%  nm={t} rm
1794    endl
1795    nm $nm
1796  endl done
1797
1798  # Convert RGB CLUTs to Grayscale when possible.
1799  repeat $! l[$>]
1800    if "ref(crop(#0,0,0,0,3,1,h,1,1),R);
1801        ref(crop(#0,0,0,0,4,1,h,1,1),G);
1802        ref(crop(#0,0,0,0,5,1,h,1,1),B);
1803        R==G && G==B?1:0"
1804      channels 0,3
1805    fi
1806  endl done
1807
1808  # Search for duplicates and sort.
1809  p=0 for $p<$!
1810    nm0={$p,n}
1811    e[] "\r- Search duplicates for ["$p"] = '"$nm0"'                        "
1812    q={$p+1} for $q<$!
1813      nm={$q,n}
1814      if ['$nm0']==['$nm']
1815        e[] "   > Found duplicate ["$q"] -> Original 1x"{$p,h}", new 1x"{$q,h}"\n"
1816        rv[$p,$q] rm[$q]
1817      else q+=1
1818      fi
1819    done
1820    p+=1
1821  done
1822  sort_list +,n
1823
1824  # Display all clut names.
1825  doc="#@cli clut : \"clut_name\",_resolution>0,_cut_and_round={ 0=no | 1=yes }\n"\
1826      "#@cli : Insert one of the "$!" pre-defined CLUTs at the end of the image list.\\n\n"\
1827      "#@cli : 'clut_name' can be {" sep="|"
1828  nbc=28
1829  repeat $! l[$>]
1830    if !$< sep="}" fi
1831    str=" "{n}" "$sep
1832    s_str={size(['$str'])}
1833    nbc+=$s_str
1834    if $nbc<118
1835      doc=${doc}${str}
1836    else
1837      doc=${doc}" \\\n#"${str}
1838      nbc={1+$s_str}
1839    fi
1840  endl done
1841
1842  doc=${doc}"\n"\
1843      "#@cli : Default values: 'resolution=33' and 'cut_and_round=1'.\n"\
1844      "#@cli : $ clut summer clut alien_green,17 clut orange_dark4,48\n"
1845  e[] "\n"$doc
1846
1847#@cli m : eq. to 'command'. : (+)
1848
1849#@cli command : _add_debug_info={ 0 | 1 },{ filename | http[s]://URL | "string" } : (+)
1850#@cli : Import G'MIC custom commands from specified file, URL or string.
1851#@cli : (eq. to 'm').\n
1852#@cli : Imported commands are available directly after the 'command' invocation.
1853#@cli : Default value: 'add_debug_info=1'.
1854#@cli : $ image.jpg command "foo : mirror y deform $""1" +foo[0] 5 +foo[0] 15
1855
1856# compress_gmic
1857# Compress .gmic custom command files for compressing update files a little bit,
1858# by removing empty lines, and useless comments.
1859compress_gmic :
1860  merge_multiline_comments
1861  merge_multiline
1862  eval " # Remove useless comments
1863    p = 0;
1864    while (p<h,
1865      q = find(#-1,_'\n',p)%h;
1866      p==q?++p:(
1867        i[p]==_'#'?( # Line starts with a '#'
1868          ref(crop(0,p,1,5),cr);
1869          cr!='#@gui' && cr!='#@cli' && cr!='#@web'?copy(i[p],-1,q-p+1,1,0); # Remove comment line
1870        ):( # Line doesn't start with a '#' -> look for comment at line end
1871          l = find(#-1,_'#',p);
1872          l>=0 && l<q && i[l-1]<=_' '?(
1873            while (i[--l]<=_' ',0);
1874            copy(i[l+1],-1,q-l-1,1,0);
1875          );
1876          0;
1877        );
1878        p = q + 1;
1879      );
1880    )"
1881  discard -1
1882  autocrop. {_'\n'}
1883  eval. "* # Merge consecutive line feeds
1884         i==_'\n' && j[1]==_'\n'?(i()=-1)"
1885  discard. -1
1886  eval. "* # Remove leading spaces on each line
1887         i<=_' ' && i!=_'\n' && j[-1]==_'\n'?(
1888          for (p = 1, j[p] && j[p]<=_' ' && j[p]!=_'\n', ++p); copy(i(),-1,p,1,0))"
1889  discard. -1
1890
1891#@cli cursor : _mode = { 0=hide | 1=show } : (+)
1892#@cli : Show or hide mouse cursor for selected instant display windows.
1893#@cli : Command selection (if any) stands for instant display window indices instead of image indices.
1894#@cli : Default value: 'mode=1'.
1895
1896#@cli delete : filename1[,filename2,...] : (+)
1897#@cli : Delete specified filenames on disk. Multiple filenames must be separated by commas.
1898
1899#@cli d : eq. to 'display'. : (+)
1900
1901#@cli display : _X[%]>=0,_Y[%]>=0,_Z[%]>=0,_exit_on_anykey={ 0 | 1 } : (+)
1902#@cli : Display selected images in an interactive viewer (use the instant display window [0] if opened).
1903#@cli : (eq. to 'd').\n
1904#@cli : Arguments 'X','Y','Z' determine the initial selection view, for 3D volumetric images.
1905#@cli : Default value: 'X=Y=Z=0' and 'exit_on_anykey=0'.
1906#@cli : $$ https://gmic.eu/oldtutorial/_display
1907
1908#@cli d0 : eq. to 'display0'.
1909d0 :
1910  _gmic_s="$?" v + _display2d 0,0
1911
1912#@cli display0
1913#@cli : Display selected images without value normalization.
1914#@cli : (eq. to 'd0').
1915display0 :
1916  _gmic_s="$?" v + _display2d 0,0
1917
1918#@cli d2d : eq. to 'display2d'.
1919d2d :
1920  _gmic_s="$?" v + _display2d 0,1
1921
1922_d2d_core :
1923  _gmic_s="$?" _d2d_core=1 v + _display2d $1,1
1924
1925#@cli display2d
1926#@cli : Display selected 2d images in an interactive window.
1927#@cli : (eq. to 'd2d').
1928#@cli : This command is used by default by command 'display' when displaying 2d images.
1929#@cli : If selected image is a volumetric image, each slice is displayed on a separate display
1930#@cli : window (up to 10 images can be displayed simultaneously this way), with synchronized moves.
1931#@cli : When interactive window is opened, the following actions are possible:
1932#@cli : * Left mouse button: Create an image selection and zoom into it.
1933#@cli : * Middle mouse button, or CTRL+left mouse button: Move image.
1934#@cli : * Mouse wheel or PADD+/-: Zoom in/out.
1935#@cli : * Arrow keys: Move image left/right/up/down.
1936#@cli : * `CTRL + A`: Enable/disable transparency (show/hide alpha channel).
1937#@cli : * `CTRL + C`: Decrease window size.
1938#@cli : * `CTRL + D`: Increase window size.
1939#@cli : * `CTRL + F`: Toggle fullscreen mode.
1940#@cli : * `CTRL + N`: Change normalization mode (can be { none | normal | channel-by-channel }).
1941#@cli : * `CTRL + O`: Save a copy of the input image, as a numbered file 'gmic_xxxxxx.gmz'.
1942#@cli : * `CTRL + R`: Reset both window size and view.
1943#@cli : * `CTRL + S`: Save a screenshot of the current view, as a numbered file 'gmic_xxxxxx.png'.
1944#@cli : * `CTRL + SPACE`: Reset view.
1945#@cli : * `CTRL + X`: Show/hide axes.
1946#@cli : * `CTRL + Z`: Hold/release aspect ratio.
1947display2d :
1948  _gmic_s="$?" v + _$0 0,1
1949
1950# $1 = exit_on_single_click?, can be { 0=no | 1=yes }.
1951# $2 = default window normalization for display window #0.
1952_display2d : check "isbool(${1=0}) && isint(${2=1}) && $2>=0"
1953  e[0--3] "Start interactive display of 2d image"$_gmic_s"."
1954  m "_d2d_format :
1955       if $""#>=8 u {_([$""*])[0,4]:\\ }\\ ...\\ {_([$""*])[$""#-4,4]:\\ }
1956       else ('\"$""*\"') replace. {','},32 u {t} rm.
1957       fi"
1958  p
1959  repeat $! l[$>]
1960    ('{n}') if s=crop();find(s,_'.',size(s)-1,-1)>0 nm={-2,b}.{-2,x} else nm={-2,b} fi rm.
1961    nm img
1962
1963    if !w v - d v + break fi
1964    if d>10
1965      error[0--6] "Command 'display2d': Input image has "{d}" slices, cannot manage more than 10 simultaneous views."
1966    fi
1967    is_multiview={d>1}
1968    is_moderate_ratio={0,D=[w,h];max(D)/min(D)<6}
1969    may_have_alpha={s==2||s>=4}
1970    alpha_mode,axes_mode,fullscreen_mode,ratio_mode={s==1||s==3},1,0,$is_moderate_ratio
1971    normalization_mode={{*}?!!{*,n}:$2}
1972    posx,posy,sizx,sizy={0,[0,0,w,h]}
1973    is_bottom_text,wait_event,mx,my,omb=0,1,-1,-1,0
1974    xsel0,ysel0,xsel1,ysel1,notification=
1975    wnormalization0={*,n}
1976    fontsize,fontsize_notif=
1977
1978    if $is_multiview" && "!${-is_macos}
1979      if d==2 if {*,u}/w>={*,v}/h wsiz0={r={*,u}*0.48;round([r,r*h/w])} else wsiz0={r={*,v}*0.40;round([r*w/h,r])} fi
1980      elif d<=4 wsiz0=${fitscreen\ .,,45%}
1981      else wsiz0=${fitscreen\ .,,30%}
1982      fi
1983      wsiz0=${fitscreen\ $wsiz0,1,,90%}
1984    else wsiz0=${fitscreen\ .}
1985    fi
1986
1987    if $is_multiview
1988      if narg($_d2d_names) l[] $_d2d_names repeat $! wname$>={$>,t} done rm endl fi
1989      com_iskey,com_flushkey,com_isvisible,com_isresizeed,com_idisp,sep=
1990      repeat {img,d}
1991        if narg($_d2d_names) w$>[] {{*$>,w}?[-1,-1]:[$wsiz0]},0,${wname$>}" ("{w}x{h}x{d}x{s}")"
1992        else w$>[] {{*$>,w}?[-1,-1]:[$wsiz0]},0,$nm" ("{w}x{h}x{d}x{s}")-#"$>
1993        fi
1994        com_iskey.=$sep"{*"$>",$""1}"
1995        com_flushkey.=$sep"{*"$>",-$""1}"
1996        com_isvisible.=$sep"{*"$>"}"
1997        com_isresized.=$sep"{*"$>",r}"
1998        com_idisp.=$sep"{*"$>",x}"
1999        sep=,
2000      done
2001      m "iskey : u {max("$com_iskey")}"\n\
2002        "flushkey : skip "$com_flushkey\n\
2003        "isvisible : u {min("$com_isvisible")}"\n\
2004        "isresized : u {max("$com_isresized")}"\n\
2005        "idisp : u {argmax("$com_idisp")}"
2006
2007      # Auto-arrange display window layout in multi-view mode.
2008      if !${-is_macos}
2009        ww,wh={[$wsiz0]+[8,40]}
2010        if d==2
2011          if {*,u}/$ww>{*,v}/$wh # Horizontal alignment
2012            ox,oy={round([max(0,{*,u}-2*$ww),max(0,{*,v}-$wh)]/2)}
2013            w0[] -1,-1,-1,-1,$ox,$oy
2014            w1[] -1,-1,-1,-1,{$ox+$ww},$oy
2015          else # Vertical alignment
2016            ox,oy={round([{*,u}-$ww,{*,v}-2*$wh]/2)}
2017            w0[] -1,-1,-1,-1,$ox,$oy
2018            w1[] -1,-1,-1,-1,$ox,{$oy+$wh}
2019          fi
2020        elif d<=4
2021          ox,oy={round([max(0,{*,u}-2*$ww),max(0,{*,v}-2*$wh)]/2)}
2022          ww,wh+=$ox,$oy
2023          w0[] -1,-1,-1,-1,$ox,$oy
2024          w1[] -1,-1,-1,-1,$ww,$oy
2025          w2[] -1,-1,-1,-1,$ox,$wh
2026          if d==4 w3[] -1,-1,-1,-1,$ww,$wh fi
2027        else
2028          ox,oy={round([max(0,{*,u}-3*$ww),max(0,{*,v}-(d>6?3:2)*$wh)]/2)}
2029          ww2,wh2={2*[$ww,$wh]+[$ox,$oy]}
2030          ww,wh+=$ox,$oy
2031          w0[] -1,-1,-1,-1,$ox,$oy
2032          w1[] -1,-1,-1,-1,$ww,$oy
2033          w2[] -1,-1,-1,-1,$ww2,$oy
2034          w3[] -1,-1,-1,-1,$ox,$wh
2035          w4[] -1,-1,-1,-1,$ww,$wh
2036          if d>5 w5[] -1,-1,-1,-1,$ww2,$wh fi
2037          if d>6 w6[] -1,-1,-1,-1,$ox,$wh2 fi
2038          if d>7 w7[] -1,-1,-1,-1,$ww,$wh2 fi
2039          if d>8 w8[] -1,-1,-1,-1,$ww2,$wh2 fi
2040        fi
2041      fi
2042
2043    else
2044      w[] {{*,w}?[-1,-1]:[$wsiz0]},0,$nm" ("{w}x{h}x{d}x{s}")"
2045      m "iskey : u {*,$""1}"\n\
2046        "flushkey : skip {*,-$""1}"\n\
2047        "isvisible : u {*}"\n\
2048        "isresized : u {*,r}"\n\
2049        "idisp : u 0"
2050    fi
2051
2052    repeat {img,d} cursor[$>] 0 done
2053
2054    # Start event loop.
2055    for ${-isvisible}" && "!${"iskey ESC"}" && "!((${"iskey CTRLLEFT"}" || "${"iskey CTRLRIGHT"})" && "${"iskey W"})
2056
2057      # Correct aspect ratio while centering image.
2058      if $ratio_mode" && "$sizx>16" && "$sizy>16
2059        nposx,nposy,nsizx,nsizy=$posx,$posy,$sizx,$sizy
2060        repeat 2
2061          if {*,w}/$sizx<{*,h}/$sizy
2062            nposy,nsizy={nsizy=$nsizx*{*,h}/{*,w};round([$nposy-(nsizy-$nsizy)/2,nsizy])}
2063          else
2064            nposx,nsizx={nsizx=$nsizy*{*,w}/{*,h};round([$nposx-(nsizx-$nsizx)/2,round(nsizx)])}
2065          fi
2066          if $<" && "$nsizx>w#0" && "$nsizy>h#0 nposx,nposy,nsizx,nsizy={[0,0,w#0,h#0]} fi
2067        done
2068        if [$nposx,$nposy,$nsizx,$nsizy]!=[$posx,$posy,$sizx,$sizy]
2069          posx,posy,sizx,sizy=$nposx,$nposy,$nsizx,$nsizy
2070          rmn baseview
2071        fi
2072      fi
2073
2074      # Generate baseview.
2075      if !narg($baseview)
2076
2077        # Get view corresponding to position and zoom factor.
2078        ($posx,{$posx+$sizx}) ($posy;{$posy+$sizy}) r[-2,-1] 2,2 a[-2,-1] c
2079        r. {[{*,w,h}]+1},1,2,3 z. 0,0,{[w,h]-2} round. 1,-1 ind_warp={$!-1}
2080        +channels[0] {0,[0,min(3,s-1)]} warp. ..,0,0,1
2081        if $is_multiview repeat {img,d-1} +slices[img] {1+$>} warp. [$ind_warp],0,0,1 done a[-{img,d}--1] z fi
2082        rm..
2083
2084        if s>($alpha_mode?4:3) channels. 0,{$alpha_mode?3:2} fi # Discard useless channels
2085        if $normalization_mode
2086          # Find min and max values that are not nan of inf.
2087          eval[0] ">begin(m = inf; M = -inf);
2088            !isinf(i) && !isnan(i)?(m = min(i,m); M = max(i,M));
2089            end(run('im,iM=',m,',',M))"
2090          vim,viM={[$im-abs($im),$iM+abs($iM)]}
2091          f. "isnan(i)?"$vim":isinf(i)?(i<0?"$vim":"$viM"):i"
2092        else
2093          im,iM=0,255
2094          f. "isnan(i)?"$im":isinf(i)?(i<0?"$im":"$iM"):i"
2095        fi
2096
2097        # Normalize view.
2098        if $normalization_mode==1
2099          sh. 0,{s-($alpha_mode" && "$may_have_alpha?2:1)}
2100          if $is_multiview repeat {img,d} +slices. $> n. 0,255 j.. .,0,0,$> rm. done else n. 0,255 fi
2101          rm.
2102        elif $normalization_mode==2
2103          sh. 0,{s-($alpha_mode" && "$may_have_alpha?2:1)}
2104          if $is_multiview repeat d*s z,c={[$>%d,int($>/d)]} sh. $z,$z,$c n. 0,255 rm. done
2105          else repeat s sh. $> n. 0,255 rm. done
2106          fi
2107        fi
2108        if $posx<0" || "$posy<0" || "$posx+$sizx>=w#0" || "$posy+$sizy>=h#0
2109          100%,100%
2110          rectangle. {A=-[$posx,$posy]*[w,h]/[$sizx,$sizy];\
2111                      B=A+[w#0,h#0]*[w,h]/[$sizx,$sizy]-1;\
2112                      [ceil(A),floor(B)]},1,1
2113          *[-2,-1]
2114        fi
2115
2116        # Add alpha channel if necessary.
2117        if $alpha_mode
2118          coords={A=-[$posx,$posy]*[w,h]/[$sizx,$sizy];\
2119                  B=A+[w#0,h#0]*[w,h]/[$sizx,$sizy]-1;\
2120                  [ceil(A),floor(B)]}
2121          if !$may_have_alpha # Alpha mode without alpha channel -> Add alpha channel
2122            100%,100%,1,1,64 rectangle. $coords,1,255 r. 100%,100%,.. a[-2,-1] c
2123          else # Alpha mode with alpha channel
2124            sh. 100% 100%,100%,1,1,64 rectangle. $coords,1,0 r. 100%,100%,.. +[-2,-1] rm.
2125          fi
2126        fi
2127
2128        # Render image with transparency pattern.
2129        if $alpha_mode
2130          (128,160;160,128) r. 32,32,1,{-2,s-1} r. ..,..,..,100%,0,2,0.5,0.5 sh.. 100% j.. ...,0,0,0,0,1,.,255 rm[-3,-1]
2131        elif s==1 r. 100%,100%,100%,3
2132        elif s==2 r. 100%,100%,100%,3,0
2133        fi
2134        nm. baseview
2135        rmn view
2136      fi
2137
2138      # Manage notifications.
2139      if narg($notification)
2140        wait_event=0
2141        if !isnum($notification) # Create notification gfx
2142          rmn notification_gfx
2143          ofs,fs={narg($fontsize_notif)?0$fontsize_notif:32}
2144          do
2145            0 t. {``$notification},0,0,$fs,1,255
2146            if narg($fontsize_notif) break
2147            elif {baseview,"(w#-1>0.7*w || h#-1>0.25*h) && "$fs>13" && "$ofs>=$fs}
2148              ofs,fs=$fs,{max(13,round($fs/1.25))} rm.
2149            elif {baseview,"w#-1<0.3*w && h#-1<0.25*h && "$fs<64" && "$ofs<=$fs}
2150              ofs,fs=$fs,{min(64,round($fs*1.25))} rm.
2151            else
2152              fontsize_notif=$fs break
2153            fi
2154          while 1
2155
2156          r. {[w+12,h+8]},1,1,0,0,0.5,0.5 rectangle. 0,0,100%,100%,1,0xFFFFFFFF,255 to_rgb.
2157          nm. notification_gfx
2158          notification=$|
2159        else
2160          if $|>$notification+1 rm[notification_gfx] wait_event=1 notification= fi
2161          rmn view
2162        fi
2163      fi
2164
2165      # Generate view.
2166      if !narg($view)
2167        [baseview]
2168        if $mx>=0
2169          posmx,posmy={floor([$posx,$posy]+[$mx,$my]*[$sizx,$sizy]/[{*,w,h}])}
2170          is_selection_a_point={[0$xsel0,0$ysel0]==[0$xsel1,0$ysel1]}
2171
2172          if narg($xsel0)" && "!$is_selection_a_point
2173            dselx,dsely={[$xsel1-$xsel0,$ysel1-$ysel0]}
2174            ofs,fs={narg($fontsize)?0$fontsize:32}
2175            do
2176              0 t. " Box ( "{``{[min($xsel0,$xsel1),min($ysel0,$ysel1)]}}" ) - "\
2177                   "( "{``{[max($xsel0,$xsel1),max($ysel0,$ysel1)]}}" ) \n"\
2178                   " Size = ( "{``{abs([$dselx,$dsely]+1)}}" ), "\
2179                   "Length = "{_norm($dselx,$dsely)}" \n"\
2180                   " Angle = "{_atan2($dsely,$dselx)*180/pi}"\260 ",1,0,$fs,1,1
2181              if narg($fontsize) break
2182              elif {baseview,"(w#-1>0.7*w || h#-1>0.45*h) && "$fs>13" && "$ofs>=$fs}
2183                ofs,fs=$fs,{max(13,round($fs/1.25))} rm.
2184              elif {baseview,"w#-1<0.3*w && h#-1<0.45*h && "$fs<64" && "$ofs<=$fs}
2185                ofs,fs=$fs,{min(64,round($fs*1.25))} rm.
2186              else
2187                fontsize=$fs break
2188              fi
2189            while 1
2190            n. 0,255 +dilate. 3 *. -1 n. 0,80 +[-2,-1] r. 100%,100%,..,..
2191            j.. .,0,$is_bottom_text~,0,0,0.85 rm.
2192          fi
2193
2194          if $mx>=0
2195            if $posmx>=0" && "$posmx<w#0" && "$posmy>=0" && "$posmy<h#0
2196              if !narg($xsel0)" || "$is_selection_a_point
2197                repeat {img,d}
2198                  if {img,P=I($posmx,$posmy,$>);(s>=1" || "s<=4)" && "min(isint(P))" && "min(P)>=0" && "max(P)<=255}
2199                    hexstr="= \#"\
2200                          {img,`"digit(x) = (x<10?_'0' + x:_'A' + x - 10);\
2201                                 P = I("$posmx,$posmy,$>");\
2202                                 [ digit(P[0]>>4),digit(P[0]&15),\
2203                                   s<2?0:digit(P[1]>>4),s<2?0:digit(P[1]&15),\
2204                                   s<3?0:digit(P[2]>>4),s<3?0:digit(P[2]&15),\
2205                                   s<4?0:digit(P[3]>>4),s<4?0:digit(P[3]&15) ]"`}" "
2206                  else hexstr= fi
2207                  ofs,fs={narg($fontsize)?0$fontsize:32}
2208                  do
2209                    0 t. " Point ( "$posmx","$posmy" ) = [ "${_d2d_format\ {img,_I($posmx,$posmy,$>)}}" ] "$hexstr,\
2210                         1,0,$fs,1,1
2211                    if narg($fontsize) break
2212                    elif {baseview,"(w#-1>0.7*w || h#-1>0.15*h) && "$fs>13" && "$ofs>=$fs}
2213                      _ofs,fs=$fs,{max(13,round($fs/1.25))} rm.
2214                    elif {baseview,"w#-1<0.3*w && h#-1<0.15*h && "$fs<64" && "$ofs<=$fs}
2215                      ofs,fs=$fs,{min(64,round($fs*1.25))} rm.
2216                    else
2217                      fontsize=$fs break
2218                    fi
2219                  while 1
2220
2221                  n. 0,255 +dilate. 3 *. -1 n. 0,80 +[-2,-1] r. 100%,100%,1,..
2222                  j.. .,0,$is_bottom_text~,$>,0,0.85 rm.
2223                done
2224              fi
2225              x0,y0,x1,y1={[round([$posmx-$posx,$posmy-$posy]*[{*,w,h}]/[$sizx,$sizy]),\
2226                            round([$posmx-$posx+1,$posmy-$posy+1]*[{*,w,h}]/[$sizx,$sizy])-1]}
2227              if $x1-$x0>=8" && "$y1-$y0>=8 # Draw pixel contour when zoomed-in
2228                repeat d==1?1:d*s if d==1 sh. else sh. {z=$>%d;[z,z,int($>/d)]} fi
2229                  rectangle. $x0,$y0,$x1,$y1,1,0x55555555,0
2230                  rectangle. $x0,$y0,$x1,$y1,1,0xAAAAAAAA,255
2231                  rm.
2232                done
2233              fi
2234            fi
2235            if $axes_mode # Draw horizontal/vertical axes
2236              repeat d==1?1:d*s if d==1 sh. else sh. {z=$>%d;[z,z,int($>/d)]} fi
2237                line. $mx,0,$mx,100%,0.5,0xFF00FF00,255
2238                line. $mx,0,$mx,100%,0.5,0x00FF00FF,0
2239                line. 0,$my,100%,$my,0.5,0xFF00FF00,255
2240                line. 0,$my,100%,$my,0.5,0x00FF00FF,0
2241                rm.
2242              done
2243            fi
2244          fi
2245
2246          if narg($xsel0) # Draw rectangular selection
2247            x0,y0,x1,y1={xm=min($xsel0,$xsel1);xM=max($xsel0,$xsel1);\
2248                         ym=min($ysel0,$ysel1);yM=max($ysel0,$ysel1);\
2249                         [[xm-$posx,ym-$posy]*[{*,w,h}]/[$sizx,$sizy],\
2250                          [xM+1-$posx,yM+1-$posy]*[{*,w,h}]/[$sizx,$sizy]-1]}
2251            repeat d==1?1:d*s if d==1 sh. else sh. {z=$>%d;[z,z,int($>/d)]} fi
2252              rectangle. $x0,$y0,$x1,$y1,0.2,0
2253              rectangle. $x0,$y0,$x1,$y1,0.9,0x55555555,0
2254              rectangle. $x0,$y0,$x1,$y1,0.9,0xAAAAAAAA,255
2255              rm.
2256            done
2257            if $xsel0>$xsel1 x0,x1=$x1,$x0 fi
2258            if $ysel0>$ysel1 y0,y1=$y1,$y0 fi
2259            if $xsel0!=$xsel1" && "$ysel0!=$ysel1
2260              x0,y0,x1,y1={[(0.5+[$xsel0-$posx,$ysel0-$posy])*[{*,w,h}]/[$sizx,$sizy],\
2261                            (0.5+[$xsel1-$posx,$ysel1-$posy])*[{*,w,h}]/[$sizx,$sizy]]}
2262              repeat d==1?1:d*s if d==1 sh. else sh. {z=$>%d;[z,z,int($>/d)]} fi
2263                line. $x0,$y0,$x1,$y1,0.9,0x33333333,0
2264                line. $x0,$y0,$x1,$y1,0.9,0xCCCCCCCC,255
2265                rm.
2266              done
2267            fi
2268          fi
2269        fi
2270
2271        if $notification_gfx
2272          +r[notification_gfx] 100%,100%,. j.. .,{[w#-2-w-4,4]},0,0,{sqrt(max(0,1-($|-$notification)))} rm.
2273        fi
2274
2275        nm. view
2276        if $is_multiview repeat d +slices[view] $> w$>. rm. done else w. fi
2277      fi
2278
2279      # Manage user events.
2280      if $wait_event wait else wait 40 fi
2281      idisp=${-idisp} # Index of 'active' display window
2282      wait_event=1
2283
2284      old_mx,old_my=$mx,$my
2285      nposx,nposy,nsizx,nsizy=$posx,$posy,$sizx,$sizy
2286      is_CTRL,mb,mx,my,mo={${"iskey CTRLLEFT"}" || "${"iskey CTRLRIGHT"}},{*$idisp,b,x,y,-o}
2287
2288      # Test end of pan shift.
2289      if !($mb&4)" && "!($is_CTRL" && "($mb&1)) pan_mx,pan_my,pan_posx,pan_posy= fi
2290
2291      # Test if text must be displayed at the bottom.
2292      if {view,$my<0" || "$my>=h-16} is_bottom_text=0
2293      elif $my<16 is_bottom_text=1
2294      fi
2295
2296      # Events related to window resizing.
2297      if ${-isresized} # One of the display windows has been resized
2298        repeat {img,d},w if {*$w,r}
2299          w$w[] {*$w,d,e}
2300          if $is_multiview repeat {img,d} w$>[] {*$w,w,h} done fi
2301          if $nsizx>w#0" && "$nsizy>h#0 nposx,nposy,nsizx,nsizy={[0,0,w#0,h#0]} fi
2302          break
2303        fi done
2304        fontsize,fontsize_notif=
2305        rmn baseview
2306      elif $is_CTRL" && "${"iskey D"}" && "{*$idisp,d}<{*$idisp,u}" && "{*$idisp,e}<{*$idisp,v} # Increase window size
2307        w$idisp[] {round([{*$idisp,w,h}]*1.25)}
2308        if $is_multiview repeat {img,d} w$>[] {*$idisp,w,h} done fi
2309        notification="Increase Window Size"
2310        fontsize,fontsize_notif=
2311        flushkey D rmn baseview
2312      elif $is_CTRL" && "${"iskey C"}" && "{*$idisp,d}>64" && "{*$idisp,e}>64 # Decrease window size
2313        w$idisp[] {round([{*$idisp,w,h}]/1.25)}
2314        if $is_multiview repeat {img,d} w$>[] {*$idisp,w,h} done fi
2315        notification="Decrease Window Size"
2316        fontsize,fontsize_notif=
2317        flushkey C rmn baseview
2318      elif $is_CTRL" && "${"iskey R"} # Reset window size (and view)
2319        nposx,nposy,nsizx,nsizy={0,[0,0,w,h]}
2320        repeat {img,d} w$>[] $wsiz0 done
2321        notification="Reset Window Size"
2322        fontsize,fontsize_notif=
2323        flushkey R rmn baseview
2324      fi
2325
2326      # Events related to image selection.
2327      if !$is_CTRL" && "$mb&1" && "$mx>0
2328        xsel,ysel={X=[$nposx,$nposy]+[$mx,$my]*[$nsizx,$nsizy]/[{*,w,h}];\
2329                   floor([max(0,min(X[0],w#0-1)),max(0,min(X[1],h#0-1))])}
2330        if !narg($xsel0)" && "!($omb&1) xsel0,ysel0,xsel1,ysel1=$xsel,$ysel,$xsel,$ysel
2331        elif narg($xsel0) xsel1,ysel1=$xsel,$ysel
2332        fi
2333        if $mx<=16 nposx-={$nsizx/64} wait_event=0 xzoom,yzoom=
2334        elif $mx>={*,w}-17 nposx+={$nsizx/64} wait_event=0 xzoom,yzoom=
2335        fi
2336        if $my<=16 nposy-={$nsizy/64} wait_event=0 xzoom,yzoom=
2337        elif $my>{*,h}-17 nposy+={$nsizx/64} wait_event=0 xzoom,yzoom=
2338        fi
2339        wait_event=0
2340        rmn view
2341      elif !($mb&1)
2342        if narg($xsel0)
2343
2344          if "p0 = ["$xsel0,$ysel0]"; p1 = ["$xsel1,$ysel1"];  # One px selection -> reset view or exit.
2345              siz = max("$sizx,$sizy");
2346              p0==p1 || (siz>128 && norm1(p1-p0)<=siz/100)"
2347            if $1" && "$nsizx>=w#0" && "$nsizy>=h#0 break fi
2348            nposx,nposy,nsizx,nsizy={0,[0,0,w,h]}
2349          else # Otherwise, zoom in
2350            nposx,nposy,nsizx,nsizy={[min($xsel0,$xsel1),min($ysel0,$ysel1),abs([$xsel1-$xsel0,$ysel1-$ysel0])+1]}
2351          fi
2352          xzoom,yzoom= mb={$mb&6}
2353          rmn view
2354        fi
2355        xsel0,ysel0,xsel1,ysel1=
2356      fi
2357
2358      # Events related to image displacement and mode changes.
2359      if ${"iskey ARROWLEFT"} nposx-={$nsizx/($is_CTRL?4:16)} xzoom,yzoom= # Go left
2360      elif ${"iskey ARROWRIGHT"} nposx+={$nsizx/($is_CTRL?4:16)} xzoom,yzoom= # Go right
2361      elif ${"iskey ARROWUP"} nposy-={$nsizy/($is_CTRL?4:16)} xzoom,yzoom= # Go up
2362      elif ${"iskey ARROWDOWN"} nposy+={$nsizy/($is_CTRL?4:16)} xzoom,yzoom= # Go down
2363      elif $is_CTRL" && "${"iskey O"} # Save copy
2364        n=0 do filename gmic.gmz,$n n+=1 while isfile(['{/${}}'])
2365        if $is_multiview +slices[img] $idisp o. ${} rm. else o[img] ${} fi
2366        notification="Save Copy:\n"${}
2367        flushkey O
2368      elif $is_CTRL" && "${"iskey S"} # Save screenshot
2369        n=0 do filename gmic.png,$n n+=1 while isfile(['{/${}}'])
2370        if $is_multiview +slices[baseview] $idisp o. ${} rm. else o[baseview] ${} fi
2371        notification="Save Screenshot:\n"${}
2372        flushkey S
2373      elif $is_CTRL" && "${"iskey SPACE"} # Center view
2374        nposx,nposy,nsizx,nsizy={0,[0,0,w,h]}
2375        notification="Center View"
2376        flushkey SPACE
2377      elif $is_CTRL" && "${"iskey N"} # Change normalization mode
2378        normalization_mode={($normalization_mode+1)%3}
2379        notification=${"s0=Disable s1=Enable s2=\"Enable C.by.C\" u ${s"$normalization_mode"}"}" Normalization"
2380        flushkey N rmn baseview
2381      elif $is_CTRL" && "${"iskey A"} # Toggle alpha mode
2382        alpha_mode={!$alpha_mode}
2383        notification=${"s0=Disable s1=Enable u ${s"$alpha_mode"}"}" Alpha"
2384        flushkey A rmn baseview
2385      elif $is_CTRL" && "${"iskey F"} # Toggle fullscreen mode
2386        fullscreen_mode={!$fullscreen_mode}
2387        if $fullscreen_mode
2388          fullscreen_wsize={*,w,h} fullscreen_params=$nposx,$nposy,$nsizx,$nsizy
2389          w[] {*,u,v},0,1
2390        else
2391          nposx,nposy,nsizx,nsizy=$fullscreen_params
2392          w[] $fullscreen_wsize,0,0
2393        fi
2394        repeat {img,d} cursor[$>] {!$axes_mode} done
2395        notification=${"s0=Disable s1=Enable u ${s"$fullscreen_mode"}"}" Fullscreen"
2396        flushkey F rmn baseview
2397      elif $is_CTRL" && "${"iskey X"} # Toggle axes mode
2398        repeat {img,d} cursor[$>] $axes_mode done axes_mode={!$axes_mode}
2399        notification=${"s0=Hide s1=Show u ${s"$axes_mode"}"}" Axes"
2400        flushkey X rmn view
2401      elif $is_CTRL" && "${"iskey Z"}" && "$is_moderate_ratio # Toggle aspect-ratio mode
2402        ratio_mode={!$ratio_mode}
2403        notification=${"s0=Release s1=Hold u ${s"$ratio_mode"}"}" Aspect Ratio"
2404        if !$ratio_mode nposx,nposy,nsizx,nsizy={0,[0,0,w,h]} fi
2405        flushkey Z rmn baseview
2406      elif $mx>=0" && "($mb&4" || "($is_CTRL" && "$mb&1)) # Pan (middle mouse button)
2407        if !narg($pan_mx) pan_mx,pan_my,pan_posx,pan_posy=$mx,$my,$posx,$posy fi
2408        nposx,nposy={shiftx=($mx-$pan_mx)*$nsizx/{*,w};\
2409                     shifty=($my-$pan_my)*$nsizy/{*,h};\
2410                     [$pan_posx-shiftx,$pan_posy-shifty]}
2411        xzoom,yzoom=
2412      elif ${"iskey PADSUB"}" || "($mx>=0" && "$mo<0) # Zoom out
2413        if $nsizx>=w#0" && "$nsizy>=h#0
2414          nposx,nposy,nsizx,nsizy={[$nposx/2,$nposy/2,w#0,h#0]}
2415        else
2416          if !narg($xzoom)
2417            xzoom,yzoom={X=$mx<0?[$nposx,$nposy]+[$nsizx,$nsizy]/2:\
2418                           [$nposx,$nposy]+[$mx,$my]*[$nsizx,$nsizy]/[{*,w,h}];\
2419                   [max(0,min(X[0],w#0-1)),max(0,min(X[1],h#0-1))]}
2420          fi
2421          nposx,nposy,nsizx,nsizy={[[$xzoom,$yzoom]+[$nposx-$xzoom,$nposy-$yzoom]/0.75,\
2422                                   min(w#0,round($nsizx/0.75)),\
2423                                   min(h#0,round($nsizy/0.75))]}
2424          if $nsizx>w#0" && "$nsizy>h#0 nsizx,nsizy={[w#0,h#0]} fi
2425        fi
2426      elif ($nsizx>2" || "$nsizy>2)" && "(${"iskey PADADD"}" || "($mx>=0" && "$mo>0)) # Zoom in
2427        xzoom,yzoom={X=$mx<0?[$nposx,$nposy]+[$nsizx,$nsizy]/2:\
2428                             [$nposx,$nposy]+[$mx,$my]*[$nsizx,$nsizy]/[{*,w,h}];\
2429               [max(0,min(X[0],w#0-1)),max(0,min(X[1],h#0-1))]}
2430        nposx,nposy,nsizx,nsizy={[[$xzoom,$yzoom]+0.75*[$nposx-$xzoom,$nposy-$yzoom],round(0.75*[$nsizx,$nsizy])]}
2431      fi
2432
2433      # Constrain image displacement.
2434      if $nposx>=w#0-0.5*$nsizx nposx={w#0-1-0.5*$nsizx}
2435      elif $nposx<=-0.5*$nsizx nposx={1-0.5*$nsizx}
2436      fi
2437      if $nposy>=h#0-0.5*$nsizy nposy={h#0-1-0.5*$nsizy}
2438      elif $nposy<=-0.5*$nsizy nposy={1-0.5*$nsizy}
2439      fi
2440
2441      if [$nposx,$nposy,$nsizx,$nsizy]!=[$posx,$posy,$sizx,$sizy]
2442        posx,posy,sizx,sizy=$nposx,$nposy,$nsizx,$nsizy
2443        rmn baseview
2444      fi
2445      if [$mx,$my]!=[$old_mx,$old_my] rmn view fi
2446      omb=$mb
2447
2448    done
2449    k[0]
2450    if narg($_d2d_core) w[] -1,-1,$wnormalization0
2451    else
2452      if $is_multiview repeat d w$>[] 0 done else w[] 0 fi
2453    fi
2454  nm $nm um iskey,flushkey,isvisible,isresized,idisp endl done
2455  um _d2d_format v -1 d[]
2456
2457#@cli d3d : eq. to 'display3d'.
2458d3d : skip "${1=},${2=0}"
2459  l[] is_image_arg=${"is_image_arg $1"} is_arg={$is_image_arg" || isnum($1)"} onfail is_arg=0 endl
2460  if $is_arg arg=$is_image_arg,$2 if $is_image_arg pass$1 1 _d3d_wh={[w,h]} store. _d3d_background fi
2461  else arg=0,0 noarg fi
2462  v + _display3d $arg
2463
2464#@cli display3d : _[background_image],_exit_on_anykey={ 0 | 1 } : _exit_on_anykey={ 0 | 1 }
2465#@cli : Display selected 3D objects in an interactive viewer (use the instant display window [0] if opened).
2466#@cli : (eq. to 'd3d').
2467#@cli : Default values: '[background_image]=(default)' and 'exit_on_anykey=0'.
2468display3d : skip "${1=},${2=0}"
2469  l[] is_image_arg=${"is_image_arg $1"} is_arg={$is_image_arg" || isnum($1)"} onfail is_arg=0 endl
2470  if $is_arg arg=$is_image_arg,$2 if $is_image_arg pass$1 1 _d3d_wh={[w,h]} store. _d3d_background fi
2471  else arg=0,0 noarg fi
2472  v + _$0 $arg
2473
2474# $1 = is_user_background?
2475# $2 = exit_on_any_key?
2476_display3d :
2477  is_user_background,exit_on_anykey=$1,$2
2478  if !$! e[0--3] "Display 3D object []." return fi
2479
2480  repeat $! l[$>]
2481    l. check3d 0
2482    onfail l[] ({'${}'}) s +,{'"'check3d': "'} k. msg={t} rm endl error[] "Command 'display3d': "$msg
2483    endl
2484    nm={n} nbv,nbp={f2ui([i[6],i[7]])}
2485    e[0--5] "Display 3D object ["{arg(1+$>,$[])}"] = '"$nm"' ("$nbv" vertices, "$nbp" primitives)."
2486
2487    # Init display window and variables.
2488    disp_title=$nm" ("$nbv" vertices, "$nbp" primitives)"
2489    if !{*}
2490      if narg($_d3d_wh) w[] ${fitscreen\ $_d3d_wh,1},0,$disp_title
2491      else w[] {0.7*[{*,u,v}]},0,$disp_title
2492      fi
2493    else disp_normalization={*,n} w[] -1,-1,0,$disp_title fi
2494    disp_size0={*,w,h}
2495
2496    (1,0,0,0;0,1,0,0;0,0,1,0) store. pose3d
2497    posx,posy,zoomfactor=50,50,1
2498    is_fullscreen,is_zbuffer,is_axes3d,is_outline,is_boundingbox,is_animate,is_outvideo={*,f},1,1,0,0,0,0
2499    mode_render=4
2500    mode_drender={$nbp<2048?$mode_render:-1}
2501    mode_background={$is_user_background?7:3}
2502    mode_orientation=2
2503    mode_animate=1
2504    speed_animate=1
2505    focale=800
2506    mx0,my0,mx1,my1=
2507    notification= fontsize_notif=
2508    wait_event=1
2509
2510    # Create 3D axes.
2511    axes3d 40,40,40,20,X,Y,Z,0 col3d. 0,255,0 l. s3d a[0-3] y off_axes={0,h} a y endl nm. axes3d
2512
2513    # Start interactive loop.
2514    do
2515      is_motion={narg($mx0)}
2516      if $is_animate" || "$is_outvideo rmn render fi
2517
2518      # Init background image.
2519      if !narg($background)
2520        if $mode_background<3 {*,w,h},1,3,{arg(1+$mode_background,0,255,128)}
2521        elif $mode_background==3 3,2,1,1,"32,32,64,64,116,96" permute. cyzx r. {*,w,h},1,3,3 round.
2522        elif $mode_background==4 3,2,1,1,"0,0,0,0,64,96" permute. cyzx r. {*,w,h},1,3,3 round.
2523        elif $mode_background==5 3,2,1,1,"8,0,0,160,90,0" permute. cyzx r. {*,w,h},1,3,3 round.
2524        elif $mode_background==6 2,2,1,1,110,90,90,110 r. 64,64,1,3 r. {*,w,h},1,3,0,2,0.5,0.5
2525        else $_d3d_background r. {*,w,h},1,3,1
2526        fi
2527        w. nm. background
2528        rmn object3d
2529      fi
2530
2531      # Init normalized 3D object.
2532      if !narg($object3d)
2533        +c3d[0] n3d. *3d. {background,$zoomfactor*0.65*min(w,h)}
2534        if $mode_orientation==1 rv3d. fi
2535        rmn boundingbox3d
2536        if $is_boundingbox +boundingbox3d. o3d. 0.35 nm. boundingbox3d +3d.. . rv[-2,-1] fi
2537        nm. object3d
2538        rmn render
2539      fi
2540
2541      # Render 3D object.
2542      if !narg($render)
2543        $pose3d
2544        if $is_animate" || "$is_outvideo l.
2545          da={$speed_animate*($is_animate?20*($|-$time_animate):1)}
2546          if $mode_animate==0 rotation3d 1,0,0,$da rv
2547          elif $mode_animate==1 rotation3d 0,1,0,$da rv
2548          elif $mode_animate==2 rotation3d 0,0,1,$da rv
2549          else
2550            dax,day,daz={[0.75,0.82,0.97]*$da}
2551            rotation3d 0,1,0,$dax rotation3d 1,0,0,$day rotation3d 0,0,1,$daz rv
2552          fi
2553          m* 1,3,1,1 j.. .,3 rm. +store. pose3d
2554          time_animate=$|
2555        endl fi
2556        p={^} rm.
2557        m={$is_motion?$mode_drender:$mode_render}
2558        [background] nm. render
2559        if $m<0
2560          if !narg($boundingbox3d) +boundingbox3d[object3d] o3d. 0.35 nm. boundingbox3d fi
2561          +pose3d[boundingbox3d] $p
2562          j3d[render] .,$posx%,$posy%,0,1,1,0,0,$focale rm.
2563        else
2564          +pose3d[object3d] $p
2565          if $is_outline
2566            {render,[w,h]},1,3,-1
2567            j3d. ..,$posx%,$posy%,0,1,$m,{$mode_orientation==2},$is_zbuffer,$focale
2568            +channels. 0 !=. -1
2569            +dilate. 5 r. 100%,100%,1,3 j[render] .,0,0,0,0,0.8,. rm.
2570            j[render] ..,0,0,0,0,1,. rm[-2,-1]
2571          else j3d[render] .,$posx%,$posy%,0,1,$m,{$mode_orientation==2},$is_zbuffer,$focale
2572          fi
2573          rm.
2574        fi
2575        if $is_axes3d
2576          +pose3d[axes3d] $p
2577          eval " # Colorize axes depending on their Z-sign
2578            const off = "$off_axes";
2579            ref([ 255,0,0 ],col);
2580            i[13]>0?copy(i[off],col,3);
2581            i[19]>0?copy(i[off + 3],col,3);
2582            i[25]>0?copy(i[off + 6],col,3);
2583          "
2584          j3d[render] .,50,{-2,h-50},0,0.75,1,0,0,$focale rm.
2585        fi
2586        rmn view
2587
2588        if $is_outvideo
2589          o[render] $filename_outvideo,20,0,1
2590          nb_frames={int(360/$speed_animate)}
2591          if $is_outvideo>=$nb_frames
2592            o[] $filename_outvideo,20,0,0 # Close video stream
2593            is_outvideo=0
2594            notification="Output Video:\nDone!"
2595          else
2596            is_outvideo+=1
2597            notification="Output Video:\nFrame "{$is_outvideo+1}/$nb_frames
2598          fi
2599        fi
2600      fi
2601
2602      # Manage notifications.
2603      if narg($notification)
2604        wait_event=0
2605        if !isnum($notification) # Create notification gfx
2606          rmn notification_gfx
2607          ofs,fs={narg($fontsize_notif)?0$fontsize_notif:32}
2608          do
2609            0 t. {``$notification},0,0,$fs,1,255
2610            if narg($fontsize_notif) break
2611            elif {background,"(w#-1>0.7*w || h#-1>0.25*h) && "$fs>13" && "$ofs>=$fs}
2612              ofs,fs=$fs,{max(13,round($fs/1.25))} rm.
2613            elif {background,"w#-1<0.3*w && h#-1<0.25*h && "$fs<64" && "$ofs<=$fs}
2614              ofs,fs=$fs,{min(64,round($fs*1.25))} rm.
2615            else
2616              fontsize_notif=$fs break
2617            fi
2618          while 1
2619          r. {[w+12,h+8]},1,1,0,0,0.5,0.5 rectangle. 0,0,100%,100%,1,0xFFFFFFFF,255 to_rgb.
2620          nm. notification_gfx
2621          notification=$|
2622        else
2623          if $|>$notification+1 rm[notification_gfx] wait_event=1 notification= fi
2624          rmn view
2625        fi
2626      fi
2627
2628      # Refresh window view.
2629      if !narg($view)
2630        if $notification_gfx
2631          +j[render] [notification_gfx],0.99~,5,0,0,{sqrt(max(0,1-($|-$notification)))}
2632        else [render]
2633        fi
2634        nm. view w. -1,-1,0,$is_fullscreen,$disp_title
2635      fi
2636
2637      if $is_motion" || "$is_animate wait_event=0 fi
2638      if $wait_event wait elif !$is_outvideo wait 20 fi
2639
2640      # Manage user-events
2641      if $exit_on_anykey" && "{*,k} break fi
2642      if $is_outvideo continue fi # Skip user event management
2643      mx,my,mb={*,x,y,b}
2644      is_CTRL={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
2645      if {*,-F1} # Render: Dots
2646        mode_render,mode_drender={M=0;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2647        notification="Render: Dots" if $mode_drender<0 notification.=" + Box" fi
2648        rmn render
2649      elif {*,-F2} # Render: Wireframe
2650        mode_render,mode_drender={M=1;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2651        notification="Render: Wireframe" if $mode_drender<0 notification.=" + Box" fi
2652        rmn render
2653      elif {*,-F3} # Render: Flat
2654        mode_render,mode_drender={M=2;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2655        notification="Render: Flat" if $mode_drender<0 notification.=" + Box" fi
2656        rmn render
2657      elif {*,-F4} # Render: Flat-shaded
2658        mode_render,mode_drender={M=3;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2659        notification="Render: Flat-Shaded" if $mode_drender<0 notification.=" + Box" fi
2660        rmn render
2661      elif {*,-F5} # Render: Gouraud-shaded
2662        mode_render,mode_drender={M=4;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2663        notification="Render: Gouraud-Shaded" if $mode_drender<0 notification.=" + Box" fi
2664        rmn render
2665      elif {*,-F6} # Render: Phong-shaded
2666        mode_render,mode_drender={M=5;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2667        notification="Render: Phong-Shaded" if $mode_drender<0 notification.=" + Box" fi
2668        rmn render
2669      elif {*,-F7}" && "($focale>100" || "!$focale) # Decrease focale
2670        if !$focale focale=2000 else focale-=100 fi
2671        notification="Focale: "$focale
2672        rmn render
2673      elif {*,-F8}" && "$focale # Increase focale
2674        if $focale>=2000 focale=0 notification="Focale: Inf" else focale+=100 notification="Focale: "$focale fi
2675        rmn render
2676      elif {*,-F9} # Choose animation mode
2677        mode_animate={($mode_animate+1)%4}
2678        n0,n1,n2,n3="X-Axis","Y-Axis","Z-Axis","XYZ-Axes" notification="Animation Mode: "${n$mode_animate}
2679        rmn render
2680      elif {*,-F10} # Choose animation speed
2681        speed_animate={max(1,($speed_animate+1)%9)}
2682        notification="Animation Speed: X"$speed_animate
2683      elif {*,-SPACE} # Start/stop animation
2684        is_animate,time_animate={!$is_animate},$|
2685        n0,n1="Off","On" notification="Animation: "${n$is_animate}
2686        rmn render
2687      fi
2688      if $is_CTRL
2689        if {*,-A} # Show/hide 3D axes
2690          is_axes3d={!$is_axes3d}
2691          n0,n1="Off","On" notification="3D Axes: "${n$is_axes3d}
2692          rmn render
2693        elif {*,-B} # Change background
2694          mode_background={($mode_background+1)%($is_user_background?8:7)}
2695          n0,n1,n2,n3,n4,n5,n6,n7=\
2696            "Black","White","Gray","Gradient \#1","Gradient \#2","Gradient \#3","Checkerboard","User-Defined"
2697          notification="Background: "${n$mode_background}
2698          rmn background
2699        elif {*,-C}" && "{*,w}>128" && "{*,h}>128 w[] {0.8*[{*,w,h}]} # Decrease window size
2700          notification="Decrease Window Size" fontsize_notif=
2701          rmn background
2702        elif {*,-D}" && "{*,w}<0.8*{*,u}" && "{*,h}<0.8*{*,v} w[] {1.25*[{*,w,h}]} # Increase window size
2703          notification="Increase Window Size" fontsize_notif=
2704          rmn background
2705        elif {*,-F} # Toggle fullscreen
2706          is_fullscreen={!$is_fullscreen}
2707          if $is_fullscreen w[] {*,u,v} else w[] {0.75*[{*,u,v}]} fi
2708          n0,n1="Off","On" notification="Fullscreen: "${n$is_fullscreen} fontsize_notif=
2709          rmn background
2710        elif {*,-G} # Save object as a .obj file
2711          n=0 do filename gmic.obj,$n n+=1 while isfile(['{/${}}'])
2712          notification="Save Copy:\n"${}
2713          o[0] ${}
2714        elif {*,-L} # Show/hide outline
2715          is_outline={!$is_outline}
2716          n0,n1="Off","On" notification="Outline: "${n$is_outline}
2717          rmn render
2718        elif {*,-O} # Save object as a .gmz file
2719          n=0 do filename gmic.gmz,$n n+=1 while isfile(['{/${}}'])
2720          o[0] ${}
2721          notification="Save Copy:\n"${}
2722        elif {*,-P} # Print 3D pose on console
2723          $pose3d v 0 e[] "  > 3D Pose = [ "{^}" ]." rm.
2724        elif {*,-R} # Reset window
2725          w[] $disp_size0
2726          notification="Reset Window Size" fontsize_notif=
2727          rmn background
2728        elif {*,-S} # Save screenshot
2729          n=0 do filename gmic.png,$n n+=1 while isfile(['{/${}}'])
2730          o[render] ${}
2731          notification="Save Screenshot:\n"${}
2732        elif {*,-T} # Change orientation mode
2733          mode_orientation={($mode_orientation+1)%3}
2734          n0,n1,n2="Forward","Backward","Double-Sided" notification="Orientation: "${n$mode_orientation}
2735          if $mode_orientation rv3d[object3d] fi
2736          rmn render
2737        elif {*,-V} # Start/stop output video
2738          is_outvideo={!$is_outvideo}
2739          is_animate=0
2740          n=0 do filename gmic.mp4,$n n+=1 while isfile(['{/${}}'])
2741          filename_outvideo=${}
2742        elif {*,-X} # Show/hide bounding box
2743          is_boundingbox={!$is_boundingbox}
2744          n0,n1="Off","On" notification="Bounding Box: "${n$is_boundingbox}
2745          rmn object3d
2746        elif {*,-Z} # Enable/disable Z-buffer
2747          is_zbuffer={!$is_zbuffer}
2748          n0,n1="Off","On" notification="Z-Buffer: "${n$is_zbuffer}
2749          rmn render
2750        fi
2751      fi
2752      if {*,-r} rmn background fontsize_notif= fi
2753
2754      if $mx>=0 # Manage mouse-drag
2755        if $mb
2756          if !narg($mx0) mx0,my0,mx1,my1=$mx,$my,$mx,$my else mx1,my1=$mx,$my fi
2757        else
2758          if narg($mx0) rmn render fi
2759          mx0,my0,mx1,my1=
2760        fi
2761      fi
2762
2763      # Estimate new 3D pose from motion.
2764      if narg($mx1)" && "($mx0!=$mx1" || "$my0!=$my1)
2765        rmn render
2766        if $mb&1" && "!$is_CTRL # Rotation
2767          rotation3d[] {"
2768            const w2 = "{*,w}"/2;
2769            const h2 = "{*,h}"/2;
2770            const R = 0.375*min("{*,w,h}");
2771            const u0 = "$mx0" - w2;
2772            const v0 = "$my0" - h2;
2773            const u1 = "$mx1" - w2;
2774            const v1 = "$my1" - h2;
2775            n0 = norm(u0,v0);
2776            nu0 = n0>R?u0*R/n0:u0;
2777            nv0 = n0>R?v0*R/n0:v0;
2778            nw0 = sqrt(max(0,R^2 - nu0^2 - nv0^2));
2779            n1 = norm(u1,v1);
2780            nu1 = n1>R?u1*R/n1:u1;
2781            nv1 = n1>R?v1*R/n1:v1;
2782            nw1 = sqrt(max(0,R^2 - nu1^2 - nv1^2));
2783            u = nv0*nw1 - nw0*nv1;
2784            v = nw0*nu1 - nu0*nw1;
2785            w = nv0*nu1 - nu0*nv1;
2786            n = norm(u,v,w);
2787            [ u,v,w,-asin(n/R^2)*180/pi ]"}
2788          $pose3d m*[-2,-1] store. pose3d
2789          mx0,my0=$mx1,$my1
2790        elif $mb&4" || "($mb&1" && "$is_CTRL) # Pan
2791          posx,posy={"
2792            const px = "$mx1-$mx0+$posx*{*,w}%";
2793            const py = "$my1-$my0+$posy*{*,h}%";
2794            cut([ px*100/"{*,w}", py*100/"{*,h}" ],0,100)"}
2795          mx0,my0=$mx1,$my1
2796        elif $mb&2 # Zoom with mouse button
2797          fact={1+($my0-$my1)/100}
2798          zoomfactor*=$fact
2799          *3d[object3d] $fact if narg($boundingbox3d) *3d[boundingbox3d] $fact fi
2800          mx0,my0=$mx1,$my1
2801        fi
2802      fi
2803      if {*,o} # Zoom with mousewheel
2804        fact={1+{*,-o}/10}
2805        zoomfactor*=$fact
2806        *3d[object3d] $fact if narg($boundingbox3d) *3d[boundingbox3d] $fact fi
2807        rmn render
2808      fi
2809
2810    while {*}" && "!{*,ESC}" && "!($is_CTRL" && "{*,W})
2811    k[0]
2812  endl done
2813  if narg($disp_normalization) w[] -1,-1,$disp_normalization else w[] 0 fi
2814  v -1 d[]
2815
2816#@cli da : eq. to 'display_array'.
2817da :
2818  _gmic_s="$?" v + _display_array $*
2819
2820#@cli display_array : _width>0,_height>0
2821#@cli : Display images in interactive windows where pixel neighborhoods can be explored.
2822#@cli : Default values: 'width=13' and 'height=width'.
2823display_array :
2824  _gmic_s="$?" v + _$0 $*
2825
2826_display_array : check ${1=13}>0" && "${2=$1}>0
2827  e[0--3] "Display $1x$2 array of pixel values for image"$_gmic_s"."
2828
2829  dxb={round($1/2,1,1)} dxf={$1-1-$dxb}
2830  dyb={round($2/2,1,1)} dyf={$2-1-$dyb}
2831
2832  repeat $! l[$>]
2833    if w<128" && "h<128 r 128,128,100%,100%,0,0,0.5,0.5 fi # Manage cases of small and large images.
2834    x0=0 y0=0 w={w} h={h}
2835    wmax={0.9*{*,u}} hmax={0.9*{*,v}}
2836    do
2837      if w>=$wmax" || "h>=$hmax
2838        n={n} nm. "Image "'{b}.{x}'" is too large, please select a sub-image."
2839        +select. 2 x0={i[0]} y0={i[1]} w={1+i[3]-i[0]} h={1+i[4]-i[1]}
2840        rm. nm. $n
2841      fi
2842      +z. $x0,$y0,0,{$x0+$w-1},{$y0+$h-1},0 round. 1 n. 0,255
2843    while w>=$wmax" || "h>=$hmax
2844
2845    x1=-1 y1=-1 c1=0 ox1=-1 oy1=-1 oc1=-1
2846    x2=-1 y2=-1 c2=0 ox2=-1 oy2=-1 oc2=-1
2847    x3=-1 y3=-1 c3=0 ox3=-1 oy3=-1 oc3=-1
2848    c0=0 oxm=-1 oym=-1
2849    w. -1,-1,0,0,{-2,b}.{-2,x}
2850    do  # Enter event loop.
2851
2852      # Manage user interactions.
2853      wait[0-3]
2854      oc0=$c0
2855      repeat 4
2856        if $>" && "!{*$>}" && "${x$>}>=0 w$> 0 x$>=-1 y$>=-1 c$>=0 fi
2857        if {*$>,o} c$>={(${c$>}+sign({*$>,o}))%s} wait[$>] -1 fi
2858        if {*$>,SPACE}" || "{*$>,ENTER}" || "{*$>,ARROWRIGHT}" || "{*$>,ARROWDOWN} c$>={(${c$>}+1)%s} wait[$>] -1 fi
2859        if {*$>,BACKSPACE}" || "{*$>,ARROWLEFT}" || "{*$>,ARROWUP} c$>={(${c$>}-1)%s} wait[$>] -1 fi
2860      done
2861      if $oc0!=$c0 c1=$c0 c2=$c0 c3=$c0 fi
2862      xm={*,x} ym={*,y}
2863      if $xm>=0" && "{*,b}&1 x1=$xm y1=$ym fi
2864      if $xm>=0" && "{*,b}&2 x2=$xm y2=$ym fi
2865      if $xm>=0" && "{*,b}&4 x3=$xm y3=$ym fi
2866
2867      # Generate main image view.
2868      if $xm>=0" && "($oxm!=$xm" || "$oym!=$ym) w[] -1,-1,{-2,b}.{-2,x}" - ("$xm,$ym")" fi
2869      if $x1!=$ox1" || "$y1!=$oy1" || "$x2!=$ox2" || "$y2!=$oy2" || "$x3!=$ox3" || "$y3!=$oy3
2870        .
2871        if $x1>=0
2872          xb={$x1-$dxb} yb={$y1-$dyb} xe={$x1+$dxf} ye={$y1+$dyf}
2873          rectangle. $xb,$yb,$xe,$ye,0.2,0,255,255
2874          rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,0,255,255
2875        fi
2876        if $x2>=0
2877          xb={$x2-$dxb} yb={$y2-$dyb} xe={$x2+$dxf} ye={$y2+$dyf}
2878          rectangle. $xb,$yb,$xe,$ye,0.2,255,32,255
2879          rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,255,32,255
2880        fi
2881        if $x3>=0
2882          xb={$x3-$dxb} yb={$y3-$dyb} xe={$x3+$dxf} ye={$y3+$dyf}
2883          rectangle. $xb,$yb,$xe,$ye,0.2,255,255,0
2884          rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,255,255,0
2885        fi
2886        w. {-2,w},{-2,h} rm. oxm=$xm oym=$ym
2887      fi
2888
2889      # Generate zoomed views.
2890      if $x1>=0" && "($ox1!=$x1" || "$oy1!=$y1" || "$oc1!=$c1)
2891        +z.. {$x1-$dxb},{$y1-$dyb},0,$c1,{$x1+$dxf},{$y1+$dyf},0,$c1
2892        +z.. {$x1-$dxb},{$y1-$dyb},0,{$x1+$dxf},{$y1+$dyf},0
2893        __display_array[-2,-1] $1,$2,0,255,255
2894        w1. {w},{h},0,0,{-3,b}" - ("$x1,$y1,c=$c1")"
2895        rm. ox1=$x1 oy1=$y1 oc1=$c1
2896      fi
2897      if $x2>=0" && "($ox2!=$x2" || "$oy2!=$y2" || "$oc2!=$c2)
2898        +z.. {$x2-$dxb},{$y2-$dyb},0,$c2,{$x2+$dxf},{$y2+$dyf},0,$c2
2899        +z.. {$x2-$dxb},{$y2-$dyb},0,{$x2+$dxf},{$y2+$dyf},0
2900        __display_array[-2,-1] $1,$2,255,32,255
2901        w2. {w},{h},0,0,{-3,b}" - ("$x2,$y2,c=$c2")"
2902        rm. ox2=$x2 oy2=$y2 oc2=$c2
2903      fi
2904      if $x3>=0" && "($ox3!=$x3" || "$oy3!=$y3" || "$oc3!=$c3)
2905        +z.. {$x3-$dxb},{$y3-$dyb},0,$c3,{$x3+$dxf},{$y3+$dyf},0,$c3
2906        +z.. {$x3-$dxb},{$y3-$dyb},0,{$x3+$dxf},{$y3+$dyf},0
2907        __display_array[-2,-1] $1,$2,255,255,0
2908        w3. {w},{h},0,0,{-3,b}" - ("$x3,$y3,c=$c3")"
2909        rm. ox3=$x3 oy3=$y3 oc3=$c3
2910      fi
2911    while {*}" && "\
2912          !{*,ESC}" && "!{*,Q}" && "\
2913          !{*1,ESC}" && "!{*1,Q}" && "\
2914          !{*2,ESC}" && "!{*2,Q}" && "\
2915          !{*3,ESC}" && "!{*3,Q}
2916    k[0] w 0 w1 0 w2 0 w3 0
2917  endl done
2918
2919__display_array :
2920  round.. 1 c.. 0,999 r. 100%,100%,1,3,{s==1}
2921  +luminance. r.. {$1*24},{$2*24} grid.. {100/$1}%,{100/$2}%,0,0,1,0
2922  xb={24*int($1/2)} yb={24*int($2/2)} xe={$xb+24} ye={$yb+24}
2923  rectangle.. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,$3,$4,$5
2924  repeat $2,yg
2925    repeat $1,xg
2926      t.. {-3,i($xg,$yg)},{5+$xg*24},{5+$yg*24},13,0.8,{i($xg,$yg)>128?0:255}
2927    done
2928  done
2929  rm[-3,-1]
2930
2931#@cli dc : eq. to 'display_camera'.
2932dc : check_opencv $0
2933  v + _display_camera
2934
2935#@cli display_camera
2936#@cli : Open camera viewer.
2937#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
2938display_camera : check_opencv $0
2939  v + _$0
2940
2941_display_camera :
2942  e[0--3] "Open camera stream viewer."
2943
2944  # Initialize camera and get resolution.
2945  l[] camera
2946  onfail
2947    use_vt100
2948    e[0--4] ${_vt100_r}${_vt100_b}"Command 'display_camera': Unable to read camera stream. Exiting."$_vt100_n
2949    return
2950  endl
2951
2952  wc,hc={[w,h]}
2953
2954  # Open interactive window.
2955  w ${"fitscreen "$wc,$hc},0,"G'MIC Camera Stream Viewer"
2956  wnfs,hnfs={*,w,h}
2957  angle,fullscreen,brightness,contrast=0
2958  do
2959
2960    # Display frame from camera.
2961    ww,wh={*,w,h}
2962    camera
2963    if $angle rotate. {90*$angle} fi
2964    if $brightness +. {10*$brightness} c. 0,255 fi
2965    if $contrast /. 255 *. {1.2^$contrast} *. 255 c. 0,255 fi
2966    rr2d. $ww,$wh,2,1 w.
2967    rm
2968    wait 30
2969
2970    # Manage user events.
2971    if {*,r} w[] {*,d,e}
2972    elif {*,-R}" || "{*,-SPACE} angle={($angle+1)%4}
2973    elif {*,-ARROWUP} brightness={min(5,$brightness+1)}
2974    elif {*,-ARROWDOWN} brightness={max(-5,$brightness-1)}
2975    elif {*,-ARROWRIGHT} contrast={min(5,$contrast+1)}
2976    elif {*,-ARROWLEFT} contrast={max(-5,$contrast-1)}
2977    elif {*,-F}" || "{*,-ENTER}" || "{*,-F5}
2978       fullscreen={1-$fullscreen}
2979       if $fullscreen
2980         wwnfs,whnfs={*,w,h}
2981         w[] {*,u,v},0,1
2982       else
2983         w[] $wwnfs,$whnfs,0,0
2984       fi
2985    fi
2986
2987  while {*}" && "!{*,ESC}
2988  camera 0,0 w[] 0
2989
2990#@cli dfft : eq. to 'display_fft'.
2991dfft :
2992  v + _display_fft
2993
2994#@cli display_fft
2995#@cli : Display fourier transform of selected images, with centered log-module and argument.
2996#@cli : (eq. to 'dfft').
2997#@cli : $ image.jpg +display_fft
2998display_fft :
2999  v + _$0
3000
3001_display_fft :
3002  e[0--3] "Render fourier transform of image$? with centered log-module and argument."
3003  repeat $! l[$>] fftpolar +.. 1 log.. n 0,255 a x endl done s x,2
3004
3005#@cli dg : eq. to 'display_graph'.
3006dg : check "${1=0}>=0 && ${2=0}>=0" skip ${3=1},${4=0},${5=0},${6=0},${7=0},${8=0},"${9=x-axis}","${10=y-axis}"
3007  _display_graph ${1-8},"$9","$10"
3008
3009#@cli display_graph : _width>=0,_height>=0,_plot_type,_vertex_type,_xmin,_xmax,_ymin,_ymax,_xlabel,_ylabel
3010#@cli : Render graph plot from selected image data.
3011#@cli : 'plot_type' can be { 0=none | 1=lines | 2=splines | 3=bar }.
3012#@cli : 'vertex_type' can be { 0=none | 1=points | 2,3=crosses | 4,5=circles | 6,7=squares }.
3013#@cli : 'xmin','xmax','ymin','ymax' set the coordinates of the displayed xy-axes.
3014#@cli : if specified 'width' or 'height' is '0', then image size is set to half the screen size.
3015#@cli : Default values: 'width=0', 'height=0', 'plot_type=1', 'vertex_type=1', 'xmin=xmax=ymin=ymax=0 (auto)', \
3016# 'xlabel="x-axis"' and 'ylabel="y-axis"'.
3017#@cli : $ 128,1,1,1,'cos(x/10+u)' +display_graph 400,300,3
3018display_graph : check "${1=0}>=0 && ${2=0}>=0"
3019  skip ${3=1},${4=0},${5=0},${6=0},${7=0},${8=0},"${9=x-axis}","${10=y-axis}"
3020  _display_graph ${1-8},"$9","$10"
3021
3022_display_graph : check "${1=0}>=0 && ${2=0}>=0"
3023  skip ${3=1},${4=0},${5=0},${6=0},${7=0},${8=0},"${9=x-axis}","${10=y-axis}"
3024  e[0--3] "Render $1x$2 graph plot from data of image$?."
3025
3026  repeat $! l[$>] nm={0,n}
3027
3028    # Determine output size.
3029    if $1>0" && "$2>0 w,h=$1,$2 else w,h={{*,u}/2},{{*,v}/2} fi
3030    w,h={[max($w,33),max($h,33)]}
3031
3032    # Determine xmin,xmax/ymin,ymax.
3033    one={$3!=3} siz={w*h*d}
3034    if $5==$6 xmin=0 xmax={$siz-$one} else xmin={min($5,$6)} xmax={max($5,$6)} fi
3035    if $7==$8 ymin={im-(iM-im)/20} ymax={iM+(iM-im)/20} else ymin={min($7,$8)} ymax={max($7,$8)} fi
3036    dx={$xmax-$xmin} dy={$ymax-$ymin}
3037
3038    # Determine number of axes tick marks.
3039    u=${"_axes[] "$xmin,$xmax",{0.3*"$w"/14}"} offx={arg(1,$u)} deltax={arg(2,$u)}
3040    u=${"_axes[] "$ymin,$ymax",{0.3*"$h"/14}"} offy={arg(1,$u)} deltay={arg(2,$u)}
3041
3042    # Create plot canvas.
3043    gw={$w-32} gh={$h-32} gg={($gw-$one)/($siz-$one)}
3044    $gw,$gh,1,3,255
3045
3046    grid. {$deltax*$gw/$dx},{$deltay*$gh/$dy},{($offx-$xmin)*$gw/$dx},{$gh-($offy-$ymin)*$gh/$dy},0.25,0xCCCCCCCC,0
3047
3048    # Define color palette for curves.
3049    if s#-2==1 (120,120,200)
3050    elif s#-2<=3 (220,10,10;10,220,10;10,10,220)
3051    else
3052      (0,255) r. 256,1,1,1,3 map. 2 z. 2,100% permute. cxyz r. 3,{-3,max(3,s)},1,1,0,2
3053      sh. 0,2,0,0 f. 255,0,0,0,255,0,0,0,255 rm.
3054    fi
3055
3056    # Draw plot for each channel.
3057    repeat s#-3 sh... $> graph... .,$3,$4,$ymax,$ymin,1,{-2,@0-2} rm. shift. 0,-1 done
3058    rm[-3,-1]
3059    line. 0,0,100%,0,1,110 line. 100%,0,100%,100%,1,110
3060    line. 100%,100%,0,100%,1,255 line. 0,100%,0,0,1,255
3061
3062    100%,100%,1,1,255
3063    axes. $xmin,$xmax,$ymax,$ymin,14,1,0
3064    if $xmin>0 axes. 0,0,$ymax,$ymin,14,1,160 fi
3065    if $xmax<0 axes. {w-1},{w-1},$ymax,$ymin,14,1,160 fi
3066    if $ymin>0 axes. $xmin,$xmax,{h-1},{h-1},14,1,160 fi
3067    if $ymax<0 axes. $xmin,$xmax,0,0,14,1,160 fi
3068    +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1]
3069    frame. 16,16,220
3070    0 t. "$9",0,0,14,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm.
3071    0 t. "$10",0,0,14,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm.
3072
3073  nm $nm endl done c 0,255
3074
3075#@cli dh : eq. to 'display_histogram'.
3076dh :
3077  _gmic_s="$?" v + _display_histogram $"*"
3078
3079#@cli display_histogram : _width>=0,_height>=0,_clusters>0,_min_value[%],_max_value[%],_show_axes={ 0 | 1 },_expression.
3080#@cli : Render a channel-by-channel histogram.
3081#@cli : If selected images have several slices, the rendering is performed for all input slices.
3082#@cli : 'expression' is a mathematical expression used to transform the histogram data for visualization purpose.
3083#@cli : (eq. to 'dh').
3084#@cli : if specified 'width' or 'height' is '0', then image size is set to half the screen size.
3085#@cli : Default values: 'width=0', 'height=0', 'clusters=256', 'min_value=0%', 'max_value=100%', 'show_axes=1' \
3086# and 'expression=i'.
3087#@cli : $ image.jpg +display_histogram 512,300
3088display_histogram :
3089  _gmic_s="$?" v + _$0 $"*"
3090
3091_display_histogram : check "${1=0}>=0 && ${2=0}>=0 && ${3=256}>0" skip ${4=0%},${5=100%},${6=1},"${7=i}"
3092  e[0--3] "Render $1x$2 channel-by-channel histogram of image"$_gmic_s", with $3 clusters, minimum value $4
3093 and maximum value $5."
3094  repeat $! l[$>] nm={0,n}
3095    if ${is_percent\ $4} m={im+(iM-im)*$4} else m=$4 fi
3096    if ${is_percent\ $5} M={im+(iM-im)*$5} else M=$5 fi
3097    s={s} s c
3098    repeat $s l[{-1-$>}] s z histogram $3,$m,$M a z endl done
3099    a c f '"${7--1}"' vM={iM} s z
3100    repeat $! l[$>]
3101      if $1>0" && "$2>0 wh=$1,$2 else wh={{*,u}/2},{{*,v}/2} fi
3102      $wh,1,{s},-255
3103      repeat s sh[-2,-1] $> graph. ..,3,0,$vM,0,1,0 rm[-2,-1] done
3104      rm.. + 255
3105      if $6
3106        100%,100%
3107        axes. $m,$M,$vM,0,14,1,255
3108        if $m>0 axes. 0,0,$vM,0,14,1,200 fi
3109        if $M<0 axes. {w-1},{w-1},$vM,0,14,1,200 fi
3110        +dilate. 3 ri.. ... j... ..,0,0,0,0,1,.,255 rm[-2,-1]
3111      fi
3112    endl done
3113    a z nm $nm
3114  endl done
3115
3116#@cli display_parametric : _width>0,_height>0,_outline_opacity,_vertex_radius>=0,_is_antialiased={ 0 | 1 },\
3117# _is_decorated={ 0 | 1 },_xlabel,_ylabel
3118#@cli : Render 2D or 3D parametric curve or point clouds from selected image data.
3119#@cli : Curve points are defined as pixels of a 2 or 3-channel image.
3120#@cli : If the point image contains more than 3 channels, additional channels define the (R,G,B) color for each vertex.
3121#@cli : If 'outline_opacity>1', the outline is colored according to the specified vertex colors and
3122#@cli : 'outline_opacity-1' is used as the actual drawing opacity.
3123#@cli : Default values: 'width=512', 'height=width', 'outline_opacity=3', 'vertex_radius=0', 'is_antialiased=1',\
3124# 'is_decorated=1', 'xlabel="x-axis"' and 'ylabel="y-axis"'.
3125#@cli : $ 1024,1,1,2,'t=x/40;if(c==0,sin(t),cos(t))*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)' display_parametric 512,512
3126#@cli : $ 1000,1,1,2,u(-100,100) quantize 4,1 noise 12 channels 0,2 +normalize 0,255 append c \
3127# display_parametric 512,512,0.1,8
3128display_parametric : check "${1=512}>0 && ${2=$1}>0 && ${4=0}>=0" skip ${3=3},${5=1},${6=1},"${7=x-axis}","${8=y-axis}"
3129  s0="no " s1="" o0="" o1="colored "
3130  e[^-1] "Render $1x$2 parametric graph plot from data of image$?, with "${o{$3>1}}"outline opacity "\
3131      {$3>1?$3-1:$3}", vertex radius $4, "${s{$5!=0}}"antialiasing and "${s{$6!=0}}"decoration."
3132  repeat $! l[$>]
3133    nm={0,n} N={w*h*d}
3134    i[0] ('CImg3d') +[0] 0.5 i[1] ($N;$N)  # Header + nb of vertices/primitives.
3135
3136    # Calibrate colors of vertices.
3137    if s==4 +channels. 3,3 r. 100%,100%,1,2 a[-2,-1] c is_grayscale=1
3138    else is_grayscale={s<4} channels. 0,5
3139    fi
3140
3141    # Manage coordinates of vertices.
3142    sh. 0 xm={im} xM={iM} rm.
3143    sh. 1 ym={im} yM={iM} rm.
3144    sh. 2 zm={im} zM={iM} rm.
3145    permute. cxyz s. x,2
3146    i.. (1,0;1,{$N-1}) r.. 2,$N,1,1,3 round.. 1,$N,1,1,1 # Primitives, colors and opacities.
3147    y a y c3d n3d *3d 1,-1,1
3148
3149    {if($6,max(1,$1-32),$1)},{if($6,max(1,$2-32),$2)},1,{if($is_grayscale,1,3)},255
3150    *3d[0] {0.96*min(w,h)}
3151    if $6 L={0.1*max($1,$2)} grid[1] $L,$L,0,0,0.25,0xCCCCCCCC,0 fi
3152
3153    if $5 # Anti-aliased.
3154      r[1] 200%,200%,1,100%,1 *3d[0] 2
3155      if $4 +circles3d[0] {2*$4} j3d[1] [2],50%,50%,0,1,3,0,0 rm[2] fi
3156    elif $4 # Aliased.
3157      +circles3d[0] $4 j3d[1] [2],50%,50%,0,1,3,0,0 rm[2]
3158    fi
3159
3160    # Convert point cloud to connected segments.
3161    if $3 l[0] s3d f[1] 'i-y' rm[3] i[3] (2,0,1;2,{$N-2},{$N-1}) r[3] 3,{$N-1},1,1,3 round[3]
3162      r[5] 1,{h-1},1,1,0
3163      if $3>1 r[4] 3,{4,h/3},1,1,-1 r[4] 3,{4,h-1},1,1,2 else rm[4] i[4] 3,{$N-1} fi
3164      y a y endl j3d[1] [0],50%,50%,0,{if($3>1,$3-1,$3)},2,0,0
3165    fi
3166
3167    rm[0]
3168    if $5 r. 50%,50%,1,100%,2 fi
3169
3170    if $6 # Add decoration.
3171      xc={0.5*($xm+$xM)} yc={0.5*($ym+$yM)} dx={0.5*($xM-$xm)/0.96} dy={0.5*($yM-$ym)/0.96}
3172      xm={$xc-$dx} xM={$xc+$dx} ym={$yc-$dy} yM={$yc+$dy}
3173      100%,100%,1,1,255 axes. $xm,$xM,$yM,$ym,14,1,0
3174      if $xm>0 axes. 0,0,$yM,$ym,14,1,160 fi
3175      if $xM<0 axes. {w-1},{w-1},$yM,$ym,14,1,160 fi
3176      if $ym>0 axes. $xm,$xM,{h-1},{h-1},14,1,160 fi
3177      if $yM<0 axes. $xm,$xM,0,0,14,1,160 fi
3178      +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1]
3179      frame 1,1,128 frame 15,15,220
3180      0 t. "$7",0,0,14,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm.
3181      0 t. "$8",0,0,14,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm.
3182    fi
3183    nm. $nm
3184  endl done
3185
3186#@cli dp : eq. to 'display_parallel'.
3187dp :
3188  _gmic_s="$?" v + _display_parallel 1
3189
3190#@cli display_parallel
3191#@cli : Display each selected image in a separate interactive display window.
3192#@cli : (eq. to 'dp').
3193display_parallel :
3194  _gmic_s="$?" v + _$0 1
3195
3196# $1 = Normalization used for display window.
3197_display_parallel : check ${1=0}>=0
3198  e[0--3] "Display image$? in parallel."
3199  print
3200  is_d2d_compatible={"res = l<=10; repeat (l,k, res&=(w#k==w && h#k==h && d#k==1 && s#k==s))"}
3201  if $is_d2d_compatible
3202    N=$!
3203    repeat $!
3204      if s=['{$>,n}'];find(s,_'.',size(s)-1,-1)>0 nm={$>,b}.{$>,x} else nm={$>,b} fi
3205      ('$nm':;)
3206    done
3207    store[-$N--1] _d2d_names
3208    a z _display2d 0,$1 s z
3209    _d2d_names=
3210  else
3211    if $!<=1 v - d v + return fi
3212    14,$! eval. "!x?copy(i(),[[',d['],vtos(y,10,10),_']'])" =. 0 discard. 0 str={t} rm.
3213    m "__dp : parallel "$str __dp um __dp
3214  fi
3215  v -1 d[]
3216
3217#@cli dp0 : eq. to 'display_parallel0'.
3218dp0 :
3219  _gmic_s="$?" v + _display_parallel 0
3220
3221#@cli display_parallel0
3222#@cli : Display each selected image in a separate interactive display window, without value normalization.
3223#@cli : (eq. to 'dp0').
3224display_parallel0 :
3225  _gmic_s="$?" v + _display_parallel 0
3226
3227#@cli display_polar : _width>32,_height>32,_outline_type,_fill_R,_fill_G,_fill_B,_theta_start,_theta_end,_xlabel,_ylabel
3228#@cli : Render polar curve from selected image data.
3229#@cli : 'outline_type' can be { r<0=dots with radius -r | 0=no outline | r>0=lines+dots with radius r }.
3230#@cli : 'fill_color' can be { -1=no fill | R,G,B=fill with specified color }.
3231#@cli : Default values: 'width=500', 'height=width', 'outline_type=1', 'fill_R=fill_G=fill_B=200', 'theta_start=0', \
3232# 'theta_end=360', 'xlabel="x-axis"' and 'ylabel="y-axis"'.
3233#@cli : $ 300,1,1,1,'0.3+abs(cos(10*pi*x/w))+u(0.4)' display_polar 512,512,4,200,255,200
3234#@cli : $ 3000,1,1,1,'x^3/1e10' display_polar 400,400,1,-1,,,0,{15*360}
3235display_polar : check "${1=500}>32 && ${2=$1}>32"
3236  skip ${3=1},${4=200},${5=$4},${6=$5},${7=0},${8=360},"${9=x-axis}","${10=y-axis}"
3237  e[^-1] "Render $1x$2 polar graph plot from data of image"$_gmic_s", with outline $4 and fill color ($4,$5,$6)."
3238  repeat $! l[$>] nm={0,n}
3239
3240    # Compute (x,y) coordinates of the polar curve points.
3241    M={max(abs(iM),abs(im))}
3242    * {0.48*min($1,$2)/$M}
3243    y ({$7*pi/180};{-$8*pi/180}) r. 1,..,1,1,3
3244    +sin. cos.. *. ... *[-3,-2]
3245    a[-2,-1] x N={h}
3246    nm. coords
3247
3248    # Generate 3D object for curve outline.
3249    if $3
3250      ('CImg3d') +. 0.5 ($N,$N)
3251      +z[coords] 0,2
3252      1,$N,1,1,2 1,$N,1,1,'y' ++. 1 a[-3--1] x =. 0,2,100%
3253      3,$N,1,1,0 1,$N,1,1,1 y[-6--1] a[-6--1] y
3254      nm. _plot_polar_outline
3255    fi
3256
3257    # Generate 3D object for filling.
3258    if "$4>=0 && $5>=0 && $6>=0"
3259      ('CImg3d') +. 0.5 ({$N+1},$N)
3260      +z[coords] 0,-1,2,100% z. 0,2
3261      1,$N,1,1,3 1,$N 1,$N,1,1,'1+y' ++. 1 a[-4--1] x =. 1,3,100%
3262      3,$N,1,1,$4,$5,$6 1,$N,1,1,1
3263      y[-6--1] a[-6--1] y
3264      nm. _plot_polar_fill
3265    fi
3266    rm[coords]  # Remove original curve coordinates.
3267
3268    # Render graph image.
3269    {$1-32},{$2-32},1,3,255
3270    L={0.1*max($1,$2)} grid. $L,$L,0,0,0.25,0xCCCCCCCC,0  # Draw background grid
3271    if "$4>=0 && $5>=0 && $6>=0"                          # Draw curve filling
3272      j3d. [_plot_polar_fill],50%,50%,0,1,2,1,0
3273      rm[_plot_polar_fill]
3274    fi
3275    if $3
3276      if $3>=0                                            # Draw curve outline
3277        j3d. [_plot_polar_outline],50%,50%,0,1,1,0,0
3278      fi
3279      if $3!=0                                            # Draw curve vertices
3280        if abs($3)>1 circles3d[_plot_polar_outline] {abs($3)} fi
3281        j3d. [_plot_polar_outline],50%,50%,0,0.2,2,0,0
3282      fi
3283      rm[_plot_polar_outline]
3284    fi
3285
3286    # Draw axes and frame.
3287    nM={$M/0.96}
3288    100%,100%,1,1,255 axes. {-$nM},$nM,$nM,{-$nM},14,1,0
3289    +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1]
3290    frame. 1,1,128 frame. 15,15,220
3291    0 t. "$9",0,0,13,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm.
3292    0 t. "$10",0,0,13,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm.
3293
3294  nm $nm endl done
3295
3296#@cli dq : eq. to 'display_quiver'.
3297dq :
3298  _gmic_s="$?" v + _display_quiver $*
3299
3300#@cli display_quiver : _size_factor>0,_arrow_size>=0,_color_mode={ 0=monochrome | 1=grayscale | 2=color }
3301#@cli : Render selected images of 2D vectors as a field of 2D arrows.
3302#@cli : (eq. to 'dq').
3303#@cli : Default values: 'size_factor=16', 'arrow_size=1.5' and 'color_mode=1'.
3304#@cli : $ image.jpg +luminance gradient[-1] xy rv[-2,-1] *[-2] -1 a[-2,-1] c crop 60,10,90,30 +display_quiver[1] ,
3305display_quiver :
3306  _gmic_s="$?" v + _$0 $*
3307
3308_display_quiver : check "${1=16}>0 && ${2=1.5}>=0 && isint(${3=2}) && $3>=0 && $3<=2"
3309  e[0--3] "Render field of 2D arrows from image"$_gmic_s", with size factor $1, arrow size $2 in "\
3310          ${arg\ 1+$3,monochrome,grayscale,color}" mode."
3311  repeat $! l[$>]
3312    +norm. /.. {max(1e-6,iM)} rm. # Normalize vector values.
3313    {$1*w},{$1*h},1,{"1<<cut($3,0,2)"}
3314    eval.. "
3315      begin(ref(resize([255],s#1,1),C));
3316      "${-math_lib}"
3317      len = norm2(I(x,y));
3318      ang = atan2(i(x,y,0,1),i(x,y,0,0))*180/pi;
3319      P = [ -0.5,-0.05, 0.3,-0.05, 0.2,-0.25, 0.5,0, 0.2,0.25, 0.3,0.05, -0.5,0.05 ];
3320      P*=$1*$2*len;
3321      X = resize([(x+0.5)*w#1/w,(y+0.5)*h#1/h],size(P),0,2);
3322      X+=mul(P,rot(-ang°),2);
3323      if ($3,
3324        v = min(1,max(0.5,3*len));
3325        if ($3==1,
3326          C = [ 255*v,255 ],
3327          C = [ hsv2rgb([ ang,1,v ]), 255 ];
3328        );
3329      );
3330      repeat (3,k,
3331        i0 = arg(k + 1,0,2,4);
3332        i1 = arg(k + 1,2,10,6);
3333        i2 = arg(k + 1,12,12,8);
3334        polygon(#1,3,X[i0,2],X[i1,2],X[i2,2],1,C);
3335      ); I"
3336    rm..
3337  endl done
3338
3339#@cli drgba : eq. to 'display_rgba'.
3340drgba : skip "${1=none},${2=$1},${3=$1}"
3341  _gmic_s="$?" v + _display_rgba ${^0} v -
3342  if !${} noarg fi
3343
3344#@cli display_rgba : _background_RGB_color
3345#@cli : Render selected RGBA images over a checkerboard or colored background.
3346#@cli : (eq. to 'drgba').
3347#@cli : Default values: 'background_RGB_color=undefined' (checkerboard).
3348#@cli : $ image.jpg +norm threshold[-1] 40% blur[-1] 3 normalize[-1] 0,255 append c display_rgba
3349display_rgba : skip "${1=none},${2=$1},${3=$1}"
3350  _gmic_s="$?" v + _$0 ${^0} v -
3351  if !${} noarg fi
3352
3353_display_rgba : skip "${1=},${2=$1},${3=$1}"
3354  l[] is_rgb={"isnum($1)"} onfail is_rgb=0 endl
3355  if $is_rgb e[0--4] "Render RGBA image"$_gmic_s" over RGB background ($*)."
3356  else e[0--4] "Render RGBA image"$_gmic_s" over a checkerboard background."
3357  fi
3358  repeat $! l[$>] if s==2" || "s==4
3359    if $is_rgb i[0] 100%,100%,1,3 fc[0] {[$*]}
3360    else i[0] (160,128;128,160) r[0] 16,16 r[0] [1],[1],1,{s-1},0,2
3361    fi
3362    nm[0] {1,n}
3363    sh. {s-1} j[0] [1],0,0,0,0,1,[2],255 k[0]
3364  fi endl done to_rgb u $is_rgb
3365
3366#@cli dt : eq. to 'display_tensors'.
3367dt :
3368  _gmic_s="$?" v + _display_tensors $*
3369
3370#@cli display_tensors : _size_factor>0,_ellipse_size>=0,_color_mode={ 0=monochrome | 1=grayscale | 2=color },_outline>=0
3371#@cli : Render selected images of tensors as a field of 2D ellipses.
3372#@cli : (eq. to 'dt').
3373#@cli : Default values: 'size_factor=16', 'ellipse_size=1.5', 'color_mode=2' and 'outline=2'.
3374#@cli : $ image.jpg +diffusiontensors 0.1,0.9 resize2dx. 32 +display_tensors. 64,2
3375#@cli : $$ https://gmic.eu/oldtutorial/_display_tensors
3376display_tensors :
3377  _gmic_s="$?" v + _$0 $*
3378
3379_display_tensors : check "${1=16}>0 && ${2=1.5}>=0 && isint(${3=2}) && $3>=0 && $3<=2 && ${4=2}>=0"
3380  e[0--3] "Render field of 2x2 tensors from image"$_gmic_s", with size factor $1, ellipse size $2 in "\
3381          ${arg\ 1+$3,monochrome,grayscale,color}" mode and outline $4."
3382  repeat $! l[$>]
3383    * {($2*$1/2)/max(abs(im),abs(iM))} # Normalize tensor values.
3384    {$1*w},{$1*h},1,{"1<<cut($3,0,2)"}
3385    f.. "
3386      begin(C = resize([255],s#1,1); Co = resize([0],s#1,1); Co[s#1 - 1] = $3?255:0);
3387      "${-math_lib}"
3388      X = ([ x,y ]+=0.5)*$1;
3389      T = [ R, G, G, B ];
3390      E = eig(T);
3391      r1 = E[0];
3392      r2 = E[1];
3393      ang = atan2(E[3],E[2])*180/pi;
3394      if ($3,
3395        v = min(1,max(0.5,3*r1/($1*$2)));
3396        if ($3==1,
3397          C = [ 255*v,255 ],
3398          C = [ hsv2rgb([ 2*ang,1-r2/r1,v ]), 255 ];
3399        );
3400      );
3401      for (k = 1, k>=0, --k, ellipse(#1,X,r1 + k*$4,r2 + k*$4,ang°,1,arg(k + 1,C,Co)));
3402      I"
3403    rm..
3404  endl done
3405
3406#@cli dw : eq. to 'display_warp'.
3407dw :
3408  _gmic_s="$?" v + _display_warp $*
3409
3410#@cli display_warp : _cell_size>0
3411#@cli : Render selected 2D warping fields.
3412#@cli : (eq. to 'dw').
3413#@cli : Default value: 'cell_size=15'.
3414#@cli : $ 400,400,1,2,'x=x-w/2;y=y-h/2;r=sqrt(x*x+y*y);a=atan2(y,x);5*sin(r/10)*[cos(a),sin(a)]' +display_warp 10
3415display_warp :
3416  _gmic_s="$?" v + _$0 $*
3417
3418_display_warp : check "${1=15}>0"
3419  e[0--3] "Render 2D warping field"$_gmic_s", with cell size $1."
3420  repeat $! l[$>]
3421    if d!=1" || "s!=2
3422      error[0--3] "Command 'display_warp': Invalid image ["{$!-$>-1}"]: Dimensions "{w}","{h}","{d}","{s}"
3423       does not represent a 2D field of 2D vectors."
3424    fi
3425    i[0] 100%,100%,1,1,1 grid[0] $1,$1 nm[0] {1,n}
3426    warp[0] [1],1,1,0 rm[1]
3427  endl done * 255
3428
3429#@cli e : eq. to 'echo'. : (+)
3430
3431#@cli echo : message : (+)
3432#@cli : Output specified message on the error output.
3433#@cli : (eq. to 'e').\n
3434#@cli : Command selection (if any) stands for displayed call stack subset instead of image indices.
3435
3436#@cli echo_file : filename,message
3437#@cli : Output specified message, appending it to specified output file.
3438#@cli : (similar to 'echo' for specified output file stream).
3439echo_file : skip "${2='\n'}"
3440  ('"${2--1}\n"') ot. $1 rm.
3441
3442#@cli echo_stdout : message
3443#@cli : Output specified message, on the standard output (stdout).
3444#@cli : (similar to 'echo' for output on standard output instead of standard error).
3445echo_stdout :
3446  ('"$*"\n') o. -.raw,uchar rm.
3447
3448#@cli function1d : 0<=smoothness<=1,x0>=0,y0,x1>=0,y1,...,xn>=0,yn
3449#@cli : Insert continuous 1D function from specified list of keypoints (xk,yk)
3450#@cli : in range [0,max(xk)] (xk are positive integers).
3451#@cli : Default values: 'smoothness=1' and 'x0=y0=0'.
3452#@cli : $ function1d 1,0,0,10,30,40,20,70,30,80,0 +display_graph 400,300
3453function1d : check "${1=1}>=0 && $1<=1" skip ${2=0},${3=0}
3454  e[^-1] "Input continuous 1D function, with smoothness $1 and keypoints (${2--1})."
3455  l[]
3456
3457  # Sort and normalize input keypoints.
3458  smoothness={max(0,min(1,$1))}
3459  (${2--1}) r 2,{int(w/2)},1,1,-1
3460  sort +,y s x size={0,if(iM>=0,1+int(iM),0)}
3461  if !$size rm 0 break fi
3462  a x
3463
3464  # Compute slopes for splines.
3465  +f '0.5*(j(0,1,0,0,0,1)-j(0,-1,0,0,0,1))' s. x max.. 0.01 /. .. rm.. a x
3466
3467  # Determine spline coefficients for each part of the curve.
3468  $size,1,1,1,-1
3469  repeat h#0-1
3470    x0={0,i(0,$>)} y0={0,i(1,$>)} x1={0,i(0,$>+1)} y1={0,i(1,$>+1)}
3471    slope={($y1-$y0)/max(0.01,$x1-$x0)}
3472    yp0={0,i(2,$>)*$smoothness+(1-$smoothness)*$slope}
3473    yp1={0,i(2,$>+1)*$smoothness+(1-$smoothness)*$slope}
3474    i={round($x0,1,1)} j={round($x1,1,0)}
3475    line[1] $i,0,$j,0,1,$>
3476    if $j-$i<=1 # Linear interpolation for very close points.
3477      ({$y0-$x0*$slope}^{$slope}^0^0)
3478    else # Cubic interpolation otherwise.
3479      (1,$x0,{($x0)^2},{($x0)^3};\
3480       1,$x1,{($x1)^2},{($x1)^3};\
3481       0,1,{2*$x0},{3*($x0)^2};\
3482       0,1,{2*$x1},{3*($x1)^2})
3483      ($y0;$y1;$yp0;$yp1)
3484      invert.. mmul[-2,-1] y. c
3485    fi
3486  done
3487  a[2--1] x map.. . rm.
3488
3489  # Render final curve.
3490  100%,1,1,1,1
3491  (0,{w-1}) r. {-2,w},1,1,1,3 round.
3492  +sqr. +*[-2,-1] a[-4--1] c *[-2,-1] s. c +[-4--1] rm..
3493  endl
3494
3495#@cli i : eq. to 'input'. : (+)
3496
3497#@cli input : \
3498# [type:]filename : \
3499# [type:]http://URL : \
3500# [selection]x_nb_copies>0 : \
3501# { width>0[%] | [image_w] },{ _height>0[%] | [image_h] },{ _depth>0[%] | [image_d] },{ _spectrum>0[%] \
3502# | [image_s] },_{ value1,_value2,... | 'formula' } : \
3503# (value1{,|;|/|^}value2{,|;|/|^}...[:{x|y|z|c|,|;|/|^}]) : \
3504# 0 : (+)
3505#@cli : Insert a new image taken from a filename or from a copy of an existing image [index],
3506#@cli : or insert new image with specified dimensions and values. Single quotes may be omitted in
3507#@cli : 'formula'. Specifying argument '0' inserts an 'empty' image.
3508#@cli : (eq. to 'i' | (no arg)).
3509#@cli : Default values: 'nb_copies=1', 'height=depth=spectrum=1' and 'value1=0'.
3510#@cli : $ input image.jpg
3511#@cli : $ input (1,2,3;4,5,6;7,8,9^9,8,7;6,5,4;3,2,1)
3512#@cli : $ image.jpg (1,2,3;4,5,6;7,8,9) (255^128^64) 400,400,1,3,'if(x>w/2,x,y)*c'
3513#@cli : $$
3514
3515#@cli input_565 : filename,width>0,height>0,reverse_endianness={ 0 | 1 }
3516#@cli : Insert image data from a raw RGB-565 file, at the end of the list.
3517#@cli : Default value: 'reverse_endianness=0'.
3518input_565 : check "isint($2) && $2>0 && isint($3) && $3>0 && isbool(${4=0})"
3519  e[^-1] "Input raw RGB-565 file '$1', with size $2x$3."
3520  l[] raw:"$1",ushort if $4 endian ushort fi
3521  r $2,$3,1,1,-1 +>> 5 &. 63 +&.. 31 >>... 11 *[-3,-1] 8 *.. 4 a c endl
3522
3523#@cli input_cube : "filename",_convert_1d_cluts_to_3d={ 0 | 1 }.
3524#@cli : Insert CLUT data from a .cube filename (Adobe CLUT file format).
3525#@cli : Default value: 'convert_1d_cluts_to_3d=1'.
3526input_cube : skip ${2=1}
3527  e[^-1] "Input CLUT from file '$1'"
3528  l[]
3529    it[] "$1" f "i<_' ' && i!=10?_' ':i" s -,10
3530    i[0] 0
3531    range={"
3532      ref(vector128(),line);
3533      dmin = [ 0,0,0 ];
3534      dmax = [ 1,1,1 ];
3535      dim = size = 0;
3536      target = 0;
3537      for (k = 1, k<l, ++k,
3538        linesiz = min(size(line)-1,h#k);
3539        copy(line[0],i[#k,0],linesiz);
3540        copy(line[linesiz],0,size(line) - linesiz,1,0);
3541        same(line,'LUT_1D_SIZE ',12)?(
3542          copy(line[0],line[12],size(line) - 12);
3543          size = stov(line);
3544          dim = 1;
3545          resize(#0,3,size,1,1,0);
3546        ):same(line,'LUT_3D_SIZE ',12)?(
3547          copy(line[0],line[12],size(line) - 12);
3548          size = stov(line);
3549          dim = 3;
3550          resize(#0,3,size,size,size,0);
3551        ):same(line,'DOMAIN_MIN ',11)?(
3552          copy(line[0],line[11],size(line) - 11);
3553          dmin[0] = stov(line);
3554          ind = find(line,_' ');
3555          copy(line[0],line[ind + 1],size(line) - ind);
3556          dmin[1] = stov(line);
3557          ind = find(line,_' ');
3558          copy(line[0],line[ind + 1],size(line) - ind);
3559          dmin[2] = stov(line);
3560        ):same(line,'DOMAIN_MAX ',11)?(
3561          copy(line[0],line[11],size(line) - 11);
3562          dmax[0] = stov(line);
3563          ind = find(line,_' ');
3564          copy(line[0],line[ind + 1],size(line) - ind);
3565          dmax[1] = stov(line);
3566          ind = find(line,_' ');
3567          copy(line[0],line[ind + 1],size(line) - ind);
3568          dmax[2] = stov(line);
3569        ):(
3570          val = stov(line);
3571          !isnan(val)?do (
3572            i[#0,target++] = val;
3573            ind = find(line,_' ');
3574            ind<0?break();
3575            copy(line[0],line[ind + 1],size(line) - ind);
3576            val = stov(line);
3577          ,_(while); !isnan(val));
3578        );
3579      );
3580      [dmin,dmax];"}
3581
3582    k[0]
3583    permute yzcx
3584    if [$range]!=[0,0,0,1,1,1]
3585      f "begin(
3586           range = ["$range"];
3587           dmin = range[0,3];
3588           dmax = range[3,3];
3589           delta = dmax - dmin;
3590         );
3591         (I - dmin)*255/delta"
3592    else * 255
3593    fi
3594    if "w>1 && h==1 && d==1 && $2" size={w} s c y.. y. z r $size,$size,$size a c fi
3595    nm "$1"
3596  endl
3597
3598#@cli input_flo : "filename"
3599#@cli : Insert optical flow data from a .flo filename (vision.middlebury.edu file format).
3600input_flo :
3601  e[^-1] "Input optical flow from file '$1'"
3602  l[]
3603    i raw:"$1",float
3604    if i!=202021.25 endian. fi
3605    if i!=202021.25 error[0--3] "Command 'input_flo': Filename '$1' is not a valid .flo file." return fi
3606    +rows 1,2 cast. float,uint w,h={^} rm.
3607    rows 3,100% r 2,$w,$h,1,-1 permute yzcx
3608  endl
3609
3610#@cli ig : eq. to 'input_glob'.
3611ig :
3612  v + _input_glob "$*"
3613
3614#@cli input_glob : pattern
3615#@cli : Insert new images from several filenames that match the specified glob pattern.
3616#@cli : (eq. to 'ig').
3617input_glob :
3618  _input_glob "$*"
3619
3620_input_glob :
3621  e[0--3] "Input all files that match glob pattern '$*'."
3622  files 3,"$*"
3623  N=$!
3624  m "_ig : $""=arg repeat $""# i ${arg{1+$>}} done"
3625  _ig ${} um _ig
3626  if $N==$! error[0--3] "Command 'input_glob': No matching filenames for pattern '$*'." fi
3627
3628#@cli input_gpl : filename
3629#@cli : Input specified filename as a .gpl palette data file.
3630input_gpl :
3631  e[^-1] "Input .gpl palette file '$*'."
3632  l[]
3633    it[] "$*" discard 13 replace 9,32 s -,10
3634    colors=0
3635    repeat $! l[$>]
3636      s -,32
3637      if $!>=3" && "isint({0,t})" && "isint({1,t})" && "isint({2,t}) colors=$colors;{0,t},{1,t},{2,t} fi
3638      rm 0
3639    onfail rm 0 endl done
3640    rm ($colors) rows 1,100% nm "$1" permute yzcx
3641  endl
3642
3643#@cli it : eq. to 'input_text'.
3644it :
3645  v + _input_text "$*"
3646
3647#@cli input_text : filename
3648#@cli : Input specified text-data filename as a new image.
3649#@cli : (eq. to 'it').
3650input_text :
3651  v + _$0 "$*"
3652
3653_input_text :
3654  e[0--3] "Input text-data file '$*'."
3655  i raw:"$*",uchar
3656  if i[0]==239" && "i[1]==187" && "i[2]==191 rows. 3,100% fi # Remove BOM
3657  discard. {'\r'} # Remove CR
3658
3659# Merge multi-lines (lines continued by backslash before NL) in a string image.
3660merge_multiline :
3661  eval[^] "*i==_'\\' && j[-1]!=_'\\' && j[+1]==_'\n'?(
3662            for (p = 2, j[p] && j[p]<=_' ' && j[p]!=_'\n', ++p); copy(i(),-1,p,1,0))"
3663  discard -1
3664
3665# Merge multi-line comments in a string image.
3666merge_multiline_comments :
3667  eval[^] "*i==_'\\' && j[-1]!=_'\\' && j[+1]==_'\n' && j[+2]==_'#'?(
3668            for (p = 3, j[p] && j[p]<=_' ' && j[p]!=_'\n', ++p); copy(i(),-1,p,1,0))"
3669  discard -1
3670
3671#@cli network : mode={ -1=disabled | 0=enabled w/o timeout | >0=enabled w/ specified timeout in seconds } : (+)
3672#@cli : Enable/disable load-from-network and set corresponding timeout.
3673#@cli : (Default mode is 'enabled w/o timeout').
3674
3675#@cli o : eq. to 'output'. : (+)
3676
3677#@cli output : [type:]filename,_format_options : (+)
3678#@cli : Output selected images as one or several numbered file(s).
3679#@cli : (eq. to 'o').
3680#@cli : Default value: 'format_options'=(undefined).
3681
3682#@cli output_565 : "filename",reverse_endianness={ 0=false | 1=true }
3683#@cli : Output selected images as raw RGB-565 files.
3684#@cli : Default value: 'reverse_endianness=0'.
3685output_565 : check "isbool(${2=0})"
3686  e[^-1] "Output image$? as raw RGB-565 file '$1'."
3687  N=$! repeat $N +l[$>]
3688    s c c 0,255 /[-3,-1] 8 /.. 4 round
3689    bsl... 11 bsl.. 5 +
3690    if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi
3691    if $2 endian ushort fi
3692    o raw:$fn,ushort rm
3693  endl done is_change 0
3694
3695#@cli output_cube : "filename"
3696#@cli : Output selected CLUTs as a .cube file (Adobe CLUT format).
3697output_cube :
3698 e[^-1] "Output CLUT$? as file '$1'."
3699 N=$! repeat $N +l[$>] to_rgb
3700   l={round((w*h*d)^(1/3))}
3701   if w*h*d!=$l^3 error "Command '$0': CLUT '"{n}"' has invalid dimensions "({w},{h},{d},{s}). fi
3702   r $l,$l,$l,3,-1 permute cxyz / 255
3703   if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi
3704   o dlm:$fn rm it[] $fn replace {','},32
3705   0 nm. $fn basename={b} rm.
3706   header="\# Created by: G'MIC (https://gmic.eu)\n"\
3707          "TITLE \""$basename"\"\n\n"\
3708          "# LUT size\n"\
3709          "LUT_3D_SIZE "$l"\n\n"\
3710          "# Data domain\n"\
3711          "DOMAIN_MIN 0.0 0.0 0.0\n"\
3712          "DOMAIN_MAX 1.0 1.0 1.0\n\n"\
3713          "# LUT data points\n"
3714   i[0] ('$header')
3715   y a y ot $fn rm
3716 endl done is_change 0
3717
3718#@cli output_flo : "filename"
3719#@cli : Output selected optical flow as a .flo file (vision.middlebury.edu file format).
3720output_flo :
3721  e[^-1] "Output optical flow$? as file '$1'."
3722  N=$! repeat $N +l[$>]
3723   w,h={[w,h]}
3724   channels 0,1 permute cxyz i[0] (202021.25) i[1] ($w,$h) cast[1] uint,float y a y
3725   if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi
3726   o raw:$fn,float rm
3727 endl done is_change 0
3728
3729#@cli output_ggr : filename,_gradient_name
3730#@cli : Output selected images as .ggr gradient files (GIMP).
3731#@cli : If no gradient name is specified, it is deduced from the filename.
3732output_ggr : skip "${2=}"
3733 e[^-1] "Output image$? as .ggr gradient file '$1'."
3734 N=$!
3735  repeat $N +l[$>] r 1,{w*h*d},1,100%,-1 to_rgba / 255
3736    if narg("$2") name="$2"
3737    else l[] 1 nm. "$1" ('{b}') f "if(x==0 && i>=_'a' && i<=_'z',i-_'a'+_'A',i)" name={t} rm endl
3738    fi
3739    ('"GIMP Gradient\nName: "$name\n{0,h}\n')
3740    repeat h#0
3741      start={_$>/{0,h}}
3742      end={_($>+1)/{0,h}}
3743      mid={_0.5*($start+$end)}
3744      rgba={0,I(0,$>)}
3745      r={arg(1,$rgba)} g={arg(2,$rgba)} b={arg(3,$rgba)} a={arg(4,$rgba)}
3746      ('$start" "$mid" "$end" "$r" "$g" "$b" "$a" "$r" "$g" "$b" "$a" 0 0\n"')
3747    done
3748    rm[0] a x
3749    if $N>1 ot ${"filename \"$1\",$>"} else ot "$1" fi
3750    rm
3751  endl done is_change 0
3752
3753#@cli output_gmz : filename,_datatype
3754#@cli : Output selected images as .gmz files (G'MIC native file format).
3755#@cli : 'datatype' can be { bool | uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }.
3756output_gmz : skip ${2=auto}
3757  e[^-1] "Output image$? as gmz file '$1', with pixel type '$2'."
3758  1,64
3759  eval "
3760    draw([_'G',_'M',_'Z'],0,0,0,0,1,3);
3761    pos = 4;
3762    repeat (l - 1,k,
3763      ref(name(#k,1026),nam);
3764      len = find(nam,0);
3765      len>=0?(
3766        pos + len>=h?resize(#-1,1,2*h + len,1,1,0);
3767        len>0?draw(nam,0,pos,0,0,1,len);
3768        pos+=1 + len;
3769      );
3770    );
3771    resize(#-1,1,pos,1,1,0)"
3772  o cimgz:$1,$2
3773  rm.
3774
3775#@cli output_obj : filename,_save_materials={ 0=no | 1=yes }
3776#@cli : Output selected 3D objects as Wavefront 3D object files.
3777#@cli : Set 'save_materials' to '1' to produce a corresponding material file (`.mtl`) and eventually texture files.
3778#@cli : Beware, the export to `.obj` files may be quite slow for large 3D objects.
3779#@cli : Default value: 'save_materials=1'.
3780output_obj : check "isbool(${2=1})" check3d 0
3781  s0,s1=out,
3782  e[^-1] "Output 3D object$? as Wavefront 3D object file '$1' (with"${s$2}" materials)."
3783  N=$! repeat $N nm={$>,n} +l[$>]
3784    nb_materials,nb_primitives,nb_textures,nb_tcoords=0
3785
3786    # Generate basename, file header and eval macros.
3787    if $N>1 filename=${"filename \"$1\",$>"} else filename="$1" fi
3788    0 nm. $filename basename={b} folder={f} ext={x} if narg($ext) ext=.$ext fi rm.
3789    fnobj="begin(out = 0);
3790           tos(x) = vtos(x,6);
3791           write(ind,s) = (
3792             l_s = find(s,0);
3793             l_s<=0?(l_s = size(s));
3794             out + l_s>h(#ind)?resize(#ind,1,2*h(#ind) + l_s,1,1,0);
3795             copy(i[#ind,out],s,l_s);
3796             out+=l_s)"
3797
3798    # Decompose into vertices, primitives and colors.
3799    s3d 0 nb_vertices,nbp={1,f2ui([i[0],i[1]])} rm[0,1,5]
3800    e[] "  > File '"$folder$basename$ext"' ("$nb_vertices" vertices, "$nbp" primitives)."
3801
3802    # Generate vertex output.
3803    l[0]
3804      e[] "    - Export vertices."
3805      r 3,{h/3},1,1,-1 permute zycx
3806      0 nm. out_obj
3807      eval.. ">"$fnobj";
3808        !(y%10000)?run('e[] \"\r    - Export vertices: ',round(100*y/h),' %  \"');
3809        ref(string('v ',tos(i0),' ',tos(i1),' ',tos(i2),'\n'),str);
3810         write(#"$out_obj",str); I;
3811        end(resize(#"$out_obj",1,out,1,1,0))"
3812      rm[0] i[0] ('"\n# Vertices.\n"':y) a y nm out_obj
3813      e[] "\r    - Export vertices: Done."
3814    endl
3815
3816    # Generate material output.
3817    if $2 l.
3818      e[] "    - Export materials."
3819      256,256,256 nm. materials_rgb
3820      1,$nbp,1,3 nm. materials
3821      0 nm. out_mtl
3822      mv[0] $!
3823      eval $fnobj";
3824        const nbp = "$nbp";
3825        off = nb_materials = nb_textures = 0;
3826        repeat (nbp,p,
3827          !(p%10000)?run('e[] \"\r    - Export materials: ',round(100*p/nbp),' %  \"');
3828
3829          i[off]!=-128?( # RGB color
3830            ref(cut(round(crop(0,off,1,3)),0,255),rgb);
3831            m = i(#"$materials_rgb",rgb);
3832            !m?(
3833              ++nb_materials;
3834              ref(string('newmtl m',nb_materials,'\n',
3835                         'Kd ',tos(rgb[0]/255),' ',tos(rgb[1]/255),' ',tos(rgb[2]/255),'\n'),str);
3836              write(#"$out_mtl",str);
3837              m = i(#"$materials_rgb",rgb) = nb_materials;
3838            );
3839            i[#"$materials",p] = m;
3840            off+=3;
3841
3842          ):i[off + 2]?( # Non-shared texture
3843            ++nb_materials; ++nb_textures;
3844            wt = i[off + 1]; ht = i[off + 2]; st = i[off + 3];
3845            run('+rows. ',off + 4,',',off + 3 + wt*ht*st,' r. ',wt,',',ht,',1,',st,',-1 tfn=$folder${basename}_t',
3846                nb_textures,'.png o. $tfn rm.');
3847            tfile = get('tfn',1024,1);
3848            ref(string('newmtl m',nb_materials,'\n',
3849                       'map_Kd ',get('tfn',1024,1),'\n'),str);
3850            write(#"$out_mtl",str);
3851            I[#"$materials",p] = [ nb_materials,wt,ht ];
3852            off+=4 + wt*ht*st;
3853
3854          ):( # Shared texture
3855            tind = i[off + 1];
3856            I[#"$materials",p] = I[#"$materials",tind];
3857            off+=4;
3858
3859          );
3860        );
3861        resize(#"$out_mtl",1,out,1,1,0);
3862        run('nb_materials,nb_textures=',nb_materials,',',nb_textures)"
3863      e[] "\r    - Export materials: Done."
3864      k[materials,out_mtl]
3865    endl fi
3866
3867    # Generate primitive output.
3868    e[] "    - Export primitives."
3869    0 nm. out_prim
3870    mv[1] $!
3871    eval $fnobj";
3872      const materials = 0"$materials";
3873      const nbp = "$nbp";
3874      off = p_material = material = nb_primitives = nb_tcoords = 0;
3875      repeat (nbp,p,
3876        !(p%10000)?run('e[] \"\r    - Export primitives: ',round(100*p/nbp),' %  \"');
3877
3878        type = i[off];
3879        $2?material = i[#materials,p];
3880        material!=p_material?ref(string('usemtl m',material,'\n'),str_mtl);
3881
3882        type==1?( # Colored point
3883          ref(string('p ',f2ui(i[off + 1] + 1),'\n'),str);
3884          write(#"$out_prim",str);
3885          ++nb_primitives;
3886
3887        ):type==2?( # Colored or textured segment
3888          ref(string('l ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,'\n'),str);
3889          write(#"$out_prim",str);
3890          ++nb_primitives;
3891
3892        ):type==3 || (type==9 && !$2)?( # Colored triangle
3893          $2 && material!=p_material?write(#"$out_prim",str_mtl);
3894          ref(string('f ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,' ',f2ui(i[off + 3]) + 1,'\n'),str);
3895          write(#"$out_prim",str);
3896          ++nb_primitives;
3897
3898        ):type==4 || (type==12 && !$2)?( # Colored quadrangle
3899          $2 && material!=p_material?write(#"$out_prim",str_mtl);
3900          ref(string('f ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,' ',
3901                     f2ui(i[off + 3]) + 1,' ',f2ui(i[off + 4]) + 1,'\n'),str);
3902          write(#"$out_prim",str);
3903          ++nb_primitives;
3904
3905        ):type==9?( # Textured triangle
3906          material!=p_material?write(#"$out_prim",str_mtl);
3907          wt = i(#materials,0,p,0,1); ht = i(#materials,0,p,0,2);
3908          ref(string('vt ',tos(i[off + 4]/wt),' ',tos(1 - i[off + 5]/ht),'\n',
3909                     'vt ',tos(i[off + 6]/wt),' ',tos(1 - i[off + 7]/ht),'\n',
3910                     'vt ',tos(i[off + 8]/wt),' ',tos(1 - i[off + 9]/ht),'\n',
3911                     'f ',f2ui(i[off + 1]) + 1,'/',nb_tcoords + 1,' ',f2ui(i[off + 2]) + 1,'/',nb_tcoords + 2,
3912                     ' ',f2ui(i[off + 3]) + 1,'/',nb_tcoords + 3,'\n'),str);
3913          write(#"$out_prim",str);
3914          nb_tcoords+=3;
3915          ++nb_primitives;
3916
3917        ):type==12?( # Textured quadrangle
3918          material!=p_material?write(#"$out_prim",str_mtl);
3919          wt = i(#materials,0,p,0,1); ht = i(#materials,0,p,0,2);
3920          ref(string('vt ',tos(i[off + 5]/wt),' ',tos(1 - i[off + 6]/ht),'\n',
3921                     'vt ',tos(i[off + 7]/wt),' ',tos(1 - i[off + 8]/ht),'\n',
3922                     'vt ',tos(i[off + 9]/wt),' ',tos(1 - i[off + 10]/ht),'\n',
3923                     'vt ',tos(i[off + 11]/wt),' ',tos(1 - i[off + 12]/ht),'\n',
3924                     'f ',f2ui(i[off + 1]) + 1,'/',nb_tcoords + 1,' ',f2ui(i[off + 2]) + 1,'/',nb_tcoords + 2,
3925                     ' ',f2ui(i[off + 3]) + 1,'/',nb_tcoords + 3,' ',f2ui(i[off + 4]) + 1,'/',nb_tcoords + 4,'\n'),str);
3926          write(#"$out_prim",str);
3927          nb_tcoords+=4;
3928          ++nb_primitives;
3929
3930        ):( # Unsupported primitive
3931          run(['warn[] \"Command 'output_obj': Cannot convert primitive \#'],p,'/',nbp - 1,
3932               ' (size ',type,'). Ignoring it.\"');
3933        );
3934
3935        off+=type + 1;
3936        p_material = material;
3937      );
3938      resize(#"$out_prim",1,out,1,1,0);
3939      run('nb_primitives=',nb_primitives)"
3940    e[] "\r    - Export primitives: Done."
3941    l[out_obj,out_prim] i[1] ('"\n# Primitives.\n"':y) a y endl
3942    if $2 k[out_mtl,out_obj] else k[out_obj] fi
3943
3944    # Generate file header.
3945    header="# Object name: "$nm\n\
3946           "# Vertices: "$nb_vertices\n\
3947           "# Primitives: "$nb_primitives\n\
3948           "# Materials: "$nb_materials\n\
3949           "# Textures: "$nb_textures\n\
3950           {`"d2(x) = (s = date(x); s>9?vtos(s,-1,2):[0,s]+_'0');
3951              string('# Generated on ',d2(0),'/',d2(1),'/',d2(2),' at ',d2(4),':',d2(5),':',d2(6),
3952                     ' by G\47MIC (https://gmic.eu).\n')"`}
3953
3954    # Save object geometry (.obj file).
3955    l[out_obj]
3956      i[0] ('"# File: "$folder$basename$ext\n$header':y)
3957      if $2 i[1] ('"\n# Materials.\n\
3958                      mtllib "$folder$basename$ext".mtl\n"':y) fi
3959      a y
3960      o raw:$folder$basename$ext,uchar
3961    endl
3962
3963    # Save object materials (.mtl file).
3964    if $2 l[out_mtl]
3965      i[0] ('"# File: "$folder$basename$ext.mtl\n$header\n':y)
3966      a y
3967      o raw:$folder$basename$ext.mtl,uchar
3968    endl fi
3969
3970    rm
3971  endl done is_change 0
3972
3973#@cli ot : eq. to 'output_text'.
3974ot :
3975  _gmic_s="$?" v + _output_text "$*"
3976
3977#@cli output_text : filename
3978#@cli : Output selected images as text-data filenames.
3979#@cli : (eq. to 'ot').
3980output_text :
3981  _gmic_s="$?" v + _$0 "$1"
3982
3983_output_text :
3984  e[0--3] "Output image"$_gmic_s" as text-data file '$1'."
3985  o raw:"$1",uchar
3986
3987#@cli on : eq. to 'outputn'.
3988on :
3989  _gmic_s="$?" v + _outputn $*
3990
3991#@cli outputn : filename,_index
3992#@cli : Output selected images as automatically numbered filenames in repeat...done loops.
3993#@cli : (eq. to 'on').
3994outputn :
3995  _gmic_s="$?" v + _$0 $*
3996
3997_outputn : skip "${2=}"
3998  if $#==1 filename=${filename\ "$1",$>}
3999  else filename=${filename\ "$1",$2}
4000  fi
4001  e[0--3] "Output image"$_gmic_s" as file '"$filename"'."
4002  o $filename
4003
4004#@cli op : eq. to 'outputp'.
4005op :
4006  _gmic_s="$?" v + _outputp $*
4007
4008#@cli outputp : prefix
4009#@cli : Output selected images as prefixed versions of their original filenames.
4010#@cli : (eq. to 'op').
4011#@cli : Default value: 'prefix=_'.
4012outputp :
4013  _gmic_s="$?" v + _$0 $*
4014
4015_outputp : skip ${1="_"}
4016  if $!>1 e[0--4] "Output image"$_gmic_s" as their initial locations, prefixed by '$1'."
4017  else e[0--4] "Output image"$_gmic_s" as its initial location, prefixed by '$1'."
4018  fi
4019  repeat $! o[$>] {$>,f}$1{$>,b}.{$>,x} done
4020
4021#@cli ow : eq. to 'outputw'.
4022ow :
4023  _gmic_s="$?" v + _outputw
4024
4025#@cli outputw
4026#@cli : Output selected images by overwriting their original location.
4027#@cli : (eq. to 'ow').
4028outputw :
4029  _gmic_s="$?" v + _$0 $*
4030
4031_outputw :
4032  if $!>1 e[0--4] "Output image"$_gmic_s" as their initial location."
4033  else e[0--4] "Output image"$_gmic_s" as its initial location."
4034  fi
4035  repeat $! o[$>] {$>,n} done
4036
4037#@cli ox : eq. to 'outputx'.
4038ox :
4039  _gmic_s="$?" v + _outputx $*
4040
4041#@cli outputx : extension1,_extension2,_...,_extensionN,_output_at_same_location={ 0 | 1 }
4042#@cli : Output selected images with same base filenames but for N different extensions.
4043#@cli : (eq. to 'ox').
4044#@cli : Default value: 'output_at_same_location=0'.
4045outputx :
4046  _gmic_s="$?" v + _$0 $*
4047
4048_outputx :
4049  $=arg
4050  is_last_arg=0 is_same_location=0
4051  if isnum($-1) is_last_arg={isint($-1)" && "$-1>=0" && "$-1<=1} is_same_location=$-1 fi
4052  N={$#-$is_last_arg} s0= s1=s
4053  if !$N e[0--3] "Output image"$_gmic_s" at same location, with same base filename but extension ''
4054                      (skipped, no extension provided)." return
4055  fi
4056  if $is_same_location
4057    if $is_last_arg
4058      e[0--4] "Output image"$_gmic_s" at same location, with same base filename but extension"${s{$N>1}}"' ${^-1}'."
4059    else
4060      e[0--4] "Output image"$_gmic_s" at same location, with same base filename but extension"${s{$N>1}}" '$*'."
4061    fi
4062     repeat $! l[$>]
4063      repeat $N ext=${arg{1+$>}} if ext=lowercase(['$ext']);ext=='jpg'||ext=='jpeg' ext.=,85 fi o {0,f}{0,b}.$ext done
4064    endl done
4065  else
4066    if $is_last_arg e[0--4] "Output image"$_gmic_s" with same base filename but extension"${s{$N>1}}"' ${^-1}'."
4067    else e[0--4] "Output image"$_gmic_s" with same base filename but extension"${s{$N>1}}" '$*'."
4068    fi
4069    repeat $! l[$>]
4070      repeat $N ext=${arg{1+$>}} if ext=lowercase(['$ext']);ext=='jpg'||ext=='jpeg' ext.=,85 fi o {0,b}.$ext done
4071    endl done
4072  fi
4073
4074#@cli parse_cli : _output_mode,_{ * | command_name }
4075#@cli : Parse definition of '#@cli'-documented commands and output info about them in specified output mode.
4076#@cli : 'output_mode' can be { ascii | bashcompletion | html | images | print }.
4077#@cli : Default values: 'output_mode=print' and 'command_name=*'.
4078parse_cli : skip "${1=print},${2=*}"
4079  e[^-1] "Parse '#@cli' command(s) '$2' and output in '$1' mode."
4080
4081  # Check that specified output mode is actually implemented.
4082  l[] ({'$$parse_cli_$1'}) rm
4083  onfail error[0--2] "Command 'parse_cli': Invalid output mode '$1'."
4084  endl
4085
4086  if !$! l[] it ${_path_rc}update$_version.gmic onfail endl fi
4087  if !$! return fi
4088  i[0] ('\n') y a y
4089  merge_multiline_comments
4090
4091  # Split commands/section into different blocs.
4092  eval "
4093    for (p = 0, p<h,
4094
4095      # Find next occurrence of line starting with '#@cli'.
4096      q = find(#0,'\n#@cli',p);
4097      q<0?(copy(i[p],0,h - p,1,0); break());
4098      for (r = q + 6, r<h && (c=i[r])<=_' ' && c!=_'\n', ++r);
4099      r>=h || i[r]=='\n'?(copy(i[p],0,r - p,1,0); p = r; continue());
4100
4101      (i[r]==_':' && i[r + 1]==_':')?( # Category
4102        r = find(#0,_'\n',r)%h;
4103
4104      ):i[r]!=_':'?do( # Command
4105        r = find(#0,_'\n',r)%h;
4106        crop(0,r,1,6)=='\n#@cli'?(
4107          for (s = r + 6, s<h && (c=i[s])<=_' ' && c!='_\n', ++s);
4108          s<h && i[s]==':' && i[s + 1]!=':'?(r = s):break();
4109        ):break(),1
4110
4111      ):( # Unexpected '#@cli' line -> Display warning
4112        r = find(#0,_'\n',r)%h;
4113        ref(vector1024(),line);
4114        copy(line,i[q + 1],min(size(line),r - q - 1));
4115        run('warn[0--3] \"Unexpected line: \'',line,'\'.\"');
4116        copy(i[p],0,r - p,1,0); p = r; continue()
4117      );
4118      copy(i[p],0,q - p + 1,1,0); # Set values to discard
4119      p = r)"
4120  s -,0
4121
4122  # Keep only command blocs and name them.
4123  if $!
4124    $!,1,1,1,">
4125      begin(
4126        ref(vector1024(),command);
4127        ref(vector1024(),category);
4128      );
4129      for (p = 5, p<h#x && (c=i[#x,p])<=_' ' && c!=_'\n', ++p);
4130      p>=h#x || i[#x,p]==_'\n'?(run('nm[',x,'] __to_discard__'); break());
4131
4132      i[#x,p]==_':' && i[#x,p + 1]==_':'?( # Category
4133        for (p+=2, i[#x,p]<=_' ', ++p);
4134        for (q = h#x - 1, i[#x,q]<=_' ', --q);
4135        copy(category,i[#x,p],l = min(q - p + 1,size(category) - 1));
4136        category[l] = 0;
4137        run('nm[',x,'] __to_discard__');
4138      ):( # Command
4139        for (q = p, q<h#x && (c=i[#x,q])!=_':' && c>_' ', ++q);
4140        copy(command,i[#x,p],l = min(q - p,size(command) - 1));
4141        command[l] = 0;
4142        run('nm[',x,'] \"',string(command,'@',category),'\"');
4143      )"
4144    rm. rmn __to_discard__
4145    l parse_cli_trigger_$1 $2 onfail endl
4146  fi
4147  parse_cli_$1
4148
4149# Return 1 if last of the selected bloc describes a shortcut command, 0 otherwise.
4150parse_cli_is_eqto :
4151  nbl={"n = 1; for (p = 0, (q = find(#-1,_'\n',p))>=0, ++n, p = ++q); n"}
4152  u {$nbl"==1 && find(#-1,'eq. to \'')>=0"}
4153
4154#
4155# Implements 'bashcompletion' mode for command 'parse_cli'.
4156#
4157parse_cli_bashcompletion :
4158  v 0 use_vt100
4159  sort_list +,n
4160  +e[] "#"\n\
4161"#  Bash completion rules for 'gmic'."\n\
4162"#"\n\
4163"# This file has been generated automatically."\n\
4164"# Do not edit!"\n\
4165"#"\n\
4166"# This file should be copied/renamed in '/usr/share/bash-completion/completions/gmic'."\n\
4167"#"\n\n\
4168"_gmic()"\n\
4169"{"\n\
4170"    local cur prev opts coms"\n\
4171"    if type -t _init_completion >/dev/null; then"\n\
4172"        _init_completion -n = || return"\n\
4173"    else"\n\
4174"        COMPREPLY=()"\n\
4175"        cur=\"${COMP_WORDS[COMP_CWORD]}\""\n\
4176"        prev=\"${COMP_WORDS[COMP_CWORD-1]}\""\n\
4177"    fi"
4178  coms="    coms=\"" c=
4179
4180  # Extract list of available commands.
4181  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4182    ('{n}') l. s -,{'@'} name={0,t} rm endl nm $name
4183    coms.=$c$name c=" "
4184  endl done
4185  +e[] $coms"\""\n\
4186"    opts=$(echo \"$coms\" | sed \"s: \\([^ ]\\+\\): \\1 -\\1 \\+\\1:g\")"\n
4187
4188  # Duplicate list of arguments for command shortcuts.
4189  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi
4190    name={$>,n}
4191    if ${parse_cli_is_eqto[$>]}
4192      +l[$>] s -,{'"eq. to "'} k. s -,39 eqto={0,t} rm endl
4193      l[$>] pass[$eqto] 0 k. nm $name
4194      onfail
4195        warn[] "Warning: Ignoring shortcut '"$_vt100_c$name$_vt100_m$_vt100_b"', "\
4196               "links to unknown command '"$_vt100_c$eqto$_vt100_m$_vt100_b"'."
4197        nm __to_discard__
4198      endl
4199    fi
4200  done
4201  rmn __to_discard__
4202
4203  # Extract list of arguments.
4204  +e[] "    case \"${prev}\" in"
4205  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4206    name={n}
4207    if n=['$name'];"n[0]!=_'_' && find(n,'input') && find(n,'output') && "\
4208                   "n!='i' && n!='o' && n!='m' && n!='it' && n!='ot'"
4209      if s=['$name'];s=='help'||s=='h'
4210        +e[] "        \""$name"\" | \"-"$name"\" | \"+"$name"\")"
4211        +e[] "            COMPREPLY=( $(compgen -W \"$coms\" -- \"$cur\") ); return 0;;"
4212      else
4213        s -,{'\n'} k[0] discard {'#@cli'} max {'" "'} autocrop {'" "'}
4214        s -,{':'} autocrop {'" "'} rm[0] replace {'" "'},{'_'}
4215        n_args=0 args,c= repeat $! if ['{/{$>,t}}']!='(+)' args.=$c{$>,t} c=" " n_args+=1 fi done
4216        if $n_args==1 args="> "$args fi
4217        if ['$args']!=0
4218          +e[] "        \""$name"\" | \"-"$name"\" | \"+"$name"\")"
4219          +e[] "            COMPREPLY=( $(compgen -W \""{/{/$args}}"\") ); return 0;;"
4220        fi
4221      fi
4222      rm 0
4223    fi
4224  endl done
4225
4226  +e[] "    esac"\n\n\
4227"    COMPREPLY=( $(compgen -W \"$opts\" -- \"$cur\") )"\n\
4228"    if type -t _filedir >/dev/null; then"\n\
4229"        _filedir"\n\
4230"    else"\n\
4231"        comptopt -o filenames 2>/dev/null"\n\
4232"        COMPREPLY=( $(compgen -f -- ${cur}) )"\n\
4233"    fi"\n\
4234"}"\n\
4235"complete -F _gmic -o filenames gmic"
4236  rm
4237
4238parse_cli_trigger_bashcompletion :
4239  parse_cli_trigger_print $*
4240
4241#
4242# Implements 'ascii' mode for command 'parse_cli'.
4243#
4244parse_cli_ascii :
4245  use_vt100
4246  if !narg($_shell_cols) _shell_cols={${-shell_cols}-5} fi
4247  category= n_category=0
4248  if !narg($_section) _section=1 fi
4249
4250  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4251
4252    # Detect and display new category.
4253    if !0$_no_categories
4254      ('{n}') l. s -,{'@'} if $!>1 cat={t} fi rm endl
4255      if ['$cat']!=['$category']
4256        n_category+=1
4257        category=$cat
4258        ('$_section.$n_category." "') ('$category') +f.. {'" "'} +f.. {'-'} a[-4,-3] x a[-2,-1] x
4259        +e[] \n"  "$_vt100_r$_vt100_b{-2,t}$_vt100_n
4260        +e[] "  "$_vt100_r{t}$_vt100_n
4261        rm[-2,-1]
4262      fi
4263    fi
4264
4265    # Parse command declaration.
4266    s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'}
4267    +l[0]
4268      s -,{'": "'} autocrop {'" "'}
4269      name={0,t} rm[0]
4270      is_builtin=0
4271      if $!
4272        is_builtin={"find(#-1,'(+)')>=0"}
4273        if $is_builtin rm. fi
4274      fi
4275      +e[] "\n  "$_vt100_m$_vt100_b$name${"s1,s0=\" (+)\", u $s"$is_builtin}:$_vt100_n
4276      repeat $! l[$>]
4277        if ${parse_cli_is_eqto.}
4278          s -,39 eqto={1,t} k[0]
4279          +e[] "      "${_vt100_i}"Shortcut for command '"$_vt100_m$_vt100_b$eqto$_vt100_n"'."
4280        else
4281          eqto=
4282          l. _gmd2ascii_cut $_shell_cols,8 if $!>1 i[1--2] ('"\\\\\n       "':y) a y fi endl
4283          str="      "{t}${"if "$<" u \" |\" else u \"\" fi"}
4284          +e[] ${_vt100_c}$str$_vt100_n
4285        fi
4286      endl done
4287      rm
4288    endl
4289    rm[0]
4290
4291    # Parse command description.
4292    n_example=0 nl="\n"
4293    repeat $! if {$>,i==_':'} l[$>]
4294      if "h==1 || (h==2 && i[-1,2]==_' ')" rm ('":  "':y) fi # Empty description lines
4295      rows {1+(i[1]==_'" "')},100%
4296      if "find(#0,'(eq. to ')>=0" # Found 'Eq. to' description.
4297        +l s -,{'\47'} shortcut={1,t} rm onfail shortcut="(unknown)" rm endl
4298        +e[] $nl"    ("${_vt100_i}"equivalent to shortcut command '"$_vt100_m$_vt100_b$shortcut$_vt100_n"')." nl="\n"
4299      elif "find(#0,'Default value:')>=0 || find(#0,'Default values:')>=0" # Found 'Default value(s):' description
4300        if !0$_no_default_values
4301          s +,{':'}
4302          if $!>2" && "h#1==1" && "i("#1")==_':'
4303            i[0] ('$_vt100_b':y) i[2] ('$_vt100_n':y)
4304          fi
4305          a y gmd2ascii $_shell_cols,5
4306          if $!>1 i[1--2] ('"\n      "':y) a y fi autocrop {'\n'}
4307          +e[] "\n    "{/{t}} nl=
4308        fi
4309      elif i==_'$'" && "i[1]!=_'$' # Found example description
4310        if !0$_no_examples
4311          rows 1,100% autocrop {'" "'}
4312          _gmd2ascii_cut {$_shell_cols-8},7
4313          if $!>1 i[1--2] ('"\\\\\n      "':y) a y fi autocrop {'\n'}
4314          example={/{t}}
4315          n_example+=1
4316          if $n_example==1 example_str=${_vt100_b}"Example:"$_vt100_n"\n      "[#$n_example] nl="\n"
4317          else example_str="  "[#$n_example]
4318          fi
4319          +e[] $nl"    "$example_str" "$_vt100_c$example$_vt100_n nl=
4320        fi
4321      elif i==_'$'" && "i[1]==_'$' # Found link to tutorial page
4322        if !0$_no_tutorial_link
4323          if h==2
4324            url=https://gmic.eu/tutorial/$name
4325          else
4326            rows 2,100 autocrop {'" "'} url={t}
4327          fi
4328          +e[] "\n    "${_vt100_b}"Tutorial: "$_vt100_n$_vt100_u$url$_vt100_n nl=
4329        fi
4330      else # Other kind of description line
4331        parse_cli_text_ascii.
4332        if w +e[] $nl{/{t}} nl= fi
4333      fi
4334
4335    endl fi done
4336    rm 0
4337  endl done rm u $eqto
4338
4339parse_cli_text_ascii :
4340  if !narg($_shell_cols) _shell_cols={${-shell_cols}-5} fi
4341  replace_str "\\n","\n" gmd2ascii $_shell_cols,0
4342
4343  # Ensure output text contains no more than two consecutive newlines.
4344  # Also add a 4-chars left margin on each line.
4345  if w
4346    s +,{'\n'}
4347    if {i==_'\n'} rm. fi # Remove last newline.
4348    eval "repeat (l,p,
4349            i(#p)==_'\n'?(
4350              h(#p)>2?resize(#p,1,1,1,1,0)
4351            ):(
4352              resize(#p,1,h#p + 4,1,1,0,0,0,1);
4353              copy(i[#p,0],_' ',4,1,0))
4354            )"
4355    a y
4356  fi
4357
4358parse_cli_trigger_ascii :
4359  if "['$1']!='*'"
4360    1,$!,1,1,"
4361      begin(ref([lowercase(['$1']),_'@'],str));
4362      ref(lowercase(name(#y)),nm);
4363      !find(nm,str)?y:-1"
4364    discard. -1 if h k[{i}] else rm fi
4365  fi
4366
4367#
4368# Implements 'html' mode for command 'parse_cli'.
4369#
4370parse_cli_html :
4371  v 0 use_vt100 e[] ""
4372
4373  # Sort by categories.
4374#  repeat $! ('{$>,n}') l. s +,{'@'} rv a y nm={t} endl rm. nm[$>] $nm done # Rename as 'category@name'
4375#  sort_list +,n # Sort by categories
4376#  repeat $! ('{$>,n}') l. s +,{'@'} rv a y nm={t} endl rm. nm[$>] $nm done # Rename as 'name@category'
4377
4378  # Generate html page 'List of Commands'.
4379  html="<!DOCTYPE html>"\n\
4380"<html lang=\"en\">"\n\
4381"  <head>"\n\
4382"    <meta charset=\"utf-8\">"\n\
4383"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
4384"    <link rel=\"stylesheet\" href=\"../highslide/highslide.css\"/>"\n\
4385"    <link rel=\"stylesheet\" href=\"https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css\">"\n\n\
4386"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
4387"- Reference Documentation</title>"\n\
4388"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
4389"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
4390"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
4391"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
4392"  </head>"\n\n\
4393"  <body>"\n\
4394"    <div id=\"include_header\"></div>"\n\n\
4395"    <div class=\"section_title\"><a href=\"index.html\"><p>Reference</p></a></div><div class=\"section_content\">"\n\n\
4396"    <a name=\"top\"></a>"\n\n\
4397"<!-- begin_content -->"\n\n\
4398"<!-- list_of_categories -->"\n
4399
4400  list_categories,c=
4401  category,p_category= n,is_tr,is_table,row=0
4402  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4403    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4404    if ['$category']!=['$p_category']
4405      strvar $category category_id=${}
4406      if $is_tr
4407        if $n%5" && "$row>1 html.="        <td colspan=\""{5-$n%5}"\"></td>"\n fi
4408        html.="      </tr>"\n is_tr=0
4409      fi
4410      if $is_table html.="    </table><br/>"\n is_table=0 fi
4411      html.="\n    <h1 class=\"ref_h1\"><a name=\""$category_id"\"></a>"$category":</h1>"\n\
4412            "    <table class=\"ref_table_category\">"\n
4413
4414      ('$category') replace_str. ",","&#44;" lcategory={t} rm.
4415      list_categories.=$c$lcategory c=,
4416      is_table=1
4417      n,row=0
4418    fi
4419    if !${parse_cli_is_eqto.}
4420      is_builtin={"find(#-1,'(+)')>=0"}
4421      if !($n%5)
4422        if $is_tr html.="      </tr>"\n fi
4423        html.="      <tr>"\n
4424        is_tr=1 row+=1
4425      fi
4426      if ['$name']=='index' url_name=_index.html else url_name=$name.html fi
4427      if $is_builtin
4428        html.="        <td><a href=\""$url_name"#top\"><span class=\"gmd_monospace\" >"$name"</span></a></td>"\n
4429      else
4430        html.="        <td><a href=\""$url_name"#top\">"$name"</a></td>"\n
4431      fi
4432      n+=1
4433    fi
4434    p_category=$category
4435  endl done
4436  if $is_tr
4437    if $n%5" && "$row>1 html.="        <td colspan=\""{5-$n%5}"\"></td>"\n fi
4438    html.="      </tr>"\n
4439  fi
4440  if $is_table html.="    </table>"\n fi
4441
4442  # Add 'Shortcuts' category.
4443  html.="\n    <h1 class=\"ref_h1\"><a name=\"shortcuts\"></a>Command Shortcuts:</h1>"\n\
4444        "    <table class=\"ref_table_shortcuts\">"\n\
4445        "      <tr style=\"background-color: \#d8dcea;\"><td><b>Shortcut name</b></td><td>"\
4446        "<b>Equivalent command name</b></td></tr>"\n
4447
4448  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4449    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4450    if ${parse_cli_is_eqto.}
4451      is_builtin={"find(#-1,'(+)')>=0"}
4452      +l s -,{'"eq. to \47"'} k. s -,{'\47'} k[0] autocrop {'" "'} eqto={t} rm endl
4453      ('$name') replace_str. ">","&gt;" replace_str. "<","&lt;" html_name={t} rm.
4454      html.="      <tr><td><a href=\""$eqto.html"#top\"><b>"$html_name"</b></a></td>"
4455      if $is_builtin
4456        html.="<td><a href=\""$eqto.html"#top\"><span class=\"gmd_monospace\">"$eqto"</span></a></td>"
4457      else
4458        html.="<td><a href=\""$eqto.html"#top\">"$eqto"</a></td>"
4459      fi
4460      html.="</tr>"\n
4461      n+=1
4462    fi
4463  endl done
4464  if $is_table html.="    </table>"\n fi
4465
4466  html.=\n\
4467"<!-- end_content -->"\n\n\
4468"<!-- ref_navigation_bottom -->"\n\
4469"    </div><div class=\"section_end\"></div>"\n\
4470"    <div id=\"include_footer\"></div>"\n\
4471"  </body>"
4472  ({'$html'}:y)
4473
4474  # Insert TOC for categories.
4475  toc_html="    <h1 class=\"ref_h1\">Categories:</h1><ul>"\n
4476  repeat narg({/$list_categories})
4477    arg 1+$>,{/$list_categories} category=${}
4478    ('$category') replace_str. "&#44;","," category={t} rm.
4479    strvar $category category_id=${}
4480    toc_html.="      <li><a href=\"#"$category_id"\">"$category"</a></li>"\n
4481  done
4482  toc_html.="      <li><a href=\"#shortcuts\">Command Shortcuts</a></li>"\n
4483  toc_html.="    </ul>"
4484
4485  replace_str. "<!-- list_of_categories -->",$toc_html
4486  ot. list_of_commands.html
4487  if !isfile('index.html') x "ln -fs list_of_commands.html index.html" fi
4488  rm.
4489
4490  # Retrieve full list of commands and discard those starting with '_'.
4491  repeat $! if s=['{$<,n}'];s[0]==_'_' rm[$<] else l[$<]
4492    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4493    is_eqto=${parse_cli_is_eqto.}
4494    if !$is_eqto _is_$name=1 else rm fi
4495  endl fi done
4496
4497  # Generate html page for each command.
4498  repeat $! l[$>]
4499    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4500    e[] "\r  > "$_vt100_c[#{1+$>}]$_vt100_n" "{`copy(vector48(_'" "'),['$category" / "$name'])`}
4501
4502    # Find previous and next commands.
4503    if !$> previous= else pass[{$>-1}] 1 ('{n}') discard. {'_c1'} previous={t} rm[-2,-1] fi
4504    if !$< next= else pass[{$>+1}] 1 ('{n}') l. s -,{'@'} next={0,t} rm endl rm. fi
4505    if ['$name']=='index' url_name=_index.html else url_name=$name.html fi
4506    if ['$previous']=='index' url_previous=_index.html else url_previous=$previous.html fi
4507    if ['$next']=='index' url_next=_index.html else url_next=$next.html fi
4508
4509    strvar[] $category category_id=${}
4510
4511    html="<!DOCTYPE html>"\n\
4512"<html lang=\"en\">"\n\
4513"  <head>"\n\
4514"    <meta charset=\"utf-8\">"\n\
4515"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
4516"    <link rel=\"stylesheet\" href=\"../highslide/highslide.css\"/>"\n\
4517"    <link rel=\"stylesheet\" href=\"https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css\">"\n\n\
4518"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
4519"- Reference Documentation - "$name"</title>"\n\
4520"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
4521"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
4522"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
4523"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
4524"    <script src=\"../highslide/highslide-full.js\"></script>"\n\
4525"    <script>"\n\
4526"      hs.graphicsDir = '../highslide/graphics/';"\n\
4527"      hs.wrapperClassName = 'wide-border';"\n\
4528"      hs.showCredits = 'false';"\n\
4529"    </script>"\n\
4530"  </head>"\n\n\
4531"  <body>"\n\
4532"    <div id=\"include_header\"></div>"\n\n\
4533"    <div class=\"section_title\"><a href=\"index.html\"><p>Reference</p></a></div>"\
4534"<div class=\"section_content\" style=\"padding-top: 0;\">"\n\n\
4535"      <a name=\"top\"></a>"\n\n\
4536"<!-- begin_content -->"\n\n
4537
4538    if !0$_pdf_output
4539      html.="      <table class=\"ref_navigation_top\"><tr><td>"\
4540"<a href=\"index.html\">Table of Contents</a>&nbsp;&nbsp;&#9656;&nbsp;&nbsp;"\
4541"<a href=\"list_of_commands.html#top\">List of Commands</a>&nbsp;&nbsp;&#9656;&nbsp;&nbsp;"\
4542"<a href=\"list_of_commands.html#"$category_id"\">"$category"</a>&nbsp;&nbsp;&#9656;&nbsp;&nbsp;"\
4543"<a href=\""$url_name"#top\"><samp>"$name"</samp></a></td>"
4544      if narg($previous)" || "narg($next)
4545        html.="<td>"
4546        if narg($previous) html.="<a href=\""$url_previous"#top\">&#9664;&nbsp;&nbsp;<samp>"$previous"</samp></a>" fi
4547        if narg($previous)" && "narg($next) html.="&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;" fi
4548        if narg($next) html.="<a href=\""$url_next"#top\"><samp>"$next"</samp>&nbsp;&nbsp;&#9654;</a>" fi
4549        html.="</td>"\n
4550      fi
4551      html.="      </tr></table>\n"
4552
4553    else
4554      html.="<img style=\"margin-top: 2em; width: 100%;\" src=\"reference_pdf.png\"/>"\n
4555    fi
4556
4557    # Parse command declaration.
4558    s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'}
4559    +l[0]
4560      s -,{'": "'} autocrop {'" "'} rm[0]
4561      is_builtin=0
4562      if $!" && find(#-1,'(+)')>=0" is_builtin=1 rm. fi
4563      if $is_builtin
4564        html.="      <table class=\"ref_h1_builtin\"><tr><td><h1 class=\"ref_h1\" style=\"margin-bottom: 0;\">"\
4565              $name"</h1></td><td><span class=\"ref_builtin_command\">Built-in command</span></td></tr></table>"\n
4566      else
4567        html.="      <h1 class=\"ref_h1\">"$name"</h1>"\n
4568      fi
4569      if $!
4570        html.="\n      <h2>Arguments:</h2>\n      <ul>"\n
4571        if $!>1 or="&nbsp;&nbsp;&nbsp; or" else or= fi
4572        repeat $! l[$>]
4573          if !$< or= fi
4574          html.="        <li><span class=\"gmd_monospace\">"{t}"</span>"$or"</li>"\n
4575        endl done
4576        html.="      </ul>"\n
4577      else
4578        html.="\n      <h3>No arguments</h2>"\n
4579      fi
4580      rm
4581    endl
4582    rm[0]
4583
4584    # Parse command description.
4585    html.="\n      <h2>Description:</h2>"\n
4586    line=0
4587    n_example=0
4588    nb_examples=0
4589    is_tutorial_tag=0
4590    tutorial_html=
4591
4592    repeat $! if {$>,i==_':'} l[$>]
4593      if "h==1 || (h==2 && i[-1,2]==_' ')" rm ('":  "':y) fi # Empty description lines
4594      rows {1+(i[1]==_'" "')},100%
4595      if i==_'$'" && "i[1]!=_'$' nb_examples+=1 fi
4596    endl fi done
4597
4598    repeat $! l[$>]
4599      if "find(#0,'(eq. to ')>=0"
4600
4601        # Found 'Eq. to' description.
4602        +l s -,{'\47'}
4603          replace_str[1] "&","&amp;"
4604          replace_str[1] "<","&lt;"
4605          replace_str[1] ">","&gt;"
4606          shortcut={1,t}
4607          rm
4608        onfail
4609          shortcut="(unknown)"
4610          rm
4611        endl
4612        html.="      <p>(<em>equivalent to shortcut command</em> <span class=\"gmd_monospace\">"$shortcut\
4613              "</span>).</p>"\n
4614
4615      elif "find(#0,'See also:')>=0"
4616
4617        # Found 'See also:' description.
4618        s +,{':'} autocrop {'" "'}
4619        if $!>2" && "h#1==1" && "i("#1")==_':'
4620          html.="\n      <h2>See also:</h2>"\n
4621          rm[0,1]
4622        fi
4623        a y
4624        gmd2html 0
4625        html.="      "{t}\n
4626
4627      elif "find(#0,'Default value:')>=0 || find(#0,'Default values:')>=0"
4628
4629        # Found 'Default value(s):' description
4630        s +,{':'} autocrop {'" "'}
4631        if $!>2" && "h#1==1" && "i("#1")==_':'
4632          html.="\n<h2>Default values:</h2>"\n
4633          rm[0,1]
4634        fi
4635        a y
4636        gmd2html 0
4637        html.="<p style=\"word-wrap:break-word\">"{t}"</p>"\n
4638
4639      elif i==_'$'" && "i[1]!=_'$'
4640
4641        # Found 'Example of use' description.
4642        rows 1,100% autocrop {'" "'}
4643        if !0$_pdf_output
4644          replace_str "image.jpg","<a href=\"image.jpg\" class=\"highslide\" onclick=\"return hs.expand(this)\">"\
4645                      "image.jpg</a>"
4646        fi
4647        example={/{t}}
4648        n_example+=1
4649        if $n_example>1 basename=${name}_$n_example.jpg else basename=${name}.jpg fi
4650        if $n_example==1
4651           if !$is_tutorial_tag html.="<tutorial_tag>" is_tutorial_tag=1 fi
4652           html.="\n      <h2>Example"${"if "$nb_examples">1 u s else u \"\" fi"}" of use:</h2>"\n fi
4653        if $nb_examples!=1 html.="      <h3>&bull;&nbsp;Example \#"$n_example"</h3>"\n fi
4654        html.="      <div class=\"gmd_code_block\">$ gmic "$example"</div><br/>"\n
4655
4656        if 0$_pdf_output html.="<div style=\"text-align: center;\">"\n fi
4657        if isfile('img/f_$basename') # Single output image
4658          if !0$_pdf_output
4659            html.="      <span><a href=\"img/"f_$basename"\" class=\"highslide\" onclick=\"return hs.expand(this)\">"\
4660                  "<img class=\"center_image\" src=\"img/"t_$basename"\"/></a>"\
4661                  "<div class=\"highslide-caption\">Command: <b>"$example"</b></div></span>"\n
4662          else
4663            html.="<img style=\"max-width:45%\" src=\"img/"t_$basename"\"/>"\n
4664          fi
4665        elif isfile('img/f0_$basename') # Multiple output images
4666          i=0
4667          html.="        <div class=\"center\">"
4668          for isfile('img/f${i}_$basename')
4669            if !0$_pdf_output
4670              html.="        <span><a href=\"img/"f${i}_$basename"\" class=\"highslide\" "\
4671                    "onclick=\"return hs.expand(this)\">"\
4672                    "<img src=\"img/"t${i}_$basename"\"/></a>"\
4673                    "<div class=\"highslide-caption\">Command: <b>"$example"</b></div></span>"\n
4674            else
4675              html.="<img style=\"max-width:45%\" src=\"img/"t${i}_$basename"\"/>"\n
4676            fi
4677            i+=1
4678          done
4679          html.="        </div>"
4680        fi
4681        if 0$_pdf_output html.="</div>"\n fi
4682
4683      elif i==_'$'" && "i[1]==_'$'
4684
4685        # Found link to tutorial page.
4686        if !$is_tutorial_tag html.="<tutorial_tag>" is_tutorial_tag=1 fi
4687        if h==2
4688          url=https://gmic.eu/tutorial/$name
4689          l[] it $url if "find(#-1,'404 Not Found')>=0" url=https://gmic.eu/oldtutorial/_$name fi rm endl
4690        else
4691          rows 2,100 autocrop {'" "'} url={t}
4692        fi
4693
4694        tutorial_html.="      <p>This command has a <span class=\"ref_tutorial_button\"><a href=\""$url"\">"\
4695                       "tutorial page</a></span>.</p>"
4696      else
4697
4698        # Other kind of description line
4699        +autocrop {"' '"} is_list={isin(i,_'*',_'-',_'.')} rm.
4700        ('\n') a y
4701        replace_str "\\n","\n" gmd2html 0
4702        if $line==1" && "!$is_list html.="      <br/>"\n fi
4703        html.={t}
4704        if i[-1,2]!=_'\n' html.="\n" fi
4705        line+=1
4706      fi
4707    endl done
4708
4709    # Generate page.
4710    html.=\n\
4711"<!-- end_content -->"\n\n\
4712"<!-- ref_navigation_bottom -->"\n\
4713"    </div><div class=\"section_end\"></div>"\n\
4714"    <div id=\"include_footer\"></div>"\n\
4715"  </body>"
4716    rm ({'$html'}:y)
4717    replace_str. "<tutorial_tag>",$tutorial_html # Insert tutorial button
4718    nm $name ot $url_name
4719  endl done
4720  rm
4721  e[] "\r  > "${_vt100_g}{`copy(vector64(_'" "'),'"Parsing done!"')`}$_vt100_n
4722
4723parse_cli_trigger_html :
4724  parse_cli_trigger_print $*
4725
4726#
4727# Implements 'image' mode for command 'parse_cli'.
4728#
4729parse_cli_images :
4730  v 0 use_vt100 e[] ""
4731  old_category,category= n_category,n_example=0
4732  if !isfile('image.jpg') l[] sp bottles,640 onfail testimage2d 500 endl o. image.jpg rm. fi
4733  n_global=0
4734  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4735    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4736
4737    if ['$category']!=['$old_category']
4738      n_category+=1
4739      e[] $_vt100_r"\n ** Section \#"$n_category": "$category"."$_vt100_n"\n"
4740    fi
4741    s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'}
4742    n_example=0
4743
4744    repeat $! l[$>]
4745      if "i==_':' && (
4746            for (p = 1, p<h && i[p]<=_' ', ++p);
4747            i[p]==_'$' && i[p+1]!=_'$')"
4748
4749        s +,{'$'} rm[0,1] a y autocrop {'" "'} example={/{t}}
4750        n_example+=1
4751        n_global+=1
4752        if $n_example>1
4753          basename=${name}_$n_example.jpg
4754        else
4755          basename=${name}.jpg
4756        fi
4757        e[] $_vt100_c[$n_global]$_vt100_n" Command '"$_vt100_g$name$_vt100_n"': $ "$example
4758        skip_rendering={"['"$name"'][0]==_'_' || "\
4759                        "(isfile(['"{/${_parse_cli_images_path}f_$basename}"']) &&"\
4760                        " isfile(['"{/${_parse_cli_images_path}t_$basename}"'])) || "\
4761                        "(isfile(['"{/${_parse_cli_images_path}f0_$basename}"']) && "\
4762                        "isfile(['"{/${_parse_cli_images_path}t0_$basename}"']))"}
4763
4764        if !$skip_rendering # Generate picture only if the file doesn't yet exist
4765          m "run_example : "$example
4766          l[]
4767            reset etime=$|
4768            run_example
4769            etime="done in "$_vt100_n{_round($|-$etime,0.01)}"s"$_vt100_m
4770            _parse_cli_images
4771            if $!
4772              if $!>1 repeat $! o[$>] ${_parse_cli_images_path}f$>_$basename,85 done
4773              else o ${_parse_cli_images_path}f_$basename,85 fi
4774              rr2d 480,320,0,2
4775              if $!>1 repeat $! o[$>] ${_parse_cli_images_path}t$>_$basename,75 done
4776              else o ${_parse_cli_images_path}t_$basename,75 fi
4777              rm
4778            fi
4779          onfail
4780            rm gui_error_preview "Unable to generate preview image"
4781            o ${_parse_cli_images_path}f_$basename,85
4782            rr2d 480,320,0,2
4783            o ${_parse_cli_images_path}t_$basename,75
4784            rm
4785            etime="failed"
4786          endl
4787          um run_example
4788        else etime="skipped"
4789        fi
4790        e[] "\r"$_vt100_c[$n_global]$_vt100_n" Command '"$_vt100_g$name$_vt100_n"': $ "\
4791            $example" "${_vt100_m}"("$etime")."$_vt100_n
4792      fi
4793    endl done
4794    old_category=$category
4795    rm 0
4796  endl done rm
4797
4798# Generate a single image from a list of images, for the reference documentation.
4799_parse_cli_images :
4800  if !$! rm return fi
4801  W,H={[640,480]}
4802
4803  repeat $! l[$>] nm={n}
4804    label=[$>]:" "'$nm'
4805    if ${-is_3d} # 3D vector object
4806      label2="("{i[6]}" vert., "{i[7]}" prim.)"
4807      r3d 1,1,0,-80 r3d 0,1,0,80 snapshot3d {max($W,$H)}
4808    else # Regular 1D,2D or 3D image
4809      label2="("{w}x{h}x{d}x{s}")"
4810      r 100%,100%,100%,3,{s==1?1:0}
4811      if d>1 # Volumetric image -> Render as a 2D image
4812        +slices 50%
4813        +z[0] 50%,0,0,50%,100%,100% permute. zyxc
4814        +z[0] 0,50%,0,100%,50%,100% permute. xzyc
4815        rr2d... $W,$H,0,2
4816        r2dy.. {-3,h},1
4817        r2dx. {-3,w},1
4818        s={min(w#-2,h)}
4819        if $s>64 projections3d[0] 50%,50%,50%,1 mv[0] $! r3d. 1,1,0,-80 r3d. 0,1,0,80 snapshot3d. $s
4820        else rm[0] 0
4821        fi
4822
4823        eW,eH={[w#0+w#1,h#0+h#2]}
4824        if $eW>$W" || "$eH>$H fact={min($W/$eW,$H/$eH)*100} r $fact%,$fact%,1,100%,2 fi
4825        n 0,255
4826
4827        fs={0,max(w,h)*7%}
4828        if {0,h>1.5*$fs} to[0] XY,4,2,$fs fi
4829        if {1,h>1.5*$fs} to[1] XZ,4,2,$fs fi
4830        if {2,h>1.5*$fs} to[2] YZ,4,2,$fs fi
4831        if w to[3] 3D,4,2,$fs frame[3] 1,1,200 fi
4832        frame[0-2] 1,1,200
4833        a[0,1] x a[1,2] x a y
4834      fi
4835    fi
4836
4837    if w>5*h r $W,{$H/3},1,100%,1
4838    elif h>5*w r {$W/3},$H,1,100%,1
4839    else rr2d $W,$H,0,{w<$W&&h<$H?1:2}
4840    fi
4841    n 0,255
4842
4843    r {[w,h]+2},1,100%,0,0,0.5,0.5
4844    - 245 r {[w+10,h+5]},1,100%,0,0,0.5,0.5 r 100%,{h+1},1,100%,0,0,0,1 + 245
4845
4846    0 t. $label" "$label2,5,0,32,1,1
4847    if w>w#0" || "h>h#0
4848       rm. 0 t. $label\n$label2,5,0,32,1,1
4849       if w>w#0" || "h>h#0 rr2d. {0,[w,h]},0,2 fi
4850    fi
4851    *. -1 n. 0,255 to_rgb.
4852    - 245 a y,0.5 + 245
4853
4854  endl done
4855  c 0,255
4856
4857parse_cli_trigger_images :
4858  parse_cli_trigger_print $*
4859
4860#
4861# Implements 'list' mode for command 'parse_cli'.
4862# (return a list of all commands, separated by commas).
4863#
4864parse_cli_list :
4865  res=
4866  repeat $! ('{$>,n}') l. s -,{'@'} res.=$c{0,t} c=, rm endl done
4867  rm u $res
4868
4869#
4870# Implements 'print' mode for command 'parse_cli'.
4871#
4872parse_cli_print :
4873  v 0 e[] ""
4874  sort_list +,n
4875  repeat $! l[$>]
4876    ('{n}') l. s -,{'@'} name={0,t} rm endl
4877    +e[] $name
4878  endl done rm
4879
4880parse_cli_trigger_print :
4881  if "['$1']!='*'"
4882    1,$!,1,1,"
4883      begin(ref(lowercase(['$1']),str));
4884      ref(lowercase(name(#y)),nm);
4885      arobace = find(nm,_'@');
4886      inrange(find(nm,str),0,arobace - 1)?y:-1"
4887    discard. -1 k[{^}]
4888  fi
4889
4890#@cli parse_gmd
4891#@cli : Parse and tokenize selected images, viewed as text strings formatted with the G'MIC markdown syntax.
4892parse_gmd :
4893  e[^-1] "Parse and tokenize images$? viewed as text strings formatted with the G\47MIC markdown syntax."
4894  y 1 a[^-1] .,y rm.
4895  eval[^] ">"${-_gmd_tokens}"
4896    begin(
4897      section = subsection = subsubsection = subsubsubsection = anchor =
4898      bullet = subbullet = subsubbullet = center = right = table = blockquote = detail_block = code_block =
4899      shell = bold_italic_a = bold_italic_u = bold_a = bold_u = italic_a = italic_u = strikethrough = underline =
4900      monospace = value_set = word_highlight = url = page_link = text_link1 = text_link2 = img1 = img2 = pipeline =
4901      formula = opening_offset = -1;
4902
4903      blank(c) = isin(c,_' ',_'\n',s_whitespace,s_tab,0);
4904      semiblank(c) = isin(c,_' ',_'\n',_'.',_',',_';',_':',_'!',_'?',
4905                            _')',_'(',_'[',_']',_'|',_'-',s_whitespace,s_tab,0);
4906      newline(c) = isin(c,_'\n',0);
4907      reset(c) = (#c = -1);
4908      res = crop();
4909    );
4910
4911    pc = j[-1]; c = i; nc = j[1]; ac = j[2];
4912    ym1 = y - 1; y1 = y + 1; y2 = y + 2;
4913    linestart = newline(pc);
4914
4915    is_raw = max(blockquote,code_block,shell,monospace,url,page_link,text_link2,img2,pipeline,formula)>=0;
4916    non_escaped = is_raw || (pc!=_'\\');
4917
4918    # Manage document layout.
4919    #------------------------
4920
4921    # Escaped character.
4922    !non_escaped &&
4923    isin(c,_'%',_'$',_'\\',_'\'',_'`',_'*',_'_',_'{',_'}',_'[',_']',_'<',_'>',_'(',_')',_'#',_'+',_'-',_'.',_'!')?(
4924      res[ym1] = i[ym1] = 0;
4925    ):
4926
4927    # Section: '# Section name'.
4928    linestart && c==_'#' && nc==_' '?( # Opening
4929      section = y;
4930    ):
4931    section>=0 && newline(c)?( # Closing
4932      opening_offset = section;
4933      res[section++] = s_section;
4934      while (blank(res[section]), res[section++] = 0); # Remove leading whitespaces
4935      res[y] = e_section;
4936      for (p = opening_offset - 1, p>=0 && res[p]==_'\n', res[p--] = 0); # Remove newlines preceding
4937      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
4938      for (p = y1, p<size(res) && blank(res[p]), res[p++] = 0); # Remove newlines following
4939    ):
4940
4941    # Subsection: '## Subsection name'.
4942    linestart && c==_'#' && nc==c && ac==_' '?( # Opening
4943      subsection = y;
4944    ):
4945    subsection>=0 && newline(c)?( # Closing
4946      opening_offset = subsection;
4947      res[subsection++] = s_subsection; res[subsection++] = 0;
4948      while (blank(res[subsection]), res[subsection++] = 0); # Remove leading whitespaces
4949      res[y] = e_subsection;
4950      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
4951      for (p = y1, p<size(res) && res[p] && blank(res[p]), res[p] = 0); # Remove newlines following
4952    ):
4953
4954    # Subsubsection: '### Subsubsection name'.
4955    linestart && c==_'#' && nc==c && ac==c && j[3]==_' '?( # Opening
4956      subsubsection = y;
4957    ):
4958    subsubsection>=0 && newline(c)?( # Closing
4959      opening_offset = subsubsection;
4960      res[subsubsection++] = s_subsubsection; copy(res[subsubsection],0,2,1,0); subsubsection+=2;
4961      while (blank(res[subsubsection]), res[subsubsection++] = 0); # Remove leading whitespaces
4962      res[y] = e_subsubsection;
4963      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
4964      for (p = y1, p<size(res) && res[p] && blank(res[p]), res[p] = 0); # Remove newlines following
4965    ):
4966
4967    # Subsubsubsection: '#### Subsubsubsection name'.
4968    linestart && c==_'#' && nc==c && ac==c && j[3]==c && j[4]==_' '?( # Opening
4969      subsubsubsection = y;
4970    ):
4971    subsubsubsection>=0 && newline(c)?( # Closing
4972      opening_offset = subsubsubsection;
4973      res[subsubsubsection++] = s_subsubsubsection; copy(res[subsubsubsection],0,3,1,0); subsubsubsection+=3;
4974      while (blank(res[subsubsubsection]), res[subsubsubsection++] = 0); # Remove leading whitespaces
4975      res[y] = e_subsubsubsection;
4976      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
4977      for (p = y1, p<size(res) && res[p] && blank(res[p]), res[p] = 0); # Remove blanks following
4978    ):
4979
4980    # Anchor: '= Anchor name'.
4981    linestart && c==_'=' && nc==_' '?( # Opening
4982      anchor = y;
4983    ):
4984    anchor>=0 && newline(c)?( # Closing
4985      opening_offset = anchor;
4986      res[anchor++] = s_anchor;
4987      while (blank(res[anchor]), res[anchor++] = 0); # Remove leading whitespaces
4988      res[y] = e_anchor;
4989      res[y1]==_'\n'?(res[y1] = 0);
4990      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
4991    ):
4992
4993    # List bullet: '* List item'.
4994    linestart && isin(c,_'-',_'*',_'+') && nc==_' ' && bullet<0?( # Opening
4995      bullet = y;
4996    ):
4997    bullet>=0 && newline(c)?( # Closing
4998      opening_offset = bullet;
4999      res[bullet++] = s_bullet; res[bullet] = 0;
5000      res[y] = e_bullet;
5001    ):
5002
5003    # Sublist bullet: '  * Sub-list item'.
5004    linestart && c==_' ' && nc==_' ' && isin(ac,_'-',_'*',_'+') && j[3]==_' ' && subbullet<0?( # Opening
5005      subbullet = y;
5006    ):
5007    subbullet>=0 && newline(c)?( # Closing
5008      opening_offset = subbullet;
5009      res[subbullet++] = s_subbullet; copy(res[subbullet],0,2,1,0);
5010      res[y] = e_subbullet;
5011    ):
5012
5013    # Subsublist bullet: '    * Subsub-list item'.
5014    linestart && c==_' ' && nc==_' ' && ac==_' ' && j[3]==_' ' && isin(j[4],_'-',_'*',_'+') && j[5]==_' ' &&
5015      subsubbullet<0?( # Opening
5016      subsubbullet = y;
5017    ):
5018    subsubbullet>=0 && newline(c)?( # Closing
5019      opening_offset = subsubbullet;
5020      res[subsubbullet++] = s_subsubbullet; copy(res[subsubbullet],0,4,1,0);
5021      res[y] = e_subsubbullet;
5022    ):
5023
5024    # Centered block: '\n===\n Centered text\n===\n'.
5025    linestart && c==_'=' && nc==c && ac==c && newline(j[3])?(
5026      center<0?( # Opening
5027        center = y;
5028      ):
5029      center>=0 && y>center + 3?( # Closing
5030        opening_offset = center;
5031        res[center++] = s_center; copy(res[center],0,3,1,0); center+=3;
5032        newline(res[ym1])?(res[ym1] = 0);
5033        res[y] = e_center; copy(res[y + 1],0,3,1,0);
5034      );
5035    ):
5036
5037    # Right block: '\n>>>\n Right-aligned text\n>>>\n'.
5038    linestart && c==_'>' && nc==c && ac==c && newline(j[3])?(
5039      right<0?( # Opening
5040        right = y;
5041      ):
5042      right>=0 && y>right + 3?( # Closing
5043        opening_offset = right;
5044        res[right++] = s_right; copy(res[right],0,3,1,0); right+=3;
5045        newline(res[ym1])?(res[ym1] = 0);
5046        res[y] = e_right; copy(res[y + 1],0,3,1,0);
5047      );
5048    ):
5049
5050    # Table : '\n| foo | bar\n' (with first delimiter '||', '|+' or '|-').
5051    table<0 && linestart && c==_'|'?( # Opening
5052      table = y;
5053    ):
5054    table>=0 && newline(c) && nc!=_'|'?( # Closing
5055      opening_offset = table;
5056      d = i[table + 1];
5057      d==_'|'?(res[table++] = s_htable; res[table++] = 0):
5058      d==_'-'?(res[table++] = s_vtable; res[table++] = 0):
5059      d==_'+'?(res[table++] = s_hvtable; res[table++] = 0):(res[table++] = s_table);
5060
5061      i[table]==_' '?(res[table++] = 0);
5062      newline(i[table]) && i[table + 1]==_'|'?(copy(res[table],0,2,1,0); table+=2);
5063      for (p = table, p<y, ++p, # Find column / line separators inside table
5064        i[p]==_'|'?(
5065          res[p] = newline(i[p - 1]) || newline(i[p + 1])?0:m_table;
5066          i[p - 1]==_' '?(res[p - 1] = 0);
5067          i[p + 1]==_' '?(res[p + 1] = 0);
5068        ):newline(i[p])?(
5069          res[p] = n_table;
5070        );
5071      );
5072      res[y] = e_table;
5073    ):
5074
5075    # Blockquote : '\n> line1\n> line2'.
5076    code_block<0 && blockquote<0 && linestart && c==_'>' && blank(nc)?( # Opening
5077      blockquote = y;
5078    ):
5079    blockquote>=0 && newline(c) && (nc!=_'>' || !blank(ac))?( # Closing
5080      opening_offset = blockquote;
5081      res[blockquote++] = s_blockquote; res[blockquote] = 0;
5082      # Remove leading '>' when multi-lines:
5083      for (p = blockquote, p<y, ++p, newline(res[p - 1]) && res[p]==_'>'?copy(res[p],0,res[p + 1]==_' '?2:1,1,0));
5084      res[y] = e_blockquote;
5085    ):
5086
5087    # Detail block: '\n??? Details:\nDetail block\n???\n'.
5088    linestart && c==_'?' && nc==c && ac==c && blank(j[3])?(
5089      detail_block<0?( # Opening
5090        detail_block = y;
5091      ):
5092      detail_block>=0 && y>detail_block + 3 && newline(j[3])?( # Closing
5093        opening_offset = detail_block;
5094        res[detail_block++] = s_detail_block; copy(res[detail_block],0,2,1,0);
5095        res[ym1] = e_detail_block; copy(res[y],0,4,1,0);
5096      );
5097    ):
5098
5099    # Code block: '\n~~~\n Code block\n~~~\n'.
5100    linestart && isin(c,_'~','`') && nc==c && ac==c && newline(j[3])?(
5101      code_block<0?( # Opening
5102        code_block = y;
5103      ):
5104      code_block>=0 && y>code_block + 3?( # Closing
5105        opening_offset = code_block;
5106        res[code_block++] = s_code_block; copy(res[code_block],0,3,1,0); code_block+=3;
5107        copy(res[code_block],i[code_block],y - code_block); # Cancel tokens found inside
5108        for (p = code_block, p<y, ++p, res[p]==_' '?(res[p] = s_whitespace)); # Keep leading whitespaces.
5109        res[ym1] = e_code_block; copy(res[y],0,4,1,0);
5110      );
5111    ):
5112
5113    # Shell command: '\n"""\n Shell command\n"""\n'.
5114    linestart && c==_'\"' && nc==c && ac==c && newline(j[3])?(
5115      shell<0?( # Opening
5116        shell = y;
5117      ):
5118      shell>=0 && y>shell + 3?( # Closing
5119        opening_offset = shell;
5120        res[shell++] = s_shell; copy(res[shell],0,3,1,0); shell+=3;
5121        copy(res[shell],i[shell],y - shell); # Cancel tokens found inside
5122        res[ym1] = e_shell; copy(res[y],0,4,1,0);
5123      );
5124    ):
5125
5126    # Horizontal rule: '---', '___' or '***'.
5127    linestart && isin(c,_'-','_','*') && nc==c && ac==c && newline(j[3])?(
5128      res[y] = s_hrule;
5129      copy(res[y1],0,3,1,0);
5130    ):
5131
5132    # Manage text style.
5133    #-------------------
5134
5135    # Bold italic (asterisk): '***bold italic text***'.
5136    non_escaped && c==_'*' && nc==c && ac==c?( # Opening
5137      bold_italic_a<0?(
5138        bold_italic_a = y;
5139      ):
5140      bold_italic_a>=0 && y>bold_italic_a + 3 && j[3]!=_'*'?( # Closing
5141        opening_offset = bold_italic_a;
5142        res[bold_italic_a++] = s_bold_italic_a; res[bold_italic_a++] = 0; res[bold_italic_a] = 0;
5143        res[y] = e_bold_italic_a; res[y1] = res[y2] = 0;
5144      );
5145    ):
5146
5147    # Bold italic (underscore): '___bold italic text___'.
5148    non_escaped && c==_'_' && nc==c && ac==c?(
5149      bold_italic_u<0 && semiblank(pc)?( # Opening
5150        bold_italic_u = y;
5151      ):
5152      bold_italic_u>=0 && y>bold_italic_u + 3 && semiblank(j[3])?( # Closing
5153        opening_offset = bold_italic_u;
5154        res[bold_italic_u++] = s_bold_italic_u; res[bold_italic_u++] = 0; res[bold_italic_u] = 0;
5155        res[y] = e_bold_italic_u; res[y1] = res[y2] = 0;
5156      );
5157    ):
5158
5159    # Bold (asterisk): '**bold text**'.
5160    non_escaped && c==_'*' && nc==c?( # Opening
5161      bold_a<0?(
5162        bold_a = y;
5163      ):
5164      bold_a>=0 && y>bold_a + 2 && ac!=_'*'?( # Closing
5165        opening_offset = bold_a;
5166        res[bold_a++] = s_bold_a; res[bold_a] = 0;
5167        res[y] = e_bold_a; res[y1] = 0;
5168      );
5169    ):
5170
5171    # Bold (underscore): '__bold text__'.
5172    non_escaped && c==_'_' && nc==c?(
5173      bold_u<0 && semiblank(pc)?( # Opening
5174        bold_u = y;
5175      ):
5176      bold_u>=0 && y>bold_u + 2 && semiblank(ac)?( # Closing
5177        opening_offset = bold_u;
5178        res[bold_u++] = s_bold_u; res[bold_u] = 0;
5179        res[y] = e_bold_u; res[y1] = 0;
5180      );
5181    ):
5182
5183    # Italic (asterisk): '*italic text*'.
5184    non_escaped && c==_'*' && pc!=c?( # Opening
5185      italic_a<0?(
5186        italic_a = y;
5187      ):
5188      italic_a>=0 && y>italic_a + 1 && ac!=_'*'?( # Closing
5189        opening_offset = italic_a;
5190        res[italic_a] = s_italic_a;
5191        res[y] = e_italic_a;
5192      );
5193    ):
5194
5195    # Italic (underscore): '_italic text_'.
5196    non_escaped && c==_'_' && pc!=c?(
5197      italic_u<0 && semiblank(pc)?( # Opening
5198        italic_u = y
5199      ):
5200      italic_u>=0 && y>italic_u + 1 && semiblank(nc)?( # Closing
5201        opening_offset = italic_u;
5202        res[italic_u] = s_italic_u;
5203        res[y] = e_italic_u;
5204      );
5205    ):
5206
5207    # Strikethrough: '~~strikethrough text~~'.
5208    non_escaped && c==_'~' && nc==c && pc!=c?( # Opening
5209      strikethrough<0?(
5210        strikethrough = y;
5211      ):
5212      strikethrough>=0 && y>strikethrough + 2?( # Closing
5213        opening_offset = strikethrough;
5214        res[strikethrough++] = s_strikethrough; res[strikethrough] = 0;
5215        res[y] = e_strikethrough; res[y1] = 0;
5216      );
5217    ):
5218
5219    # Underline: '==strikethrough text=='.
5220    non_escaped && c==_'=' && nc==c && pc!=c?( # Opening
5221      underline<0?(
5222        underline = y;
5223      ):
5224      underline>=0 && y>underline + 2?( # Closing
5225        opening_offset = underline;
5226        res[underline++] = s_underline; res[underline] = 0;
5227        res[y] = e_underline; res[y1] = 0;
5228      );
5229    ):
5230
5231    # Monospace: '`monospace text`'.
5232    non_escaped && c==_'`'?(
5233      monospace<0?( # Opening
5234        monospace = y;
5235      ):
5236      monospace>=0 && y>monospace + 1?( # Closing
5237        opening_offset = monospace;
5238        res[monospace++] = s_monospace;
5239        copy(res[monospace],i[monospace],y - monospace); # Cancel tokens found inside
5240        for (p = monospace, p<y, ++p, res[p]==_' '?(res[p] = s_whitespace)); # Keep leading whitespaces.
5241        res[y] = e_monospace;
5242      );
5243    ):
5244
5245    # Value Set: '{ value1 | value2 | value3 }'.
5246    non_escaped && c==_'{' && value_set<0 && semiblank(pc)?( # Opening
5247      value_set = y;
5248    ):
5249    non_escaped && c==_'}' && value_set>=0 && y>value_set + 1 && semiblank(nc)?( # Closing
5250      opening_offset = value_set;
5251      res[value_set++] = s_value_set;
5252      copy(res[value_set],i[value_set],y - value_set); # Cancel tokens found inside
5253      res[y] = e_value_set;
5254    ):
5255
5256    # Word highlight: ' 'word_highlight' '.
5257    non_escaped && c=='\'' && nc!=c?(
5258      word_highlight<0 && semiblank(pc)?( # Opening
5259        word_highlight = y;
5260      ):
5261      word_highlight>=0 && y>word_highlight + 1 && semiblank(nc)?( # Closing
5262        opening_offset = word_highlight;
5263        res[word_highlight++] = s_word_highlight;
5264        copy(res[word_highlight],i[word_highlight],y - word_highlight); # Cancel tokens found inside
5265        res[y] = e_word_highlight;
5266      );
5267    ):
5268
5269    # URL: '<https://url.com>'.
5270    non_escaped && c==_'<' && url<0 &&
5271     (crop(0,y1,1,8)=='https://' || crop(0,y1,1,7)=='http://' || crop(0,y1,1,6)=='ftp://')?( # Opening
5272      url = y;
5273    ):
5274    non_escaped && c==_'>' && url>=0 && y>url + 1?( # Closing
5275      opening_offset = url;
5276      res[url++] = s_url;
5277      copy(res[url],i[url],y - url); # Cancel tokens found inside
5278      res[y] = e_url;
5279    ):
5280
5281    # Page link (command or reference): ' ''command or page name'' ' (not a URL!).
5282    non_escaped && c==_'\'' && nc==c?(
5283      page_link<0 && semiblank(pc)?( # Opening
5284        page_link = y;
5285      ):
5286      page_link>=0 && y>page_link + 2 && semiblank(ac)?( # Closing
5287        opening_offset = page_link;
5288        res[page_link++] = s_page_link; res[page_link++] = 0;
5289        copy(res[page_link],i[page_link],y - page_link); # Cancel tokens found inside
5290        res[y] = e_page_link; res[y1] = 0;
5291      );
5292    ):
5293
5294    # Text link: '[link text](https://url.com)'.
5295    non_escaped && c==_'[' && text_link1<0 && text_link2<0 && img1<0?( # Opening
5296      text_link1 = y;
5297    ):
5298    non_escaped && c==_']' && nc==_'(' && text_link1>=0 && text_link2<0 && img1<0?( # Middle
5299      text_link2 = y;
5300    ):
5301    non_escaped && c==_')' && text_link2>=0 && img1<0?( # Closing
5302      opening_offset = text_link1;
5303      res[text_link1] = s_text_link;
5304      res[text_link2++] = m_text_link; res[text_link2++] = 0;
5305      copy(res[text_link2],i[text_link2],y - text_link2); # Cancel tokens found inside
5306      res[y] = e_text_link;
5307    ):
5308
5309    # Image: '![alt caption](https://url.com/image.png)'.
5310    non_escaped && c==_'!' && nc==_'[' && img1<0 && img2<0?( # Opening
5311      img1 = y;
5312    ):
5313    non_escaped && c==_']' && nc==_'(' && img1>=0 && img2<0?( # Middle
5314      img2 = y;
5315    ):
5316    non_escaped && c==_')' && img2>=0?( # Closing
5317      opening_offset = img1;
5318      res[img1++] = s_img; res[img1++] = 0;
5319      res[img2++] = m_img; res[img2++] = 0;
5320      copy(res[img1],i[img1],img2 - img1 - 2); copy(res[img2],i[img2],y - img2); # Cancel tokens found inside
5321      res[y] = e_img;
5322    ):
5323
5324    # G'MIC pipeline (image): '%% G'MIC pipeline %%'.
5325    non_escaped && c==_'%' && nc==c?(
5326      pipeline<0 && semiblank(pc)?( # Opening
5327        pipeline = y;
5328      ):
5329      pipeline>=0 && y>pipeline + 2 && semiblank(ac)?( # Closing
5330        opening_offset = pipeline;
5331        res[pipeline++] = s_pipeline; res[pipeline++] = 0;
5332        copy(res[pipeline],i[pipeline],y - pipeline); # Cancel tokens found inside
5333        while (blank(i[pipeline]), res[pipeline++] = 0); # Remove leading blanks
5334        for (p = ym1, blank(res[p]), res[p--] = 0); # Remove trailing blanks
5335        res[y] = e_pipeline; res[y1] = 0;
5336      );
5337    ):
5338
5339    # Formula in LaTeX: '$$ x = cos(x + 2) $$`'.
5340    non_escaped && c==_'$' && nc==c?(
5341      formula<0 && semiblank(pc)?( # Opening
5342        formula = y;
5343      ):
5344      formula>=0 && y>formula + 2 && semiblank(ac)?( # Closing
5345        opening_offset = formula;
5346        res[formula++] = s_formula; res[formula++] = 0;
5347        copy(res[formula],i[formula],y - formula); # Cancel tokens found inside
5348        while (blank(i[formula]), res[formula++] = 0); # Remove leading blanks
5349        for (p = ym1, blank(res[p]), res[p--] = 0); # Remove trailing blanks
5350        res[y] = e_formula; res[y1] = 0;
5351      );
5352    ):
5353
5354    # G'MIC word: '\G'MIC'.
5355    semiblank(pc) && c==_'\\' && nc==_'G' && ac==_'\'' && j[3]==_'M' && j[4]==_'I' && j[5]==_'C' && semiblank(j[6])?(
5356      res[y] = s_gmic;
5357      copy(res[y1],0,4,1,0);
5358      res[y + 5] = e_gmic;
5359    ):
5360
5361    # Whitespaces.
5362    c==_' '?(
5363
5364      # Keep consecutive whitespaces.
5365      c==_' ' && (linestart || pc==c)?(
5366        res[y] = s_whitespace;
5367      );
5368
5369      # Reset tokens that cannot contain whitespace.
5370      reset(word_highlight);
5371      reset(url);
5372      text_link2>=0?(reset(text_link1); reset(text_link2));
5373      img2>=0?(reset(img1); reset(img2));
5374    ):
5375
5376    # Escaped newline.
5377    c==_'\\' && nc==_'n'?(
5378      res[y] = _'\n'; res[y + 1] = 0;
5379    ):
5380
5381    # Tab.
5382    c==_'\t'?(
5383      res[y] = s_tab;
5384    ):
5385
5386    # Newline.
5387    c==_'\n'?(
5388
5389      # Reset tokens that cannot contain newlines.
5390      reset(bold_italic_a);
5391      reset(bold_italic_u);
5392      reset(bold_u);
5393      reset(bold_a);
5394      reset(italic_a);
5395      reset(italic_u);
5396      reset(strikethrough);
5397      reset(underline);
5398      reset(monospace);
5399      reset(word_highlight);
5400      reset(url);
5401      reset(page_link);
5402      text_link2>=0?(reset(text_link1); reset(text_link2));
5403      img2>=0?(reset(img1); reset(img2));
5404    );
5405
5406    # Cancel opened tokens starting after the opening offset of the latest closed token.
5407    #------------------------------------------------------------------------------------
5408    opening_offset>=0?(
5409      section>=opening_offset ? reset(section);
5410      subsection>=opening_offset ? reset(subsection);
5411      subsubsection>=opening_offset ? reset(subsubsection);
5412      subsubsubsection>=opening_offset ? reset(subsubsubsection);
5413      anchor>=opening_offset ? reset(anchor);
5414      bullet>=opening_offset ? reset(bullet);
5415      subbullet>=opening_offset ? reset(subbullet);
5416      subsubbullet>=opening_offset ? reset(subsubbullet);
5417      center>=opening_offset ? reset(center);
5418      right>=opening_offset ? reset(right);
5419      table>=opening_offset ? reset(table);
5420      blockquote>=opening_offset ? reset(blockquote);
5421      detail_block>=opening_offset ? reset(detail_block);
5422      code_block>=opening_offset? reset(code_block);
5423      shell>=opening_offset? reset(shell);
5424      bold_italic_a>=opening_offset ? reset(bold_italic_a);
5425      bold_italic_u>=opening_offset ? reset(bold_italic_u);
5426      bold_a>=opening_offset ? reset(bold_a);
5427      bold_u>=opening_offset ? reset(bold_u);
5428      italic_a>=opening_offset ? reset(italic_a);
5429      italic_u>=opening_offset ? reset(italic_u);
5430      strikethrough>=opening_offset ? reset(strikethrough);
5431      underline>=opening_offset ? reset(underline);
5432      monospace>=opening_offset ? reset(monospace);
5433      value_set>=opening_offset ? reset(value_set);
5434      word_highlight>=opening_offset ? reset(word_highlight);
5435      url>=opening_offset ? reset(url);
5436      page_link>=opening_offset ? reset(page_link);
5437      text_link1>=opening_offset || text_link2>=opening_offset ? (reset(text_link1); reset(text_link2));
5438      img1>=opening_offset || img2>=opening_offset ? (reset(img1); reset(img2));
5439      pipeline>=opening_offset ? reset(pipeline);
5440      formula>=opening_offset ? reset(formula);
5441      opening_offset = -1;
5442    );
5443
5444    end(copy(i[0],res))"
5445  discard 0
5446
5447#@cli gmd2html : _include_default_header_footer={ 0=none | 1=Reference | 2=Tutorial | 3=News } : (no arg)
5448#@cli : Convert selected gmd-formatted text images to html format.
5449#@cli : Default values: 'include_default_header_footer=1'.
5450gmd2html : skip "${1=}"
5451  l[] is_arg={isint("$1")} onfail is_arg=0 endl
5452  if $is_arg embed_html=$1 else embed_html=1 noarg fi
5453
5454  parse_gmd
5455  s_section,e_section={${-_gmd_tokens}"[s_section,e_section]"}
5456  repeat $! l[$>] nm$>={b} strvar ${nm$>} fnm=${}
5457    if "i=="$s_section" && find(#-1,"$e_section")>0"
5458      +rows 1,{"find(#-1,"$e_section")-1"} title$>={t} rm.
5459    else title$>=
5460    fi
5461    . # [0] = Output, [1] = Input
5462    eval. ">
5463      begin("${-_gmd_tokens}${-_gmd_write}");
5464      c = i;
5465      c>0?(
5466        c==_'\n' ? write('<br/>\n'):
5467        c==_'&' ? write('&amp;'):
5468        c==_'\47' ? write('&apos;'):
5469        c==_'>' ? write('&gt;'):
5470        c==_'\"' ? write('&quot;'):
5471        c==_'<' ? write('&lt;'):
5472        write(c);
5473      ):(
5474        isin(c,e_bold_a,e_bold_u,e_bold_italic_a,e_bold_italic_u,e_italic_a,e_italic_u,
5475             e_monospace,e_strikethrough,e_underline,e_word_highlight) ? write('</span>'):
5476        isin(c,e_bullet,e_subbullet,e_subsubbullet) ? write('</div>\n'):
5477        isin(c,e_section,e_subsection,e_subsubsection,e_subsubsubsection) ? write('</div>\n'):
5478
5479        c==s_section ? (
5480          ind_e = find(#1,e_section,y);
5481          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5482          ref(get('_gmd_name',1024,1),str_nam);
5483          write_nl();
5484          write('<a name=\"');
5485          len = find(str_nam,0);
5486          write(str_nam,len);
5487          write('\"></a><div class=\"gmd_section\">');
5488        ):
5489        c==s_subsection ? (
5490          ind_e = find(#1,e_subsection,y);
5491          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5492          ref(get('_gmd_name',1024,1),str_nam);
5493          write_nl();
5494          write('<a name=\"');
5495          len = find(str_nam,0);
5496          write(str_nam,len);
5497          write('\"></a><div class=\"gmd_subsection\">');
5498        ):
5499        c==s_subsubsection ? (
5500          ind_e = find(#1,e_subsubsection,y);
5501          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5502          ref(get('_gmd_name',1024,1),str_nam);
5503          write_nl();
5504          write('<a name=\"');
5505          len = find(str_nam,0);
5506          write(str_nam,len);
5507          write('\"></a><div class=\"gmd_subsubsection\">');
5508        ):
5509        c==s_subsubsubsection ? (
5510          ind_e = find(#1,e_subsubsubsection,y);
5511          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5512          ref(get('_gmd_name',1024,1),str_nam);
5513          write_nl();
5514          write('<a name=\"');
5515          len = find(str_nam,0);
5516          write(str_nam,len);
5517          write('\"></a><div class=\"gmd_subsubsubsection\">');
5518        ):
5519        c==s_anchor ? (
5520          ind_e = find(#1,e_anchor,y);
5521          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5522          ref(get('_gmd_name',1024,1),str_nam);
5523          write('<a name=\"');
5524          len = find(str_nam,0);
5525          write(str_nam,len);
5526          write('\"></a>\n');
5527          copy(i[y],0,ind_e - y + 1,1,0);
5528        ):
5529        c==s_bullet ? write('<div class=\"gmd_bullet\">'):
5530        c==s_subbullet ? write('<div class=\"gmd_subbullet\">'):
5531        c==s_subsubbullet ? write('<div class=\"gmd_subsubbullet\">'):
5532        c==s_center ? write('<div class=\"gmd_center\">\n'):
5533        c==e_center ? (write_nl(); write('</div>')):
5534        c==s_right ? write('<div class=\"gmd_right\">\n'):
5535        c==e_right ? (write_nl(); write('</div>')):
5536        c==s_table ? (write_nl(); write('<table class=\"gmd_table\">\n<tr><td>')):
5537        c==s_htable ? (write_nl(); write('<table class=\"gmd_htable\">\n<tr><td>')):
5538        c==s_vtable ? (write_nl(); write('<table class=\"gmd_vtable\">\n<tr><td>')):
5539        c==s_hvtable ? (write_nl(); write('<table class=\"gmd_hvtable\">\n<tr><td>')):
5540        c==m_table ? write('</td><td>'):
5541        c==n_table ? write('</td></tr>\n<tr><td>'):
5542        c==e_table ? write('</td></tr>\n</table>\n'):
5543        c==s_blockquote ? (write_nl(); write('<blockquote class=\"gmd_blockquote\">\n')):
5544        c==e_blockquote ? (write('\n</blockquote>\n')):
5545        c==s_detail_block ? (
5546          write_nl();
5547          write('<details class=\"gmd_details\">\n<summary>');
5548          ny = y + 1;
5549          nc = j[1];
5550          nc!=e_detail_block?(
5551            j[1] = 0;
5552            ind_e = find(#1,e_detail_block,ny);
5553            nc==_' '?( # Has a title
5554              ind_nl = find(#1,_'\n',ny);
5555              ind_nl<0 || ind_nl>ind_e?(ind_nl = ind_e);
5556              write(#1,y + 2,ind_nl - y - 2);
5557              copy(i[ny],0,ind_nl - y - 1,1,0);
5558              i[ind_nl]!=e_detail_block?(i[ind_nl] = 0);
5559            ):write('Details:');
5560            write('</summary>\n');
5561          )
5562        ):
5563        c==e_detail_block ? (write_nl(); write('</details>\n')):
5564        c==s_code_block ? write('<div class=\"gmd_code_block\">'):
5565        c==e_code_block ? write('</div>\n');
5566
5567        c==s_shell ? (
5568          ind_e = find(#1,e_shell,y);
5569          run('_gmd2html_shell. ',y + 1,',',ind_e -  1);
5570          ref(get('_gmd_command',1024,1),str_com);
5571          write('<div class=\"gmd_code_block\">$ ');
5572          len = find(str_com,0);
5573          write(str_com,len);
5574          write('<br/><br/>\n');
5575          write(#-1,0,h(#-1));
5576          run('rm.');
5577          write('</div>\n');
5578          copy(i[y],0,ind_e - y + 1,1,0);
5579        ):
5580        c==s_hrule ? (write_nl(); write('<hr/>\n')):
5581
5582        c==s_bold_italic_a ? write('<span class=\"gmd_bold_italic_a\">'):
5583        c==s_bold_italic_u ? write('<span class=\"gmd_bold_italic_u\">'):
5584        c==s_bold_a ? write('<span class=\"gmd_bold_a\">'):
5585        c==s_bold_u ? write('<span class=\"gmd_bold_u\">'):
5586        c==s_italic_a ? write('<span class=\"gmd_italic_a\">'):
5587        c==s_italic_u ? write('<span class=\"gmd_italic_u\">'):
5588        c==s_strikethrough? write('<span class=\"gmd_strikethrough\">'):
5589        c==s_underline? write('<span class=\"gmd_underline\">'):
5590        c==s_monospace ? write('<span class=\"gmd_monospace\">'):
5591        c==s_value_set ? write('<span class=\"gmd_value_set\">{'):
5592        c==e_value_set ? write('}</span>'):
5593        c==s_word_highlight ? write('<span class=\"gmd_word_highlight\">'):
5594
5595        c==s_url ? (
5596          ind_e = find(#1,e_url,y);
5597          write('<a href=\"');
5598          write(#1,y + 1,ind_e - y - 1);
5599          write('\" target=\"_blank\">');
5600        ):
5601        c==e_url ? write('</a>'):
5602
5603        c==s_page_link ? (
5604          ind_e = find(#1,e_page_link,y);
5605          run('_gmd2html_page_link. ',y + 1,',',ind_e - 1);
5606          ref(get('_gmd_link',1024,1),str_link);
5607          ref(get('_gmd_text',1024,1),str_text);
5608          write('<span class=\"gmd_page_link\"><a href=\"');
5609          len = find(str_link,0);
5610          write(str_link,len);
5611          write('\">');
5612          len = find(str_text,0);
5613          write(str_text,len);
5614          write('</a></span>');
5615          copy(i[y],0,ind_e - y + 1,1,0);
5616        ):
5617
5618        c==s_text_link ? (
5619          ind_m = find(#1,m_text_link,y);
5620          ind_e = find(#1,e_text_link,ind_m);
5621          write('<a href=\"');
5622          write(#1,ind_m + 1,ind_e - ind_m - 1);
5623          i[ind_m + 1]==_'#' || crop(#1,0,ind_m + 1,1,15)=='https://gmic.eu'?
5624            write('\">'): # Local link
5625            write('\" target=\"_blank\">');
5626        ):
5627        c==m_text_link ? (
5628          ind_e = find(#1,e_text_link,y);
5629          copy(i[y],0,ind_e - y + 1,1,0);
5630          write('</a>');
5631        ):
5632
5633        c==s_img ? (
5634          ind_m = find(#1,m_img,y);
5635          ind_e = find(#1,e_img,ind_m);
5636          ind_d = find(#1,_'.',ind_e - 1,-1);
5637          is_video = 0;
5638          ind_d>ind_m?( # Check for a video
5639            ref(crop(#1,0,ind_d + 1,1,3),ext3);
5640            ref(crop(#1,0,ind_d + 1,1,4),ext4);
5641            is_video = ext3=='mp4' || ext3=='ogg' || ext4=='webm'
5642          );
5643          is_video?(
5644            write('<video controls loop autoplay muted src=\"');
5645            write(#1,ind_m + 1,ind_e - ind_m - 1);
5646            write('\">');
5647          ):(
5648            write('<img class=\"gmd_image\" src=\"');
5649            write(#1,ind_m + 1,ind_e - ind_m - 1);
5650            write('\" alt=\"');
5651            write(#1,y + 1,ind_m - y - 1);
5652            write('\" title=\"');
5653          );
5654        ):
5655        c==m_img ? (
5656          ind_e = find(#1,e_img,y);
5657          copy(i[y],0,ind_e - y + 1,1,0);
5658          is_video?write('</video>'):write('\"/>');
5659        ):
5660
5661        c==s_pipeline ? (
5662          ind_e = find(#1,e_pipeline,y);
5663          run('_gmd2html_pipeline. ',y + 1,',',ind_e -  1)?( # If output images
5664            ref(get('_gmd_command',1024,1),str_com);
5665            ref(get('_gmd_filename',1024,1),str_fil);
5666            write('<img class=\"gmd_image\" src=\"');
5667            len = find(str_fil,0);
5668            write(str_fil,len);
5669            write('\" alt=\"');
5670            len = find(str_com,0);
5671            write(str_com,len);
5672            write('\"/>');
5673          );
5674          copy(i[y],0,ind_e - y + 1,1,0);
5675        ):
5676
5677        c==s_formula ? (
5678          ind_e = find(#1,e_formula,y);
5679          run('_gmd2html_formula. ',y + 1,',',ind_e - 1);
5680          ref(get('_gmd_filename',1024,1),str_fil);
5681          write('<img class=\"gmd_formula\" src=\"');
5682          len = find(str_fil,0);
5683          write(str_fil,len);
5684          write('\" alt=\"');
5685        ):
5686        c==e_formula ? write('\"/>'):
5687
5688        c==s_gmic ? write('<span class=\"gmd_gmic\">G&apos;MIC</span>'):
5689        c==s_whitespace ? write('&nbsp;'):
5690        c==s_tab ? write('&nbsp;&nbsp;&nbsp;&nbsp;');
5691      );
5692      end(resize(#0,1,_write_off,1,1,0))"
5693    k[0] autocrop 0 nm $fnm.html
5694  endl done
5695
5696  # Embed with header and footer if needed.
5697  if $embed_html
5698    section_title=${"arg "$embed_html",Reference,Tutorial,News"}
5699    repeat $! l[$>] nm={b} nm0=${nm$>}
5700      if ['${title$>}']!=0 title=${title$>} else title=$nm0 fi
5701      if isfile('../style.css') style="../style.css" else style="https://gmic.eu/style.css" fi
5702      i[0] ('"<!DOCTYPE html>"\n\
5703"<html lang=\"en\">"\n\
5704"  <head>"\n\
5705"    <meta charset=\"utf-8\">"\n\
5706"    <link rel=\"stylesheet\" href=\""$style"\">"\n\
5707"    <title>"$title"</title>"\n\
5708"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
5709"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
5710"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
5711"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\
5712"  </head>"\n\n\
5713"  <body>"\n\
5714"    <div id=\"include_header\"></div>"\n\n\
5715"    <div class=\"section_title\"><p>"$section_title"</p></div><div class=\"section_content\">"\n\n\
5716"<!-- begin_content -->"\n\n':y)
5717    ('\n\n\
5718"<!-- end_content -->"\n\n\
5719"    </div><div class=\"section_end\"></div>"\n\n\
5720" <div id=\"include_footer\"></div>"\n\
5721"  </body>"':y)
5722      a y nm $nm.html
5723    endl done
5724  fi
5725
5726# Manage sections / subsections / subsubsections / subsubsubsections.
5727_gmd2html_section :
5728  +rows. $1,$2 strvar {t} _gmd_name=${} rm.
5729
5730# Manage page links.
5731_gmd2html_page_link :
5732  +rows. $1,$2
5733  if i==_'-' # Force link to a G'MIC reference page
5734    rows. 1,100% _gmd_text={t} strvar {t} varname=${}
5735    if ['$varname']=='index' varname=_index fi
5736    _gmd_link=https://gmic.eu/reference/$varname.html
5737  else
5738    basename {t} _gmd_text=${} strvar $_gmd_text varname=${}
5739    if ['$varname']=='index' varname=_index fi
5740    _gmd_link=$varname.html
5741  fi
5742  rm.
5743
5744# Manage pipeline execution and result saving.
5745_gmd2html_pipeline :
5746  +rows. $1,$2
5747  _gmd_command={t} rm.
5748  strcasevar $_gmd_command
5749  _gmd_filename=img/${}.png
5750  is_output={fsize(['$_gmd_filename'])}
5751  if !isfile(['$_gmd_filename']) l[]
5752    l[]
5753      run $_gmd_command a x
5754      if !$!" || "($!==1" && "!h) is_output=0 fi
5755    onfail
5756      rm error[] "Command '"$_gmd_command"': "${}
5757    endl
5758    if $!
5759      if !isdir('img') x "mkdir -p img" fi
5760      o. $_gmd_filename rm.
5761    fi
5762  endl fi
5763  ({'$_gmd_command'})
5764  _gmd_ascii2html.
5765  _gmd_command={t} rm.
5766  u $is_output
5767
5768# Convert formula to image (using LaTeX).
5769_gmd2html_formula :
5770  +rows. $1,$2 replace. {'\n'},{'" "'} j.. .,0,$1
5771  formula={/{t}} rm.
5772  strcasevar $formula varname=${}
5773  _gmd_filename=img/$varname.png
5774  if !isfile(['$_gmd_filename']) l[]
5775    ({'"\\documentclass[preview]{standalone}"\n\
5776       "\\usepackage{amsmath,amssymb,amsfonts}"\n\
5777       "\\pagestyle{empty}"\n\
5778       "\\begin{document}"\n\
5779       "\\begin{align*}"\n\
5780       $formula\n\
5781       "\\end{align*}"\n\
5782       "\\end{document}"'})
5783    path_tmp=${-path_tmp}
5784    ot. ${path_tmp}$varname.tex rm.
5785    x "cd "$path_tmp"; rm -f "$varname".pdf; pdflatex -interaction=nonstopmode "$varname".tex >/dev/null"
5786    if isfile(['${path_tmp}$varname.pdf'])
5787      i ${path_tmp}$varname.pdf,800
5788      autocrop. lt. 128 r2dy. 20% n. 0,255 channels. -3,0
5789      if !isdir('img') x "mkdir -p img" fi
5790      o. $_gmd_filename
5791    fi
5792    rm
5793  endl fi
5794
5795# Manage shell execution.
5796_gmd2html_shell :
5797  _gmd_shell. $*
5798  _gmd_ascii2html.
5799
5800_gmd2ascii_shell :
5801  _gmd_shell. $*
5802
5803_gmd_shell :
5804  +rows. $1,$2 _gmd_command={t} rm.
5805  path_tmp=${-path_tmp}
5806  strcasevar $_gmd_command
5807  filename=$path_tmp${}.log
5808  x $_gmd_command" > "$filename" 2>&1"
5809  if isfile(['$filename']) it $filename else 0 fi
5810  autocrop. {'\n'}
5811
5812# Convert an ascii buffer into html.
5813_gmd_ascii2html :
5814  repeat $! l[$>]
5815    . # [0] = Output, [1] = Input
5816    eval. ">
5817      begin("${-_gmd_write}${-_gmd_vt100}" spans = 0);
5818      c = i;
5819      c==_'&'?write('&amp;'):
5820      c==_'\"'?write('&quot;'):
5821      c==_'\''?write('&apos;'):
5822      c==_'<'?write('&lt;'):
5823      c==_'>'?write('&gt;'):
5824      c==_'\n'?write('<br/>\n'):
5825      c==_'\33' && j[1]==_'['?( # VT100 sequence
5826        ref(crop(0,y,1,8),C);
5827        same(C,vt100_b,size(vt100_b))?(
5828          write('<span class=\"gmd_bold_a\">');
5829          copy(i[y],0,size(vt100_b),1,0);
5830          ++spans
5831        ):same(C,vt100_c,size(vt100_c))?(
5832          write('<span style=\"color: darkcyan\">');
5833          copy(i[y],0,size(vt100_c),1,0);
5834          ++spans
5835        ):
5836        same(C,vt100_g,size(vt100_g))?(
5837          write('<span style=\"color: darkgreen\">');
5838          copy(i[y],0,size(vt100_g),1,0);
5839          ++spans
5840        ):
5841        same(C,vt100_i,size(vt100_i))?(
5842          write('<span class=\"gmd_italic_a\">');
5843          copy(i[y],0,size(vt100_i),1,0);
5844          ++spans
5845        ):
5846        same(C,vt100_m,size(vt100_m))?(
5847          write('<span style=\"color: darkmagenta\">');
5848          copy(i[y],0,size(vt100_m),1,0);
5849          ++spans
5850        ):
5851        same(C,vt100_n,size(vt100_n))?(
5852          repeat (spans,write('</span>'));
5853          copy(i[y],0,size(vt100_n),1,0);
5854          spans = 0
5855        ):
5856        same(C,vt100_r,size(vt100_r))?(
5857          write('<span style=\"color: darkred\">');
5858          copy(i[y],0,size(vt100_r),1,0);
5859          ++spans
5860        ):
5861        same(C,vt100_s,size(vt100_s))?(
5862          write('<span class=\"gmd_strikethrough\">');
5863          copy(i[y],0,size(vt100_s),1,0);
5864          ++spans
5865        ):
5866        same(C,vt100_u,size(vt100_u))?(
5867          write('<span class=\"gmd_underline\">');
5868          copy(i[y],0,size(vt100_u),1,0);
5869          ++spans
5870        ):
5871        write(c)
5872      ):write(c);
5873    "
5874    k[0] discard 0
5875  endl done
5876
5877#@cli gmd2ascii : _max_line_length>0,_indent_forced_newlines>=0 : (no arg)
5878#@cli : Convert selected gmd-formatted text images to ascii format.
5879#@cli : Default values: 'max_line_length=80' and 'indent_forced_newline=0'.
5880gmd2ascii :
5881  is_arg=1 l[] check "isint(${1=80}) && $1>0 && isint(${2=0}) && $2>=0" onfail is_arg=0 endl
5882  max_line_length,indent_forced_newline=${1-2}
5883  if !$is_arg
5884    if !narg($_shell_cols) _shell_cols={${-shell_cols}-5} fi
5885    max_line_length,indent_newline=$_shell_cols,0 noarg
5886  fi
5887
5888  use_vt100 parse_gmd
5889  repeat $! l[$>] nm={b} strvar $nm fnm=${}
5890    . # [0] = Output, [1] = Input
5891    eval. ">
5892      begin("${-_gmd_tokens}${-_gmd_write}${-_gmd_vt100}"
5893        is_blockquote = is_code_block = is_center = is_right = 0;
5894        ref(vector1024(),tmp);
5895        const nbcols = "${-shell_cols}";
5896        ref(vector(#nbcols,_' '),whiteline);
5897      );
5898      c = i;
5899
5900      c==_'\n'?(
5901        is_blockquote ? write([vt100_n,'\n  > ',vt100_m]):
5902        is_code_block ? write('\n  '):
5903        write(c);
5904        is_center ? write(whiteline,nbcols/4):
5905        is_right ? write(whiteline,nbcols/2);
5906      ):
5907      c>0?write(c):
5908      (
5909        isin(c,e_bold_a,e_bold_u,e_bold_italic_a,e_bold_italic_u,e_italic_a,e_italic_u,
5910             e_strikethrough,e_underline,e_url,e_page_link,e_formula) ? write(vt100_n):
5911        isin(c,e_bullet,e_subbullet,e_subsubbullet) ? write(_'\n'):
5912        isin(c,e_section,e_subsection,e_subsubsection,e_subsubsubsection) ? write([vt100_n,'\n\n']):
5913
5914        c==s_section ? (write_nl(); write([_'\n',vt100_r,vt100_b,'# '])):
5915        c==s_subsection ? (write_nl(); write([_'\n',vt100_r,vt100_b,'## '])):
5916        c==s_subsubsection ? (write_nl(); write([_'\n',vt100_r,vt100_b,'### '])):
5917        c==s_subsubsubsection ? (write_nl(); write([_'\n',vt100_r,vt100_b,'#### '])):
5918        c==s_anchor ? (
5919          ind_e = find(#1,e_anchor,y);
5920          copy(i[y],0,ind_e - y + 1,1,0);
5921        ):
5922        c==s_bullet ? write(' * '):
5923        c==s_subbullet ? write('   - '):
5924        c==s_subsubbullet ? write('     + '):
5925        c==s_center ? (write(whiteline,nbcols/4); is_center = 1):
5926        c==e_center ? (write(_'\n'); is_center = 0):
5927        c==s_right ? (write(whiteline,nbcols/2); is_right = 1):
5928        c==e_right ? (write(_'\n'); is_right = 0):
5929        c==s_tab ? write(whiteline,4):
5930        isin(c,s_table,s_htable,s_vtable,s_hvtable,n_table) ? (write_nl(); write([vt100_r,' | ',vt100_n])):
5931        c==m_table ? write([vt100_r,' | ',vt100_n]):
5932        c==s_blockquote ? (write([['  > '],vt100_m]); is_blockquote = 1):
5933        c==e_blockquote ? (write([vt100_n,'\n']); is_blockquote = 0):
5934        c==s_detail_block ? (
5935          ny = y + 1;
5936          nc = j[1];
5937          nc!=e_detail_block?(
5938            j[1] = 0;
5939            ind_e = find(#1,e_detail_block,ny);
5940            nc==_' '?( # Has a title
5941              ind_nl = find(#1,_'\n',ny);
5942              ind_nl<0 || ind_nl>ind_e?(ind_nl = ind_e);
5943              write([vt100_c,vt100_b,'+ ']);
5944              write(#1,y + 2,ind_nl - y - 2);
5945              write([vt100_n,vt100_c,'\n\n']);
5946              copy(i[ny],0,ind_nl - y - 1,1,0);
5947              i[ind_nl]!=e_detail_block?(i[ind_nl] = 0);
5948            ):write([vt100_c,vt100_b,'Details:',vt100_n,vt100_c,'\n\n']);
5949          )
5950        ):
5951        c==e_detail_block ? write([vt100_n,'\n']):
5952        c==s_code_block ? (_write_off?write(_'\n'); write([['  '],vt100_c]); is_code_block = 1):
5953        c==e_code_block ? (write([vt100_n,'\n\n']); is_code_block = 0):
5954        c==s_shell ? (
5955          ind_e = find(#1,e_shell,y);
5956          run('_gmd2ascii_shell. ',y + 1,',',ind_e -  1);
5957          ref(get('_gmd_command',1024,1),str_com);
5958          write('$ ');
5959          len = find(str_com,0);
5960          write(str_com,len);
5961          write('\n\n');
5962          write(#-1,0,h(#-1));
5963          run('rm.');
5964          write(_'\n');
5965          copy(i[y],0,ind_e - y + 1,1,0);
5966        ):
5967        c==s_hrule ? (write_nl(); write('+------------------------------+\n')):
5968
5969        c==s_bold_italic_a ? write([vt100_m,vt100_b,vt100_i]):
5970        c==s_bold_italic_u ? write([vt100_b,vt100_i]):
5971        c==s_bold_a ? write([vt100_m,vt100_b]):
5972        c==s_bold_u ? write(vt100_b):
5973        c==s_italic_a ? write([vt100_m,vt100_i]):
5974        c==s_italic_u ? write(vt100_i):
5975        c==s_strikethrough? write(vt100_s):
5976        c==s_underline? write(vt100_u):
5977        c==s_monospace ? write([_'\47',vt100_c]):
5978        c==e_monospace ? write([vt100_n,_'\47']):
5979        c==s_value_set ? write([vt100_g,vt100_i,_'{']):
5980        c==e_value_set ? write([_'}',vt100_n]):
5981        c==s_word_highlight ? write([_'\47',vt100_g]):
5982        c==e_word_highlight ? write([vt100_n,_'\47']):
5983
5984        isin(c,s_url,s_page_link,s_text_link) ? write(vt100_u):
5985        c==m_text_link ? (
5986          ind = find(#1,e_text_link,y);
5987          copy(i[y],0,ind - y + 1,1,0);
5988          write(vt100_n);
5989        ):
5990        c==s_img ? (
5991          ind_m = find(#1,m_img,y);
5992          ind_e = find(#1,e_img,ind_m);
5993          write(vt100_u);
5994          write(#1,ind_m + 1,ind_e - ind_m -1);
5995          write([vt100_n,_' ',vt100_g,vt100_i,'[image: \'']);
5996        ):
5997        c==m_img ? (
5998          ind = find(#1,e_img,y);
5999          copy(i[y],0,ind - y + 1,1,0);
6000          write([['\']'],vt100_n]);
6001        ):
6002        c==s_pipeline ? (
6003          ind = find(#1,e_pipeline);
6004          write([vt100_g,vt100_i,'[image]',vt100_n]);
6005          copy(i[y],0,ind - y + 1,1,0);
6006        ):
6007        c==s_formula ? write(vt100_g);
6008
6009        c==s_gmic ? (
6010          write([vt100_b,'G\47MIC',vt100_n]);
6011        ):
6012        c==s_whitespace ? write(_' ');
6013      );
6014      end(resize(#0,1,_write_off,1,1,0))"
6015    k[0] discard 0
6016    _gmd2ascii_cut $max_line_length,$indent_forced_newline
6017    nm $fnm.txt
6018
6019  endl done
6020
6021# Cut long ascii lines in selected text images (ignoring the VT100 codes for the length).
6022# $1: Max length of each line >0.
6023# $2: Indentation put after each forced newline.
6024_gmd2ascii_cut :
6025  repeat $! l[$>]
6026    . # [0] = Output, [1] = Input
6027    eval ${-_gmd_write}"
6028      const N = max(8,$1);
6029      ref(vector(#$2+1,_' '),nl); nl[0] = _'\n';
6030
6031      # p = pos of block start, q = pos of latest endable (>=p), l = text length in block (w/o VT100 codes).
6032      p = q = r = 0; l = 1;
6033      while (r<h,
6034        i[r]=='\33' && i[r + 1]==_'['?( # VT100-code -> ignore
6035          while (i[r++]!=_'m', 0);
6036        ):i[r]==_'\n'?(
6037          len = r - p + 1;
6038          write(#1,p,len);
6039          p = q = r + 1;
6040          l = 0;
6041        ):l>N?(
6042          q - p<0.6*N?(q = r - 1);
6043          len = q - p + 1;
6044          write(#1,p,len);
6045          write(nl);
6046          p = r = ++q;
6047          l = size(nl) - 1;
6048        ):(
6049          isin(i[r],_',',_' ')?(q = r);
6050        );
6051        ++r; ++l;
6052      );
6053      p<h?(len = h - p; write(#1,p,len))"
6054
6055    k[0] autocrop 0
6056  endl done
6057
6058# Define variables associated to markdown tokens, for the math parser.
6059_gmd_tokens :
6060  u "const s_section = -1;
6061     const e_section = -2;
6062     const s_subsection = -3;
6063     const e_subsection = -4;
6064     const s_subsubsection = -5;
6065     const e_subsubsection = -6;
6066     const s_subsubsubsection = -7;
6067     const e_subsubsubsection = -8;
6068     const s_anchor = -9;
6069     const e_anchor = -10;
6070     const s_bullet = -11;
6071     const e_bullet = -12;
6072     const s_subbullet = -13;
6073     const e_subbullet = -14;
6074     const s_subsubbullet = -15;
6075     const e_subsubbullet = -16;
6076     const s_center = -17;
6077     const e_center = -18;
6078     const s_right = -19;
6079     const e_right = -20;
6080     const s_table = -21;
6081     const s_htable = -22;
6082     const s_vtable = -23;
6083     const s_hvtable = -24;
6084     const m_table = -25;
6085     const n_table = -26;
6086     const e_table = -27;
6087     const s_blockquote = -28;
6088     const e_blockquote = -29;
6089     const s_detail_block = -30;
6090     const e_detail_block = -31;
6091     const s_code_block = -32;
6092     const e_code_block = -33;
6093     const s_shell = -34;
6094     const e_shell = -35;
6095     const s_hrule = -36;
6096
6097     const s_bold_italic_a = -37;
6098     const e_bold_italic_a = -38;
6099     const s_bold_italic_u = -39;
6100     const e_bold_italic_u = -40;
6101     const s_bold_a = -41;
6102     const e_bold_a = -42;
6103     const s_bold_u = -43;
6104     const e_bold_u = -44;
6105     const s_italic_a = -45;
6106     const e_italic_a = -46;
6107     const s_italic_u = -47;
6108     const e_italic_u = -48;
6109     const s_strikethrough = -49;
6110     const e_strikethrough = -50;
6111     const s_underline = -51;
6112     const e_underline = -52;
6113     const s_monospace = -53;
6114     const e_monospace = -54;
6115     const s_value_set = -55;
6116     const e_value_set = -56;
6117     const s_word_highlight = -57;
6118     const e_word_highlight = -58;
6119
6120     const s_url = -59;
6121     const e_url = -60;
6122     const s_page_link = -61;
6123     const e_page_link = -62;
6124     const s_text_link = -63;
6125     const m_text_link = -64;
6126     const e_text_link = -65;
6127     const s_img = -66;
6128     const m_img = -67;
6129     const e_img = -68;
6130     const s_pipeline = -69;
6131     const e_pipeline = -70;
6132     const s_formula = -71;
6133     const e_formula = -72;
6134
6135     const s_gmic = -73;
6136     const e_gmic = -74;
6137     const s_whitespace = -75;
6138     const s_tab = -76;"
6139
6140# Define variables corresponding to VT100 sequences, for the math parser.
6141_gmd_vt100 :
6142  use_vt100
6143  u "ref('"$_vt100_b"',vt100_b);
6144     ref('"$_vt100_c"',vt100_c);
6145     ref('"$_vt100_g"',vt100_g);
6146     ref('"$_vt100_i"',vt100_i);
6147     ref('"$_vt100_m"',vt100_m);
6148     ref('"$_vt100_n"',vt100_n);
6149     ref('"$_vt100_r"',vt100_r);
6150     ref('"$_vt100_s"',vt100_s);
6151     ref('"$_vt100_u"',vt100_u);"
6152
6153# Define variables and functions to write strings and characters in a streamed way for the math parser.
6154_gmd_write :
6155  u "write(str) = write(str,0);
6156     write(str,len) = (
6157       ref(str,_str); unref(_siz); const _siz = size(_str); ref(len,_len);
6158       _siz?( # String
6159         l = _len?_len:_siz;
6160         _write_off + l>h(#0)?resize(#0,1,8 + l + 5*h(#0)/3,1,1,0);
6161         copy(i[#0,_write_off],_str,l);
6162         _write_off+=l;
6163       ):( # Character
6164         _write_off>=h(#0)?resize(#0,1,8 + 5*h(#0)/3,1,1,0);
6165         i[#0,_write_off++] = _str;
6166       )
6167     );
6168     write(ind,start,len) = (
6169       ref(len,_len);
6170       _write_off + _len>h(#0)?resize(#0,1,8 + _len + 5*h(#0)/3,1,1,0);
6171       copy(i[#0,_write_off],i[#ind,start],_len);
6172       _write_off+=_len;
6173     );
6174     write_nl() = ( # Force newline
6175       i[#0,_write_off -1 ]!=_'\n'?write(_'\n');
6176     );
6177     _write_off = 0;"
6178
6179#@cli parse_gui : _outputmode,_{ * | filter_name}
6180#@cli : Parse selected filter definitions and generate info about filters in selected output mode.
6181#@cli : 'outputmode' can be { json | list | print | strings | update | zart }.\n
6182#@cli : It is possible to define a custom output mode, by implementing the following commands
6183#@cli : ('outputmode' must be replaced by the name of the custom user output mode):
6184#@cli : . 'parse_gui_outputmode' : A command that outputs the parsing information with a custom format.
6185#@cli : . 'parse_gui_parseparams_outputmode' (optional): A simple command that returns 0 or 1. It tells the parser \
6186# whether parameters of matching filter must be analyzed (slower) or not.
6187#@cli : . 'parse_gui_trigger_outputmode' (optional): A command that is called by the parser just before parsing \
6188# the set of each matching filters.\n
6189#@cli : Here is the list of global variables set by the parser, accessible in command 'parse_gui_outputmode':\n
6190#@cli : '$_nbfilters': Number of matching filters.
6191#@cli : '$_nongui' (stored as an image): All merged lines in the file that do not correspond to '#@gui' lines.\n
6192#@cli : For each filter `\#F` ('F' in range `[0,$_nbfilters-1]`):
6193#@cli : * '$_fF_name' : Filter name.
6194#@cli : * '$_fF_path' : Full path.
6195#@cli : * '$_fF_locale' : Filter locale (empty, if not specified).
6196#@cli : * '$_fF_command' : Filter command.
6197#@cli : * '$_fF_commandpreview' : Filter preview command (empty, if not specified).
6198#@cli : * '$_fF_zoomfactor' : Default zoom factor (empty, if not specified).
6199#@cli : * '$_fF_zoomaccurate' : Is preview accurate when zoom changes ? (can be { 0=false | 1=true }).
6200#@cli : * '$_fF_inputmode' : Default preferred input mode (empty, if not specified).
6201#@cli : * '$_fF_hide' : Path of filter hid by current filter (for localized filters, empty if not specified).
6202#@cli : * '$_fF_nbparams' : Number of parameters.\n
6203#@cli : For each parameter `\#P` of the filter \#F ('P' in range `[0,$_fF_nbparams-1]`):
6204#@cli : * '$_fF_pP_name' : Parameter name.
6205#@cli : * '$_fF_pP_type' : Parameter type.
6206#@cli : * '$_fF_pP_responsivity' : Parameter responsivity (can be { 0 | 1 }).
6207#@cli : * '$_fF_pP_visibility' : Parameter visibility.
6208#@cli : * '$_fF_pP_propagation' : Propagation of the parameter visibility.
6209#@cli : * '$_fF_pP_nbargs' : Number of parameter arguments.\n
6210#@cli : For each argument `\#A` of the parameter \#P ('A' in range `[0,$_fF_pP_nbargs-1]`):
6211#@cli : * '$_fF_pP_aA' : Argument value\n
6212#@cli : Default parameters: 'filter_name=*' and 'output_format=print'.
6213parse_gui : skip "${1=print},${2=*}"
6214  e[^-1] "Parse '#@gui' filters '$2' and output in '$1' mode."
6215
6216  # Check that specified output mode is actually implemented.
6217  l[] ({'$$parse_gui_$1'}) rm
6218  onfail error[0--2] "Command 'parse_gui': Invalid output mode '$1'."
6219  endl
6220
6221  # Input .gmic data.
6222  use_vt100
6223  if !$! l[] it ${_path_rc}update$_version.gmic onfail endl fi
6224  if !$! return fi
6225  y a y merge_multiline_comments
6226
6227  # Extract all filter chunks with full path.
6228  e[] ""
6229  e[] "\r  > Extract filter chunks."
6230  macros="
6231    is_blank(p) = ((_c=source[p])<=_' ' && _c!=_'\n');
6232    skip_blank(p) = (while (is_blank(p), ++p));
6233    clean(name) = (
6234      _l = find(name,0);
6235      _l>0?(
6236        _p = 0; while (1,
6237          _p = find(name,_'<',_p);
6238          _p<0 || _p>=_l?break();
6239          isin(name[_p+1],_'b',_'i') && name[_p+2]==_'>'?( # Opening tag
6240            copy(name[_p],name[_p+3],_l-_p-2);
6241            _l-=3;
6242          ):name[_p+1]==_'/' && isin(name[_p+2],_'b',_'i') && name[_p+3]==_'>'?( # Closing tag
6243            copy(name[_p],name[_p+4],_l-_p-3);
6244            _l-=4;
6245          ):++_p;
6246        );
6247        _p = 0; while (1, _p = find(name,_'/',_p); _p<0 || _p>=_l?break(); name[_p++] = _'-'); # Replace '/' by '-'
6248      );
6249    );"
6250
6251  eval $macros"
6252    store_block(p,q,path,name,hide) = (
6253      run('+rows[0] ',p,',',q,' discard. -1 autocrop. 10 autocrop. 32 nm. \"',path,'/',name,'\"');
6254
6255      # Insert hide() info at the end of the chunk, with magic number '-2'.
6256      hide[0]?run([40,45,50,44,123,39,34],hide,[34,39,125,41],' y. a[-2,-1] y');
6257    );
6258
6259    # Init variables.
6260    #----------------
6261    ref(crop(),source);
6262    path = prev_path = name = prev_name = hide = prev_hide = vector1024();
6263
6264    # Start parsing.
6265    #---------------
6266    prev_p0 = p0 = p = 0;
6267    while (p<size(source),
6268      p = find(source,'#@gui',p)%size(source);
6269
6270      !p || (p>0 && source[p - 1]==_'\n')?( # Found line starting with '#@gui'
6271        p0 = p; # Remember starting position of line
6272        p+=5;
6273        source[p]==_'_' && (l = find(source,_' ',++p))>=0?(p = l + 1); # Skip locale
6274
6275        # Detect new filter, folder or hide() directive.
6276        #-----------------------------------------------
6277        skip_blank(p);
6278        source[p]!=_':'?(
6279          hide[0] = 0;
6280          q = find(source,_':',p)%size(source);
6281          r = find(source,_'\n',p)%size(source);
6282          is_hide = q>r && (l=find(source,'hide(',p)%size(source))<r;
6283          r<q && !is_hide?(
6284
6285            # Definition of a new folder.
6286            #----------------------------
6287            r0 = r; while (is_blank(--r),0);
6288            copy(name,source[p],r-p+1); name[r-p+1] = 0;
6289            for (levelup = 0, name[levelup]==_'_', ++levelup);
6290            levelup?(
6291              copy(name,name[levelup],r-p+2-levelup); # Clean folder name
6292              while (levelup--, # Update path
6293                lp = find(path,0); l = max(0,find(path,_'/',lp-1,0)); path[l] = 0;
6294              );
6295            );
6296            clean(name);
6297            name[0]?(
6298              ln = find(name,0); # Update path
6299              lp = find(path,0);
6300              lp?(path[lp] = _'/'; copy(path[lp+1],name,ln); path[lp+ln+1] = 0):(copy(path,name,ln); path[ln] = 0);
6301            );
6302            copy(i[#0,p0],-1,r0-p0+1,1,0); # Discard folder definition line (will be removed later)
6303            p = r0 + 1;
6304          ):!is_hide?(
6305
6306            # Definition of a new filter.
6307            #----------------------------
6308            while (is_blank(--q),0);
6309            copy(name,source[p],q-p+1); name[q-p+1] = 0;
6310            clean(name);
6311            store_block(prev_p0,p0-1,prev_path,prev_name,prev_hide);
6312
6313            prev_p0 = p0;
6314            prev_path = path;
6315            prev_name = name;
6316            prev_hide = hide;
6317            p = q + 1;
6318          ):(
6319
6320            # Manage hide() directive.
6321            #--------------------------
6322            l0 = l + 5; l = find(source,_')',l0);
6323            l<r?(
6324              copy(hide,source[l0],l-l0); hide[l-l0] = 0;
6325              copy(i[#0,p0],-1,r-p0+1,1,0);
6326            );
6327            p = r + 1;
6328          )
6329        )
6330      ):++p
6331    );
6332    prev_p0!=p0?store_block(prev_p0,size(source)-1,prev_path,prev_name,prev_hide) # Insert last block
6333    "
6334  rm[0]
6335
6336  # Isolate only requested filters.
6337  if "['$2']!='*'"
6338    1,$!,1,1,"
6339      begin(ref(lowercase(['$2']),str));
6340      ref(lowercase(name(#y)),nm);
6341      find(nm,str)>=0?y:-1"
6342    discard. -1 k[{^}]
6343  fi
6344  l parse_gui_trigger_$1 onfail endl # Allow global pre-processing before parsing each filter.
6345
6346  # Parse each filter.
6347  nbchunks=$!
6348  f=0 repeat $nbchunks,c _f${f}_path={$>,f} if {$>,h} l[$>]
6349    if crop(0,0,1,5)!='#@gui' # Non-gui : merge code and skip chunk
6350      autocrop {'\n'} store nongui$c 0 continue
6351    fi
6352
6353    # Init variables.
6354    _f${f}_locale=
6355    _f${f}_commandpreview=
6356    _f${f}_zoomfactor=
6357    _f${f}_zoomaccurate=
6358    _f${f}_inputmode=
6359    _f${f}_nbparams=0
6360    _f${f}_hide=
6361
6362    # Detect locale.
6363    if crop(0,0,1,6)=='#@gui_' _f${f}_locale={`crop(0,6,1,2)`} fi
6364
6365    # Detect hid filter.
6366    p={"find(#0,-2,h-1,0)"}
6367    if $p>=0 +rows. {$p+1},100% _f${f}_hide={t} rm. fi
6368
6369    # Separate '#@gui' lines from other lines, and merge them.
6370    eval $macros"
6371      ref(crop(),source);
6372      is_first = 1;
6373      p = 0;
6374      while (p<size(source),
6375        p = find(source,'#@gui',p)%size(source);
6376        !p || (p>0 && source[p - 1]==_'\n')?( # Found line starting with '#@gui'
6377          p0 = p; # Remember starting position of line
6378          p+=5;
6379          source[p]==_'_' && (l = find(source,_' ',++p))>=0?(p = l + 1); # Skip locale
6380          skip_blank(p);
6381          source[p]==_':'?(++p; skip_blank(p));
6382          r = find(source,_'\n',p)%size(source); # Find end of line
6383          run('+rows[0] ',p,',',r);
6384          source[r]==_'\n'?(i[#-1,h(#-1)-1] = is_first?0:_' ');
6385          copy(i[#0,p0],-1,min(r,size(source)-1)-p0+1,1,0); # Part to be discarded
6386          p = r + 1;
6387          is_first = 0;
6388        ):++p
6389      )"
6390    a[^0] y discard.. -1
6391    autocrop[0] {'\n'} store[0] nongui$c
6392    s -,0 # Split in two images : definition and parameters
6393    f "max(_' ',i)" # Replace all blank characters by space
6394
6395    # Parse filter definition.
6396    l[0]
6397      _f${f}_zoomaccurate=0
6398      s -,{':'} autocrop {'" "'}
6399      _f${f}_name={`$macros" name=[['"{0,t}"'],0]; clean(name); name"`} rm[0] # Filter name
6400      if $! l[0]
6401        s -,{','} autocrop {'" "'}
6402        _f${f}_command={0,t} # Filter command
6403        if $!>1 l[1]
6404          s -,{'('}
6405          _f${f}_commandpreview={0,t} # Filter preview command
6406          if $!>1 l[1]
6407            s +,{'+'}
6408            discard[0] {')'}
6409            _f${f}_zoomfactor={0,t} # Default zoom factor
6410            if $!>1" && "i[0]==_'+' _f${f}_zoomaccurate=1 fi # Is zoom accurate ?
6411          endl fi
6412        endl fi
6413        rm
6414      endl fi
6415      if $! _f${f}_inputmode={0,t} fi # Default input mode
6416      rm
6417    endl
6418    e[] "\r  > "$_vt100_c[#$f]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
6419
6420    # Parse filter parameters.
6421    parseparams=1
6422    l[] parseparams=${-parse_gui_parseparams_$1} onfail endl
6423
6424    if !$!" || "!$parseparams _f${f}_nbparams=0
6425    else
6426      eval $macros"
6427        ref(crop(),source);
6428        ref(vector256(),name);
6429        ref(vector256(),type);
6430        ref(vector65536(),argument);
6431
6432        p = nbparams = 0;
6433        while (p<size(source),
6434          q = find(source,_'=',p)%size(source);
6435          source[q]==_'='?( # Found new parameter
6436            skip_blank(p);
6437            r = q + 1; skip_blank(r);
6438            while (is_blank(--q),0);
6439            copy(name,source[p],q-p+1); name[q-p+1] = 0; # Parameter name
6440            store(name,'tmparg',q-p+1);
6441            p = r;
6442            q = min(find(source,_'(',p)%size(source),
6443                    find(source,_'{',p)%size(source),
6444                    find(source,_'[',p)%size(source));
6445            c = source[q]==_'('?_')':source[q]==_'{'?_'}':_']';
6446            r = q + 1; skip_blank(r);
6447            while (is_blank(--q),0);
6448
6449            responsivity = 1;
6450            source[p]==_'_'?(responsivity = 0; ++p);
6451            copy(type,source[p],q-p+1); type[q-p+1] = 0; # Parameter type
6452            for (i = 0, type[i], ++i, type[i] = lowercase(type[i])); # Force lowercase
6453            run('$tmparg _f"${f}"_p',nbparams,'_name={t} rm. _f"${f}"_p',nbparams,'_type=',
6454                 type,' _f"${f}"_p',nbparams,'_responsivity=',responsivity);
6455
6456            # Parse list of arguments.
6457            p = r;
6458            r = find(source,c,p)%size(source);
6459            nbargs = 0;
6460
6461            type[0,5]==[['bool'],0] ||
6462            type[0,7]==[['button'],0] ||
6463            type[0,6]==[['value'],0] ||
6464            type[0,7]==[['filein'],0] ||
6465            type[0,8]==[['fileout'],0] ||
6466            type[0,7]==[['folder'],0] ||
6467            type[0,5]==[['note'],0] ||
6468            type[0,10]==[['separator'],0]?( # Single-argument parameter
6469              p!=r?(
6470                skip_blank(p);
6471                pe = r;
6472                while (is_blank(--pe),0);
6473                copy(argument,source[p],pe-p+1); argument[pe-p+1] = 0; # Parameter argument
6474                store(argument,'tmparg',1,pe-p+1); run('$tmparg _f"${f}"_p',nbparams,'_a0={t} rm.');
6475                nbargs = 1;
6476              ):(argument[0] = 0);
6477              p = r + 1;
6478            ):( # Multiple-arguments parameter -> parse list of arguments
6479              while (p<r,
6480                skip_blank(p);
6481                pe = q = min(r,find(source,_',',p)%size(source));
6482                while (is_blank(--pe),0);
6483                copy(argument,source[p],pe-p+1); argument[pe-p+1] = 0; # Parameter argument
6484                store(argument,'tmparg',1,pe-p+1); run('$tmparg _f"${f}"_p',nbparams,'_a',nbargs,'={t} rm.');
6485                ++nbargs;
6486                p = q + 1;
6487              );
6488            );
6489            run('_f"${f}"_p',nbparams,'_nbargs=',nbargs);
6490
6491            # Extract visibility state.
6492            p = r + 1;
6493            p<size(source) && source[p]==_'_' && isin(source[p+1],_'0',_'1',_'2')?(
6494              r = p + 1;
6495              run('_f"${f}"_p',nbparams,'_visibility=',source[r]-_'0');
6496              isin(source[r+1],_'+',_'-',_'*')?run('_f"${f}"_p',nbparams,'_propagation=',[source[++r]]);
6497            );
6498
6499            p = r + 1;
6500            skip_blank(p);
6501            source[p]==_','?++p;
6502            ++nbparams;
6503          ):++p;
6504        );
6505        run('_f"${f}"_nbparams=',nbparams)"
6506    fi
6507    if !$! 0 fi
6508    f+=1
6509  endl fi done
6510  _nbfilters=$f rm
6511
6512  # Merge all non-gui code.
6513  repeat $nbchunks if narg(${nongui$>}) ${nongui$>} if !w rm. fi fi done
6514  if $! ('\n') a[^-1] .,y rm. a y else 0 fi
6515  store. _nongui
6516
6517  # Done!
6518  e[] "\r  > "${_vt100_g}{`copy(vector64(_'" "'),'"Parsing done!"')`}$_vt100_n
6519  v + parse_gui_$1 # Output result of the parsing.
6520
6521#
6522# Implements 'print' mode for command 'parse_gui' (default output mode).
6523#
6524parse_gui_parseparams_print : u 0 # Tell parser to not parse filter parameters
6525parse_gui_trigger_print : sort_list +,n
6526parse_gui_print :
6527  e[] "\r"${_vt100_g}{`vector68(_'" "')`}$_vt100_n
6528  e[] "  > "${_vt100_g}"List of matching filters:"$_vt100_n"\n\n"
6529  repeat $_nbfilters
6530    +e[] "  [#"{1+$>}"] "${_f$>_path}${_f$>_name}
6531  done
6532
6533#
6534# Implements 'whatsnew' mode for command 'parse_gui'.
6535#
6536parse_gui_parseparams_whatsnew : u 0 # Tell parser to not parse filter parameters
6537parse_gui_whatsnew :
6538  e[] "  >> Generate output, in 'whatsnew' mode.\n"
6539  l[]
6540    1,1,1,1x$_nbfilters
6541    repeat $! nm[$>] -${_f$>_path}${_f$>_name} f[$>] $> done
6542    sort_list +,n a x
6543    repeat w n={0,i[$>]} ('${_f${n}_path}" "${_f${n}_name}\n') done
6544    rm[0]
6545  endl
6546  y a y
6547  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
6548
6549#
6550# Implements 'list' mode for command 'parse_gui'.
6551#
6552parse_gui_parseparams_list : u 0 # Tell parser to not parse filter parameters
6553parse_gui_list :
6554  e[] "  >> Generate output, in 'list' mode.\n"
6555  ('"*** List of filters in the G\47MIC plug-in ("$_nbfilters" filters, on "\
6556               {`v=date(1);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}"/"\
6557               {`v=date(2);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}", "\
6558               {`v=date(4);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}":"\
6559               {`v=date(5);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}") ***\n\n"')
6560
6561  ('"* List of filters, sorted by path:\n\n"')
6562  l[]
6563    1,1,1,1x$_nbfilters
6564    repeat $! nm[$>] -${_f$>_path}${_f$>_name} f[$>] $> done
6565    sort_list +,n a x
6566    repeat w n={0,i[$>]}
6567      ('${_f${n}_path}${_f${n}_name}" (command: '"${_f${n}_command}"')\n"')
6568    done
6569    rm[0]
6570  endl
6571
6572  ('"\n* List of filters, sorted alphabetically:\n\n"')
6573  l[]
6574    1,1,1,1x$_nbfilters
6575    repeat $! nm[$>] -${_f$>_name} f[$>] $> done
6576    sort_list +,n a x
6577    repeat w n={0,i[$>]}
6578      ('${_f${n}_name}" (in '"${_f${n}_path}"')\n"')
6579    done
6580    rm[0]
6581  endl
6582
6583  ('"\n*** End of list ***\n"')
6584  y a y
6585  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
6586
6587#
6588# Implements 'update' mode for command 'parse_gui'.
6589#
6590parse_gui_parseparams_update : u 1 # Tell parser to parse filter parameters
6591parse_gui_update :
6592  e[] "  >> Generate output, in 'update' mode.\n"
6593  path=/
6594  N={$_nbfilters-1}
6595  is_af={date([1,2])==[4,1]" && "(date(4)%2)} # April fool mode ?
6596  repeat $_nbfilters,f
6597    e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
6598
6599    # Insert directives to change folder if necessary.
6600    if ;['$path']!=['${_f${f}_path}']
6601      command_path={`"
6602        src = ['"$path"'];
6603        dest = ['"${_f${f}_path}'"];
6604        ref(vector1024(),command);
6605        for (p = 0, p<min(size(src),size(dest)) && src[p]==dest[p], ++p); # Find common base
6606        while (p>0 && src[p]!=_'/' && dest[p]!=_'/', --p);
6607
6608        nb_levelup = 0;
6609        for (q = p + 1, q<size(src), ++nb_levelup, q = find(src,_'/',q) + 1);
6610        nb_levelup?(copy(command,'#@gui '); copy(command[6],_'_',nb_levelup,1,0));
6611
6612        is_testing = !find(dest,'Testing/');
6613        is_about = !find(dest,'About/');
6614
6615        dest[p]==_'/'?++p;
6616        while (p<size(dest),
6617          q = find(dest,_'/',p)%size(dest);
6618          eoc = find(command,0);
6619          !eoc || command[eoc-1]!=_'_'?(copy(command[eoc],'#@gui '); eoc+=6);
6620          is_italic = (is_testing && find(dest,'Testing/',p)<0) || is_about;
6621          copy(command[eoc],is_italic?'<i>':'<b>');
6622          copy(command[eoc + 3],dest[p],q-p);
6623          copy(command[eoc + 3 + q - p],is_italic?'</i>\n':'</b>\n');
6624          p = q + 1;
6625        ); command"`}
6626      ('$command_path\n')
6627    fi
6628
6629    path=${_f${f}_path}
6630
6631    if narg(${_f${f}_commandpreview}) commandpreview=,${_f${f}_commandpreview} else commandpreview= fi
6632    if narg(${_f${f}_locale}) locale=_${_f${f}_locale} else locale= fi
6633    if narg(${_f${f}_zoomfactor}) zoomfactor=(${_f${f}_zoomfactor}) else zoomfactor= fi
6634    if ${_f${f}_zoomaccurate} zoomaccurate=+ else zoomaccurate= fi
6635    if narg(${_f${f}_inputmode}) inputmode=" : "${_f${f}_inputmode} else inputmode= fi
6636    strcapitalize ${_f${f}_name} name=${}
6637
6638    if ['${_f${f}_hide}']!=0 ('"#@gui"$locale" hide("${_f${f}_hide}")\n"') fi
6639    ('"#@gui"$locale" "$name:${_f${f}_command}$commandpreview$zoomfactor$zoomaccurate$inputmode\n')
6640
6641    repeat ${_f${f}_nbparams},p
6642      responsivity,visibility=
6643      if !${_f${f}_p${p}_responsivity} responsivity=_ fi
6644      if narg(${_f${f}_p${p}_visibility}) visibility=_${_f${f}_p${p}_visibility}${_f${f}_p${p}_propagation} fi
6645      is_choice={['${_f${f}_p${p}_type}']=='choice'}
6646      is_color={['${_f${f}_p${p}_type}']=='color'}
6647
6648      # Build list of arguments.
6649      args= c=
6650      if $is_choice # Capitalize arguments of choice()
6651        repeat ${_f${f}_p${p}_nbargs},a
6652          if $is_af straprilfool ${_f${f}_p${p}_a${a}}
6653          else strcapitalize ${_f${f}_p${p}_a${a}}
6654          fi
6655        args.=$c${} c=, done
6656      elif $is_color # Force html color code for color()
6657        if inrange(${_f${f}_p${p}_nbargs},3,4)" && "\
6658           inrange(${_f${f}_p${p}_a0},0,255)" && "\
6659           inrange(${_f${f}_p${p}_a1},0,255)" && "\
6660           inrange(${_f${f}_p${p}_a2},0,255)" && "\
6661           (${_f${f}_p${p}_nbargs}==3" || "inrange(${_f${f}_p${p}_a3},0,255))
6662          args=#
6663          repeat ${_f${f}_p${p}_nbargs},a
6664            args.={`"tab = [ _'0',_'1',_'2',_'3',_'4',_'5',_'6',_'7',_'8',_'9',_'a',_'b',_'c',_'d',_'e',_'f' ];
6665                     const v = "${_f${f}_p${p}_a${a}}"; [ tab[(v>>4)&15],tab[v&15] ]"`}
6666          done
6667        else repeat ${_f${f}_p${p}_nbargs},a args.=$c${_f${f}_p${p}_a${a}} c=, done
6668        fi
6669      else
6670        repeat ${_f${f}_p${p}_nbargs},a args.=$c${_f${f}_p${p}_a${a}} c=, done
6671      fi
6672      sep1,sep2={`"
6673        s = ['"$args"'];
6674        size(s)?(
6675          find(s,_'(')<0 && find(s,_')')<0?[_'(',_',',_')']:
6676          find(s,_'{')<0 && find(s,_'}')<0?[_'{',_',',_'}']:
6677          [_'[',_',',_']']
6678        ):[_'(',_',',_')'];
6679      "`}
6680      if s=['${_f${f}_p${p}_type}'];"s=='link' || s=='note' || s=='separator' || s=='value'" name=_
6681      else strcapitalize ${_f${f}_p${p}_name} name=${}
6682      fi
6683      ('"#@gui"$locale" :"{/$name}=$responsivity${_f${f}_p${p}_type}{``$sep1}{``{/$args}}{``$sep2}$visibility\n')
6684
6685    done
6686  done
6687#  repeat $! l[$>] utf82html endl done
6688  $_nongui y a y
6689
6690  # Clean generated output, add footer and header.
6691  compress_gmic.
6692  i[0] ('"#@gmic"\n\
6693         "#"\n\
6694         "#  File         : update"$_version".gmic"\n\
6695         "#                 ( G\47MIC command file )"\n\
6696         "#"\n\
6697         "#  Description  : Update file for G\47MIC commands and filters (for version "${-strver}")."\n\
6698         "#                 ( https://gmic.eu )"\n\
6699         "#"\n\
6700         "#  License      : CeCILL v2.1"\n\
6701         "#                 ( https://cecill.info/licences/Licence_CeCILL_V2.1-en.html )"\n\
6702         "#"\n\
6703         "#  Generated on : "\
6704         {date(0)}"/"\
6705         {`v=date(1);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}"/"\
6706         {`v=date(2);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}", "\
6707         {`v=date(4);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}":"\
6708         {`v=date(5);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}\n\
6709         "#"\n')
6710  ('"\n# Local Variables:"\n\
6711    "# mode: sh"\n\
6712    "# End:"\n\
6713    "#"\n\
6714    "# (End of G\47MIC update file)"')
6715  y a y
6716  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
6717
6718# April Fool version!
6719straprilfool : skip "${1=}"
6720  if ['"$1"']==0 u "" return fi
6721  l[] ('{/"$1"}')
6722    f "(i<=_' ' || i==_'_') && i!=_'\n'?_' ':i" # Replace blank characters and underscores
6723    f "x%2?lowercase(i):uppercase(i)"
6724    f "c = lowercase(i);
6725       c==_'o'?_'0':
6726       c==_'i'?_'1':
6727       c==_'e'?_'3':
6728       c==_'a'?_'4':
6729       c==_'s'?_'5':
6730       c==_'t'?_'7':
6731       c==_'b'?_'8':
6732       i"
6733  u {t} rm endl
6734
6735#
6736# Implements 'json' mode for command 'parse_gui'.
6737#
6738parse_gui_parseparams_json : u 1 # Tell parser to not parse filter parameters
6739parse_gui_trigger_json :
6740  repeat $! l[$>] # Keep only 1-level folders
6741    nm {`s=[['{n}'],0];p=find(s,_'/',size(s)-1,0);p>=0?(p=find(s,_'/',p-1,0);p>0?copy(s,s[p+1],size(s)-p));s`}
6742  endl done
6743  sort_list +,n
6744
6745parse_gui_json :
6746  e[] "  >> Generate output, in 'json' mode.\n"
6747  ('"{\n  \"format_version\": \"gmic_json_1.0\",\n  \"gmic_version\": \""${-strver}"\",\n  \"categories\": [\n"')
6748
6749  current_category=
6750  N={$_nbfilters-1}
6751  repeat $_nbfilters,f
6752    e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
6753
6754    # Manage categories.
6755    0 nm. {`s=['${_f${f}_path}'];s[0,size(s)-1]`} _parse_gui_json[] {b} path=${} rm.
6756    if ['$current_category']!=['$path']
6757      if ['$current_category']!=0 ('"    ]\n  },\n"') fi
6758      ('"  {\n"')
6759      ('"    \"name\": \""$path"\", \"filters\": [\n"')
6760      current_category=$path
6761    else
6762      if i[-1,2]==_'\n' z. 0,{w-2} fi
6763      ('",\n"')
6764    fi
6765
6766    # Filter definition.
6767    _parse_gui_json[] ${_f${f}_name} fname=${}
6768
6769    locale=en
6770    if narg(${_f${f}_locale}) locale=${_f${f}_locale} fi
6771
6772    ('"    {\n      \"name\": \""$fname"\", \"lang\": \""$locale"\", \"command\": \""${_f${f}_command}"\", "\
6773      "\"command_preview\": \""${_f${f}_commandpreview}"\", \"parameters\": [\n"')
6774
6775    sepf={`$<?_',':0`}
6776    ppos=1
6777    repeat ${_f${f}_nbparams},p
6778      _parse_gui_json ${_f${f}_p{$p}_name} name={/${}}
6779      type=${_f${f}_p{$p}_type}
6780      nbargs=${_f${f}_p{$p}_nbargs}
6781      arg0= repeat $nbargs ('${_f${f}_p${p}_a$>}') autocrop. {'\"'} _parse_gui_json {t} arg$>=${} rm. done
6782      sepp={`$<?_',':0`}
6783      if narg(${_f${f}_p{$p}_visibility})
6784        visibility=", \"visibility\": \""${_f${f}_p{$p}_visibility}"\""
6785      else
6786        visibility=
6787      fi
6788
6789      if ['$type']=='bool'
6790        if lowercase(['$arg0'])=='false' arg0=0 elif lowercase(['$arg0'])=='true' arg0=1 elif !isnum($arg0) arg0=0 fi
6791        ('"      { \"type\": \"bool\", \"name\": \""$name"\", \"default\": \""$arg0"\", \"pos\": "\
6792          "\""$ppos"\""$visibility" }"$sepp\n')
6793        ppos+=1
6794
6795      elif ['$type']=='button'
6796        if narg($arg0) alignment=", \"alignment\": \""$arg0"\"" else alignment= fi
6797        ('"      { \"type\": \"button\", \"name\": \""$name"\""$alignment", \"pos\": \""$ppos"\""$visibility" }"\
6798         $sepp\n')
6799        ppos+=1
6800
6801      elif ['$type']=='choice'
6802        default=0 n=0 choices=
6803        l[] if isint($arg0) default=$arg0 n+=1 fi onfail endl
6804        c= repeat $nbargs-$n,a choices.=${c}"\""$>"\": \""${arg{$n+$a}}"\"" c=", " done
6805        ('"      { \"type\": \"choice\", \"name\": \""$name"\", \"default\": \""$default"\", \"pos\": \""$ppos"\", "\
6806          "\"choices\": { "$choices" }"$visibility" }"$sepp\n')
6807        ppos+=1
6808
6809      elif ['$type']=='color'
6810        args= c= repeat $nbargs,a args.=$c${arg$a} c="," done
6811        ('"      { \"type\": \"color\", \"name\": \""$name"\", \"default\": \""$args"\", \"pos\": "\
6812          "\""$ppos"\""$visibility" }"$sepp\n')
6813        ppos+=$nbargs
6814
6815      elif s=['$type'];s=='int'
6816        ('"      { \"type\": \"int\", \"name\": \""$name"\", \"default\": \""$arg0"\", \"min\": \""$arg1"\", "\
6817          "\"max\": \""$arg2"\", \"pos\": \""$ppos"\""$visibility" }"$sepp\n')
6818        ppos+=1
6819
6820      elif s=['$type'];s=='float'
6821         ('"      { \"type\": \"float\", \"name\": \""$name"\", \"default\": \""$arg0"\", \"min\": \""$arg1"\", "\
6822           "\"max\": \""$arg2"\", \"pos\": \""$ppos"\""$visibility" }"$sepp\n')
6823        ppos+=1
6824
6825      elif ['$type']=='file'" || "['$type']=='filein'" || "['$type']=='fileout'
6826        ('"      { \"type\": \"file\", \"name\": \""$name"\", \"default\": \""{/$arg0}"\", \"pos\": "\
6827          "\""$ppos"\""$visibility" }"$sepp\n')
6828        ppos+=1
6829
6830      elif ['$type']=='folder'
6831        ('"      { \"type\": \"folder\", \"name\": \""$name"\", \"default\": \""{/$arg0}"\", \"pos\": "\
6832          "\""$ppos"\""$visibility" }"$sepp\n')
6833        ppos+=1
6834
6835      elif ['$type']=='link'
6836        align=-1 name= url= n=0
6837        l[] if isnum($arg0) align=$arg0 n+=1 fi onfail endl
6838        if $nbargs-$n>1 name=${arg$n} url=${arg{$n+1}}
6839        else url,name=${arg$n}
6840        fi
6841        if $align==0 align=left elif $align==1 align=right else align=center fi
6842        ('"      { \"type\": \"link\", \"name\": \""$name"\", \"url\": \""{/$url}"\", \"align\": "\
6843          "\""$align"\""$visibility" }"$sepp\n')
6844
6845      elif ['$type']=='note'
6846        ('"      { \"type\": \"note\", \"text\": \""{/$arg0}"\""$visibility" }"$sepp\n')
6847
6848      elif ['$type']=='point'
6849        ('"      { \"type\": \"point\", \"name\": \""$name"\", \"position\": \""$arg0,$arg1"\", \"pos\": "\
6850          "\""$ppos"\""$visibility" }"$sepp\n')
6851        ppos+=2
6852
6853      elif ['$type']=='separator'
6854        ('"      { \"type\": \"separator\""$visibility" }"$sepp\n')
6855
6856      elif ['$type']=='text'
6857        ('"      { \"type\": \"text\", \"name\": \""$name"\", \"default\": \""{/$arg0}"\", \"pos\": "\
6858          "\""$ppos"\""$visibility" }"\
6859         $sepp\n')
6860        ppos+=1
6861
6862      elif ['$type']=='value'
6863        ('"      { \"type\": \"value\", \"value\": \""{/$arg0}"\", \"pos\": \""$ppos"\""$visibility" }"$sepp\n')
6864        ppos+=1
6865
6866      else # Unknown parameter
6867        ('"      { \"type\": \"unknown\", \"name\": \""$name"\""$visibility" }"$sepp\n')
6868      fi
6869
6870    done
6871    ('"      ]\n    }\n"')
6872  done
6873  if ['$current_category']!=0 ('"    ]\n  }\n"') fi
6874  ('"  ]\n}\n"')
6875  y a y html2utf8
6876  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
6877
6878_parse_gui_json :
6879  l[]
6880    ('{/"$*"}')
6881    replace_str "\\","\\\\"
6882    replace_str "\n","\\n"
6883    replace_str "\r","\\r"
6884    replace_str "\"","\\\""
6885    replace_str "&amp;","&"
6886    replace_str "&nbsp;"," "
6887    replace_str. "<i>",""
6888    replace_str. "</i>",""
6889    replace_str. "<b>",""
6890    replace_str. "</b>",""
6891    replace_str. "<small>",""
6892    replace_str. "</small>",""
6893    u {t} rm.
6894  onfail u "" endl
6895
6896#
6897# Implements 'strings' mode for command 'parse_gui'.
6898# (Extract all strings from filters, for translation purpose).
6899#
6900parse_gui_strings :
6901  e[] "  >> Generate output, in 'strings' mode.\n"
6902  m "is_str : u {\"s = ['$""1']; isl = 0; "\
6903    "repeat (size(s),k, inrange(lowercase(s[k]),_'a',_'z')?(isl = 1; break())); isl && size(s)>2 \"}"
6904  N={$_nbfilters-1}
6905  repeat $_nbfilters,f
6906    if !narg(${_f${f}_locale})" && "find(['${_f${f}_path}'],'Testing/')<0  # Consider only English, non-Testing filters
6907      e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
6908      name=${_f${f}_name}
6909      is_str $name if ${} ({'$name'}:y) nm. $name fi
6910      repeat ${_f${f}_nbparams},p
6911        pname=${_f${f}_p${p}_name}
6912        ptype=${_f${f}_p${p}_type}
6913        is_str $pname if ${} ({'$pname'}:y) nm. $pname fi
6914        if ['$ptype']==['choice']
6915          repeat ${_f${f}_p${p}_nbargs},a
6916            ({'${_f${f}_p${p}_a${a}}'}) discard. {'\"'}
6917            aname={t} rm.
6918            is_str $aname if ${} ({'$aname'}:y) nm. $aname fi
6919          done
6920        fi
6921      done
6922    fi
6923  done
6924  repeat $! nm[$>] {`lowercase(['{$>,n}'])`} done
6925  sort_list +,n
6926  repeat $!-1 if ['{$<,n}']==['{{$<+1},n}'] rm[$<] fi done
6927  um is_str
6928  if $! y i[1--1] (10) a y else 0 fi
6929
6930#@cli pass : _shared_state={ -1=status only | 0=non-shared (copy) | 1=shared | 2=adaptive } : (+)
6931#@cli : Insert images from parent context of a custom command or a local environment.
6932#@cli : Command selection (if any) stands for a selection of images in the parent context.
6933#@cli : By default (adaptive shared state), selected images are inserted in a shared state if they do not belong
6934#@cli : to the context (selection) of the current custom command or local environment as well.
6935#@cli : Typical use of command 'pass' concerns the design of custom commands that take images as arguments.
6936#@cli : This commands return the list of corresponding indices in the status.
6937#@cli : Default value: 'shared_state=2'.
6938#@cli : $ command "average : pass$""1 add[^-1] [-1] remove[-1] div 2" sample ? +mirror y +average[0] [1]
6939
6940#@cli plot : _plot_type,_vertex_type,_xmin,_xmax,_ymin,_ymax,_exit_on_anykey={ 0 | 1 } : \
6941# 'formula',_resolution>=0,_plot_type,_vertex_type,_xmin,xmax,_ymin,_ymax,_exit_on_anykey={ 0 | 1 } : (+)
6942#@cli : Display selected images or formula in an interactive viewer (use the instant display window [0] if opened).
6943#@cli : 'plot_type' can be { 0=none | 1=lines | 2=splines | 3=bar }.
6944#@cli : 'vertex_type' can be { 0=none | 1=points | 2,3=crosses | 4,5=circles | 6,7=squares }.
6945#@cli : 'xmin', 'xmax', 'ymin', 'ymax' set the coordinates of the displayed xy-axes.
6946#@cli : Default values: 'plot_type=1', 'vertex_type=1', 'xmin=xmax=ymin=ymax=0 (auto)' and 'exit_on_anykey=0'.
6947
6948#@cli p : eq. to 'print'. : (+)
6949
6950#@cli print : (+)
6951#@cli : Output information on selected images, on the standard error (stderr).
6952#@cli : (eq. to 'p').
6953
6954#@cli random_pattern : _width>0,_height>0,_min_detail_level>=0
6955#@cli : Insert a new RGB image of specified size at the end of the image list, rendered with a random pattern.
6956#@cli : Default values: 'width=height=512' and 'min_detail_level=2'.
6957#@cli : $ repeat 6 random_pattern 256 done
6958random_pattern : check "isint(${1=512}) && $1>0 && isint(${2=$1}) && $2>0 && ${3=2}>=0"
6959  e[^-1] "Generate a RGB image $1x$2, with a random pattern."
6960  m "rgb2rgb:"
6961
6962  # Generate the 3 channels independently.
6963  repeat 3
6964    do
6965      expr="z = [ "{u(20)}"*((x+0.5)/w-0.5), "{u(20)}"*((y+0.5)/h-0.5) + 0.5/h ]; "${-_random_pattern_expr}
6966      {round([$1,$2]*128/min($1,$2))},1,2,*$expr
6967      replace_naninf. 0 norm. n. 0,255 equalize. 256 c. 0,255
6968      cond=${-_random_pattern_check_image.\ $3}
6969      if !$cond rm. fi
6970    while !$cond
6971    rm. $1,$2,1,2,*$expr
6972    replace_naninf. 0 norm. n. 0,255 equalize. 256 c. 0,255
6973  done
6974  a[-3--1] c
6975
6976  # Convert result from random colorspace to RGB.
6977  ${"arg "{1+int(u(6))%6}",ycbcr2rgb,hsi82rgb,hsl82rgb,hsv82rgb,lab82rgb,rgb2rgb"}.
6978
6979  # Local normalization in random colorspace.
6980  ac. "normalize_local {[u(2,15),u(2,max(w,h)/2)]}",${"arg "{1+int(u(4))%4}",lab_l,ycbcr_y,hsl_l,rgb"}
6981
6982  # Random color balance.
6983  ac. "*. '[{u([0.25,0.25,0.25],[2,2,2])}]'",${"arg "{1+int(u(6))%6}",lab,ycbcr,hsi,hsl,hsv,rgb"}
6984
6985  # Equalize
6986  +equalize. 256 j.. .,0,0,0,0,{u(0.25,1)} rm. n. 0,255
6987  um rgb2rgb nm random_pattern
6988
6989# $1 : probability of a termination node in [0,1]
6990# $2 : level of recursion (avoid stack overflow due to recursivity)
6991_random_pattern_expr : skip "${1=0},${2=0}"
6992  nl={$2+1}
6993  if (u<$1" && "$2>1)" || "$2>6 # Termination node
6994    r={u}
6995    if $r<0.75 u z
6996    else u [{_round(u([-1,-1],[1,1]),0.1)}]
6997    fi
6998  else # Function or operator
6999    p1,p2={[$1,$1]+u([0.2,0.2])}
7000    r={u(27)}
7001    if $r<1 u ccos(${$0\ $p1,$nl})
7002    elif $r<2 u csin(${$0\ $p1,$nl})
7003    elif $r<3 u ctan(${$0\ $p1,$nl})
7004    elif $r<4 u ccosh(${$0\ $p1,$nl})
7005    elif $r<5 u csinh(${$0\ $p1,$nl})
7006    elif $r<6 u ctanh(${$0\ $p1,$nl})
7007    elif $r<7 u cexp(${$0\ $p1,$nl})
7008    elif $r<8 u clog(${$0\ $p1,$nl})
7009    elif $r<9 u [cabs(${$0\ $p1,$nl}),0]
7010    elif $r<10 u [0,cabs(${$0\ $p1,$nl})]
7011    elif $r<11 u [carg(${$0\ $p1,$nl}),0]
7012    elif $r<12 u [0,carg(${$0\ $p1,$nl})]
7013    elif $r<13 u cconj(${$0\ $p1,$nl})
7014    elif $r<14 u (${$0\ $p1,$nl})+(${$0\ $p2,$nl})
7015    elif $r<15 u (${$0\ $p1,$nl})-(${$0\ $p2,$nl})
7016    elif $r<16 u ${$0\ $p1,$nl}**${$0\ $p2,$nl}
7017    elif $r<17 u ${$0\ $p1,$nl}//${$0\ $p2,$nl}
7018    elif $r<18 u (${$0\ $p1,$nl})^^0.5
7019    elif $r<19 u (${$0\ $p1,$nl})^^2
7020    elif $r<20 u (${$0\ $p1,$nl})^^3
7021    elif $r<21 u ${$0\ $p1,$nl}*${$0\ $p2,$nl}
7022    elif $r<22 u ${$0\ $p1,$nl}/(0.01+cabs(${$0\ $p2,$nl}))
7023    elif $r<23 u abs(${$0\ $p1,$nl})^0.5
7024    elif $r<24 u (${$0\ $p1,$nl})^2
7025    elif $r<25 u [(${$0\ $p1,$nl})[0],0]
7026    elif $r<26 u [0,(${$0\ $p1,$nl})[1]]
7027    else u (${$0\ $p1,$nl})^3
7028    fi
7029  fi
7030
7031# Check that rendered image has enough details in it.
7032# $1 = Minimum detail level.
7033_random_pattern_check_image :
7034  cond=1
7035  +s. xy,4
7036  l[^0]
7037    repeat $! if $cond l[$>]
7038      gradient_norm.
7039      if ic<$1 cond=0 fi
7040    endl fi done
7041    rm
7042  endl
7043  u $cond
7044
7045#@cli screen : _x0[%],_y0[%],_x1[%],_y1[%] : (+)
7046#@cli : Take screenshot, optionally grabbed with specified coordinates, and insert it
7047#@cli : at the end of the image list.
7048
7049#@cli select : feature_type,_X[%]>=0,_Y[%]>=0,_Z[%]>=0,_exit_on_anykey={ 0 | 1 },_is_deep_selection={ 0 | 1 } : (+)
7050#@cli : Interactively select a feature from selected images (use the instant display window [0] if opened).
7051#@cli : 'feature_type' can be { 0=point | 1=segment | 2=rectangle | 3=ellipse }.
7052#@cli : Arguments 'X','Y','Z' determine the initial selection view, for 3D volumetric images.
7053#@cli : The retrieved feature is returned as a 3D vector (if 'feature_type==0') or as a 6d vector
7054#@cli : (if 'feature_type!=0') containing the feature coordinates.
7055#@cli : Default values: 'X=Y=Z=(undefined)', 'exit_on_anykey=0' and 'is_deep_selection=0'.
7056
7057#@cli serialize : _datatype,_is_compressed={ 0 | 1 },_store_names={ 0 | 1 } : (+)
7058#@cli : Serialize selected list of images into a single image, optionnally in a compressed form.
7059#@cli : 'datatype' can be { auto | uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }.
7060#@cli : Specify 'datatype' if all selected images have a range of values constrained to a particular datatype,
7061#@cli : in order to minimize the memory footprint.
7062#@cli : The resulting image has only integers values in [0,255] and can then be saved as a raw image of
7063#@cli : unsigned chars (doing so will output a valid .cimg[z] or .gmz file).
7064#@cli : If 'store_names' is set to '1', serialization uses the .gmz format to store data in memory
7065#@cli : (otherwise the .cimg[z] format).
7066#@cli : Default values: 'datatype=auto', 'is_compressed=1' and 'store_names=1'.
7067#@cli : $ image.jpg +serialize uchar +unserialize[-1]
7068
7069#@cli shape_circle : _size>=0
7070#@cli : Input a 2D circle binary shape with specified size.
7071#@cli : Default value: 'size=512'.
7072#@cli : $ shape_circle ,
7073shape_circle : check "${1=512}>=0"
7074  e[^-1] "Input a $1x$1 circle binary shape."
7075  ir={round($1)}
7076  if !$ir 0
7077  elif $ir<2 $ir,$ir,1,1,1
7078  else
7079    {int($ir/2)+($ir%2)},{int($ir/2)+($ir%2)} =. 1,100%,100%
7080    distance. 1 <=. {(i+0.4)/sqrt(2)}
7081    +mirror. x
7082    if $ir>1" && "($ir%2) r. {w-1},100%,1,1,0,0,1 fi
7083    a[-2,-1] x +mirror. y
7084    if $ir>1" && "($ir%2) r. 100%,{h-1},1,1,0,0,0,1 fi
7085    a[-2,-1] y
7086  fi
7087  nm. "[2D circle shape]"
7088
7089#@cli shape_cupid : _size>=0
7090#@cli : Input a 2D cupid binary shape with specified size.
7091#@cli : Default value: 'size=512'.
7092#@cli : $ shape_cupid ,
7093shape_cupid : check "${1=512}>=0"
7094  e[^-1] "Input a $1x$1 cupid binary shape."
7095  ir={round($1)}
7096  if !$ir 0
7097  else
7098    base642img[] \
7099"MiBzaG9ydCBsaXR0bGVfZW5kaWFuCjEgMjMwMSAxIDEgIzI2NDQKeJx1mOl3lEUWxm+n6aS7IUSQRQQEFA37FtkFVFAEBxHZEoKACwRQEUHU0fGM58y"\
7100"Zxa9z5i9AFBAhAZKwg8qACMM6CDgZIIQ9Kx0CmAHqzq/u2yR80A91nue5daveWm5tb2moNBSSkAiptCoq9RqToa6p3NVmMtyli2pzecp1IHWUke6R30"\
7101"wjyB9hfh0o97AMIw11D8kQ0mDXRga6VpLlWsoA10L6uQzpQ909XVy6u6hkulTp6lJkyamw1GhTGc33b2iGHHYR+RT8RVvIUReThKbI3+H/0wflGD512"\
7102"kT+oa3EaRt0M7mlEanFZ4X2QqdTLiLX8flK+5i+rWn36eb0MYqOyErT7eBp8rX2gz8Ej9H3iGww3Y5vZRg/QztvaqMuox13tAvpAdMX3JO0J+CX3AuG"\
7103"l904wytuvOFV9yLYF5wgG8Fy9xK6j1S4iWBvcBL23lLpXkH3kio3Bd1Tqt10sIdcczOkULtLws0Eu4GzwEypdbPJfxx8S3aC190i2aWPge+iu0idW4r"\
7104"uJDfcMvARuek+Ajsa7tT24O9lB33zuE3b4rdY1mtrw3zGPFtTZbm+K9/Qv+mM1Re6iPFKl2mM1Qp9R1Yxd1NJX8FXYptCDK2Ef0m5yfitgtcy7qsNMy"\
7105"i7iLY9IGsMW1Cvx5aylm94XGfYSgp0seF6w9aMyWLKt6HP76HbShFYCxaDCXCTYTvZqksYq4fp01KwA31dKjXE6ff6PthZ9uoyxrSL7AOr3KNyQD9gz"\
7106"B+Tg2CFe1yOGHaTf4Plrrv8BNYRq7eZi1OUqSN+7zJPJfCDGpJu6tdNXzmDPqRh6aHPShn8MDHXQ5+Ti8bD0l3HyiXa4u3ddZxcpT2ed9PxUtHAX5RK"\
7107"fA4Z/51UJXkmvJp+HYQ/Aa+BH2J8M3WCXKPfB+GPUzbgacYTxmNWNsGYHqSdmWb3vBn1jIcvop4IcZzKfKbKSdp1Td+2b9ajsyl/ClsC22H8/NrKoew"\
7108"p+pLQt8x2kzSDeT6lz1N2ofWjDlsue8hJ+p/QBeZXR30ziaEy2ndXx7BmA3st9le1JWPzstmOoBPYX2WNXzHbfPOrwT6LuKzQSazT+dbGauqaTcxW6S"\
7109"vUH/hVYZtDPNckbUewVZjtYeoNbEepqxzba8R/gvoCW4Q5iWLrQP3etoAYiMhlK8v60Yn2XW+7ZGUfZe+ZSPvyiJGIXKDOOdqVcXuJdr0hJ7CVYZvNG"\
7110"r3DHFToa4xRRErN70O+P0d+hp81/QHfno2OEEdpDboE/V+rY5lpz0tMv8/YzEJHrIzXl3Wm6VM2TkvJD/RJ00tMn0afsDFbgn8uuokct/F/j/wZfDsV"\
7111"nYZenNQROYYtlzi5ojmWf9Ri4B3ak2Nt9/P1BvFdT4xc1Wwr42NzHrFzhxgpT/p5W55+zDjk2Bj8i7RAPyHefX6EdRjoqqT/ftJC/QPzOMP8f2zQgf8"\
7112"+2vkW+lpS7yXf60SyfKA/RU+3Nv2zIT+Vto4z2+5knSU60vq2O9mGEh1h+rsGPczG8lvrwyeM21Cbi2+tn38iHoaY3kWb5uqf6fNg0ztIb+pf6GOjfl"\
7113"3/RhsCvd3m/nNibbDVv83m5nPiKsj3eib+9ejT6C2kHP0r6yf4ntfT9VvL22zzcMD4JvvuAfPZRB3zdCfnZ5rpYjAPfRheZHyH7QGFxrfbPN3j3mej8"\
7114"W3GN1hdW22drbe+brH59/xN3ZTkUfpY1GCfpRtsvRVYXwosnvLh2bqWtRTwV3QVa8jzKGdJAeOdZvbp1FkKrjP/rbae1uGTQ9sumD3K3rPL1uhaUo5+"\
7115"RwymwqP4f0+sRThfPP/B5t3bp+t+1nLApzJGt5I+U+D1ST4Jfsd4TCbC1fxj8rz+aPgcuA4co/usnjGmozIazDfcT39jhuvBZ8ENGpdnwI3oUfgVgk9"\
7116"RvhgcqLuZv7hk0eYt6CzdQ2x4/IEz2tv3c341Bc/JfwyvyDn24QFazvmSDlYRcxnST6tZHy05XyroV2swHuqhTUMlrPOpmh46Q53Z2jp0mTpytHPoOn"\
7117"t0rj4Rus39aoYOD03WJqGb+gTndkqoWjszJimh89QzWUOhMvZjj+eo/2VV5iVDxuovrLV0xuMW6yVOf2/anvU02IbUWuukhV6TGO2N6iVJ1YvSRM9Lm"\
7118"BTSUql3P8stt4+7Uzr3j48Z8xhzlsk5wz3PfWjzcZF23EDXOn9OtSD2s8kPs889wNqPc89qTmxPw5ZCrMe5fzWFT6P/IcY2Lhe5a966Tx/nPnXP99e4"\
7119"n4+fXHbSPyYnuHcFPjE56XIb/Eu4f93zP+3mWJu8zxn3GmUD/7PudePe55x7Ax74lLk3OTe9PSrn3dwkj9HWeQ38ksuDh83H8xtJ+xU3v4FfdQuMF+F"\
7120"T7hbCw2avuI9Xci+sS/r/Fq9yb8ODtnl+Pdn+Knxqf4VX41OL/+Yk93f0+/kWxqcGfs14jLhPI969DnM/8+unOfW8TaymmB5LTG1J6m2UHUfMbeXsqM"\
7121"bfr4Px+hD2Rj2BM3o7ugr/Hfjncj9u1DH2md7gQvbdMOsnxnnWp0Hvwn8W97VGHUP3x28BayiFPT3OHjwgqcOcAT7f6/nsLWHWoc/PQuex76SgfX6j3"\
7122"m35T1JvHudCmHPH5z9JvXnEclj2WHsGYl8g5/G/p/fR/zL0Xmv/IM7DRaZ/QOey3x/izC2l/I/W3zGss0Wm96NzuHuVcmafxX+/rfEXqHsxazKFemKs"\
7123"/Qj311Gc4Uuwhbn/xeUFzrxK7g1eH0I/j67mnnGaMofRY/BPcE/xdRxBP82ZeF0/Mv9j6BE6nJj5mG+G2cfjMpgz6iZno/c/jh5An3/hbPX+J9D90PW"\
7124"cx2dt7TaVPujb+kfrw8/oXui7+pn1we9xPbTYfE+zz3WDe78zdm8tMn4Wn65w7+/3wke10Ph59pLOcO9zEXtH3Wj8Mrw93Ptcxacd55Ln5fC2ut54Jb"\
7125"wV3PvXEKMtOYu8PQHP0Hzj19n30nWd8Trs8SS/RdlUzjTP6+Fh/cbafwcuugb+oDj3NdhCbrvVYHP2vlVgnHfLV2CU9fOl3V0qDbmPui/AJuxxcxmrI"\
7126"cYv2v4yyOo+z9vvBrHj+Tn2plp4Kd8/w15Ww3hudZPpU5Zs5s15mRgu5k16gVjf4J6hTD/Jd8MY676y1vVlzvvIN64r8dhb1vCe2swbdDXvsULePavY"\
7127"YwvAlbx91rLWVrgmvON6yATXm/MsLMvdAOI5zf4jrOQtvIc+3WF+VruBxHMz+5+wxg0iljvyrcGG+W4IeR2lwA1Fd6JNw5I4FHsnKXTDkziM+h4x3I1"\
7128"/Efj9fVgMfsd+UES5Xdzni8Gd3PWLqH8774BivreV/aOI72/WNobFnGVFtG0j+04h7d0AFoEFnCke19HeIpfFG7UZ5bN4z0bx6897N9X0cuZhEzgN36"\
7129"mkALNkiqX+Msn1I/WViYzPBNdTXuQdOc5lyljel8/x1hzN+/MZ0tOuk4wijWzA4J9K8D+lvf1PCf6ltLU0iPdvkO7Xngf/XIbw/r2H98r4fzD9eWf3Z"\
7130"u5GVv4foPOEnDEgMjAgMSAxICMzOQp4nHNn8GWIYmBgSGfIZchkSGaIZzCAQj0gLxMoms5QBZQHAHyqBfY="
7131    decompress_rle. r. $ir,$ir,1,1,5 if $ir>480 b. 0.2% fi >=. 40%
7132  fi
7133  nm. "[2D cupid shape]"
7134
7135#@cli shape_diamond : _size>=0
7136#@cli : Input a 2D diamond binary shape with specified size.
7137#@cli : Default value: 'size=512'.
7138#@cli : $ shape_diamond ,
7139shape_diamond : check "${1=512}>=0"
7140  e[^-1] "Input a $1x$1 diamond binary shape."
7141  ir={round($1)}
7142  if !$ir 0
7143  elif $ir<2 $ir,$ir,1,1,1
7144  else
7145    {int($ir/2)+($ir%2)},{int($ir/2)+($ir%2)} =. 1,100%,100%
7146    distance. 1,1 <=. {i/2}
7147    +mirror. x
7148    if $ir>1" && "($ir%2) r. {w-1},100%,1,1,0,0,1 fi
7149    a[-2,-1] x +mirror. y
7150    if $ir>1" && "($ir%2) r. 100%,{h-1},1,1,0,0,0,1 fi
7151    a[-2,-1] y
7152  fi
7153  nm. "[2D diamond shape]"
7154
7155#@cli shape_dragon : _size>=0,_recursion_level>=0,_angle
7156#@cli : Input a 2D Dragon curve with specified size.
7157#@cli : Default value: 'size=512', 'recursion_level=18' and 'angle=0'.
7158#@cli : $ shape_dragon ,
7159shape_dragon : check "${1=512}>=0 && ${2=18}>=0" skip "${3=0}"
7160  e[^-1] "Input a $1x$1 Dragon curve, with recursion level $2 and angle $3."
7161  ir={round($1)}
7162  if !$ir 0
7163  else l[]
7164    (0,1^0,0) ind=1
7165    repeat $2 +f "begin(C = I["$ind"]; R = rot(90°)); R*(I - C) + C" ind={2*h} a y done
7166    s c -.. {-2,ia} -. {ia} a c / {max(abs(im),abs(iM))}
7167    angle={$3-atan2(i1,i0)*180/pi} f. "begin(R = rot("$angle"°)); R*I"
7168    n 0,{$1-1} round $1,$1,1,1 eval.. "!x?polygon(#1,2,I(0,y),I(1,y),1,1); I" k.
7169  endl fi
7170  nm. "[2D dragon shape]"
7171
7172#@cli shape_fern : _size>=0,_density[%]>=0,_angle,0<=_opacity<=1,\
7173# _type={ 0=Asplenium adiantum-nigrum | 1=Thelypteridaceae }
7174#@cli : Input a 2D Barnsley fern with specified size.
7175#@cli : Default value: 'size=512', 'density=50%', 'angle=30', 'opacity=0.3' and 'type=0'.
7176#@cli : $ shape_fern ,
7177shape_fern : check "${1=512}>=0 && ${2=50%}>=0 && isnum(${3=30}) && ${4=0.3}>=0 && $4<=1 && isnum(${5=0})"
7178  e[^-1] "Input a $1x$1 Barnsley fern, with density $2, angle $3 and opacity $4."
7179  ir={round($1)}
7180  if !$ir 0
7181  else l[]
7182    N={${"is_percent $2"}?$ir^2*$2:$2}
7183    $N,1,1,2
7184    eval "
7185     if ($5==0,
7186       # Type 0: Asplenium adiantum-nigrum.
7187       f1 = [ 0,0,0,0.16 ];           g1 = [ 0,0 ];
7188       f2 = [ 0.2,-0.26,0.23,0.22 ];  g2 = [ 0,1.6 ];
7189       f3 = [ -0.15,0.28,0.26,0.24 ]; g3 = [ 0,0.44 ];
7190       f4 = [ 0.85,0.04,-0.04,0.85 ]; g4 = [ 0,1.6 ],
7191
7192       # Type 1: Thelypteridaceae.
7193       f1 = [ 0,0,0,0.25 ];             g1 = [ 0,-0.4 ];
7194       f2 = [ 0.035,-0.2,0.16,0.04 ];   g2 = [ -0.09,0.02 ];
7195       f3 = [ -0.04,0.2,0.16,0.04 ];    g3 = [ 0.12,0.07 ];
7196       f4 = [ 0.95,0.005,-0.005,0.93 ]; g4 = [ -0.002,0.5 ];
7197     );
7198     xy = [ 0,0 ];
7199     repeat ("$N",n,
7200       r = u(100);
7201       xy = r<=1?((f1*xy)+=g1):
7202            r<=8?((f2*xy)+=g2):
7203            r<=15?((f3*xy)+=g3):((f4*xy)+=g4);
7204       I[n] = xy
7205    )"
7206    permute xczy
7207    i.. 2,2,1,1,{"R=rot(-$3°); R[2] = -R[2]; R[3] = -R[3]; R"} m*
7208    repeat 2 sh. $>,$>,0,0 -. {im} rm. done n 0,{$ir-1}
7209    pointcloud -$4 r $ir,$ir,1,1,0,0,0.5,0.5
7210  endl fi
7211  nm. "[2D Barnsley fern]"
7212
7213#@cli shape_gear : _size>=0,_nb_teeth>0,0<=_height_teeth<=100,0<=_offset_teeth<=100,0<=_inner_radius<=100
7214#@cli : Input a 2D gear binary shape with specified size.
7215#@cli : Default value: 'size=512', 'nb_teeth=12', 'height_teeth=20', 'offset_teeth=0' and 'inner_radius=40'.
7216#@cli : $ shape_gear ,
7217shape_gear : check "${1=512}>=0 && isint(${2=12}) && $2>0 && ${3=20}>=0 && $3<=100 && ${4=0}>=0 && $4<=100 &&
7218                    ${5=40}>=0 && $5<=100"
7219  e[^-1] "Input a $1x$1 gear binary shape with $2 teeth, teeth height $3, teeth offset $4 and inner radius $5."
7220  ir={round($1)}
7221  if !$ir 0
7222  else l[]
7223    $ir,$ir
7224    polygon {"
7225      const nb_teeth = $2;
7226      const height_teeth = $3%;
7227      const offset_teeth = 2*$4%;
7228      ref(vector2048(),pts);
7229      for (i = 0, i<size(pts),
7230        a = i*2*pi/size(pts);
7231        r =  1 - height_teeth + height_teeth*(int(a*nb_teeth/pi + 2*nb_teeth - offset_teeth)%2);
7232        pts[i++] = round(w/2*(1 + r*cos(a)));
7233        pts[i++] = round(h/2*(1 + r*sin(a)));
7234      );
7235      [size(pts)/2,pts];
7236    "},1,1
7237    if $5 circle. 50%,50%,{0.5*w*$5%},1,0 fi
7238  endl fi
7239  nm. "[2D gear shape]"
7240
7241#@cli shape_heart : _size>=0
7242#@cli : Input a 2D heart binary shape with specified size.
7243#@cli : Default value: 'size=512'.
7244#@cli : $ shape_heart ,
7245shape_heart : check "${1=512}>=0"
7246  e[^-1] "Input a $1x$1 heart binary shape."
7247  ir={round($1)}
7248  if !$ir 0
7249  else l[]
7250      2048,1,1,1,"t = x*2*pi/w; 16*sin(t)^3"
7251      2048,1,1,1,"t = x*2*pi/w; 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t)"
7252      a c display_parametric $ir,$ir,1,0,0,0
7253      flood 50%,50%,0,0,0,1,0 ==. 0
7254    endl
7255  fi
7256  nm. "[2D heart shape]"
7257
7258#@cli shape_polygon : _size>=0,_nb_vertices>=3,_angle
7259#@cli : Input a 2D polygonal binary shape with specified geometry.
7260#@cli : Default value: 'size=512', 'nb_vertices=5' and 'angle=0'.
7261#@cli : $ repeat 6 shape_polygon 256,{3+$>} done
7262shape_polygon : check "${1=512}>=0 && isint(${2=5}) && $2>=3" skip ${3=0}
7263  e[^-1] "Input a $1x$1 polygon binary shape, with $2 vertices and angle $3 deg."
7264  ir={round($1)}
7265  if !$ir 0
7266  else l[]
7267      (0;{2*pi}) + {($3-90)*pi/180} r. 1,{$2+1},1,1,3 rows. 0,{h-2}
7268      ir2={round($ir/2)}
7269      +sin. cos..
7270      a x n 0,{$ir-1} s x +.. {-2,0.5*($ir-im-iM)} +. {-1,0.5*($ir-im-iM)} a x
7271      $ir,$ir polygon. $2,{-2,^},1,1 rm..
7272    endl
7273  fi
7274  nm. "[2D $2-polygon shape]"
7275
7276#@cli shape_snowflake : size>=0,0<=_nb_recursions<=6
7277#@cli : Input a 2D snowflake binary shape with specified size.
7278#@cli : Default values: 'size=512' and 'nb_recursions=5'.
7279#@cli : $ repeat 6 shape_snowflake 256,$> done
7280shape_snowflake : check "${1=512}>=0 && isint(${2=5}) && $2>=0 && $2<=6"
7281  e[^-1] "Input a $1x$1 snowflake binary shape, with $2 recursions."
7282  ir={round($1)}
7283  if !$ir 0
7284  else l[]
7285    $ir,$ir (0;120;240) *. {pi/180} +sin. cos.. a[-2,-1] c
7286    repeat $2
7287      1,{4*h},1,2
7288      f.. "
7289        p0 = I;
7290        p1 = I[(y+1)%h];
7291        t = (p1 - p0)/3;
7292        pm = (p0 + p1)/2 - 0.866*[ -t[1], t[0] ];
7293        k = 4*y;
7294        I[#-1,k++] = p0;
7295        I[#-1,k++] = p0 + t;
7296        I[#-1,k++] = pm;
7297        I[#-1,k] = p1 - t;
7298      "
7299      rm..
7300    done
7301    *. {0.5*$1} +. {$1/2} permute. cyzx polygon.. {h},{^},1,1 rm.
7302  endl fi
7303  nm. "[2D snowflake shape]"
7304
7305#@cli shape_star : _size>=0,_nb_branches>0,0<=_thickness<=1
7306#@cli : Input a 2D star binary shape with specified size.
7307#@cli : Default values: 'size=512', 'nb_branches=5' and 'thickness=0.38'.
7308#@cli : $ repeat 9 shape_star 256,{$>+2} done
7309shape_star : check "${1=512}>=0 && ${2=5}>0 && ${3=0.5}>=0 && $3<=1"
7310  e[^-1] "Input a $1x$1 star binary shape, with $2 branches and thickness $3."
7311  ir={round($1)}
7312  if !$ir 0
7313  else l[]
7314      star3d $2,$3 col3d 1 c3d n3d *3d $1,$1
7315      $1,$1 j3d. ..,50%,50%,0,1,2 rm..
7316    endl
7317  fi
7318  nm. "[2D star shape]"
7319
7320#@cli sh : eq. to 'shared'. : (+)
7321
7322#@cli shared : x0[%],x1[%],y[%],z[%],c[%] : y0[%],y1[%],z[%],c[%] : z0[%],z1[%],c[%] : c0[%],c1[%] : c0[%] : \
7323# (no arg) : (+)
7324#@cli : Insert shared buffers from (opt. points/rows/planes/channels of) selected images.
7325#@cli : Shared buffers cannot be returned by a command, nor a local environment.
7326#@cli : (eq. to 'sh').
7327#@cli : $ image.jpg shared 1 blur[-1] 3 remove[-1]
7328#@cli : $ image.jpg repeat s shared 25%,75%,0,$> mirror[-1] x remove[-1] done
7329#@cli : $$ https://gmic.eu/oldtutorial/_shared
7330
7331#@cli sp : eq. to 'sample'.
7332sp : skip "${1=?}",${2=0}
7333  v + _sample[] "$1",${2--1} v -
7334  if !${} noarg fi
7335
7336#@cli sample : _name1={ ? | apples | balloons | barbara | boats | bottles | butterfly | cameraman | car | cat | \
7337# cliff | chick | colorful | david | dog | duck | eagle | elephant | earth | flower | fruits | gmicky | \
7338# gmicky_mahvin | gmicky_wilber | greece | gummy | house | inside | landscape | leaf | lena | leno | lion | \
7339# mandrill | monalisa | monkey | parrots | pencils | peppers | portrait0 | portrait1 | portrait2 | portrait3 | \
7340# portrait4 | portrait5 | portrait6 | portrait7 | portrait8 | portrait9 | roddy | rooster | rose | square | swan | \
7341# teddy | tiger | tulips | wall | waterfall | zelda },_name2,...,_nameN,_width={ >=0 | 0 (auto) },\
7342# _height = { >=0 | 0 (auto) } : (no arg)
7343#@cli : Input a new sample RGB image (opt. with specified size).
7344#@cli : (eq. to 'sp').\n
7345#@cli : Argument 'name' can be replaced by an integer which serves as a sample index.
7346#@cli : $ repeat 6 sample done
7347sample : skip "${1=?}",${2=0}
7348  v + _sample[] "$1",${2--1} v -
7349  if !${} noarg fi
7350
7351__sample :
7352  u apples,balloons,barbara,boats,bottles,butterfly,cameraman,car,cat,chick,cliff,colorful,\
7353    david,dog,duck,eagle,elephant,earth,flower,fruits,gmicky,gmicky_mahvin,gmicky_wilber,greece,gummy,house,\
7354    inside,landscape,leaf,lena,leno,lion,mandrill,monalisa,monkey,parrots,pencils,peppers,\
7355    portrait0,portrait1,portrait2,portrait3,portrait4,portrait5,portrait6,portrait7,portrait8,portrait9,\
7356    roddy,rooster,rose,square,swan,teddy,tiger,tulips,wall,waterfall,zelda
7357
7358_sample :
7359  l[]
7360    if "$#>=3 && isnum($-2) && isint($-2) && $-2>=0 && isnum($-1) && isint($-1) && $-1>=0" # W and H specified.
7361      N={$#-2} W=$-2 H=$-1
7362    elif "$#>=2 && isnum($-1) && isint($-1) && $-1>=0" # Only W specified.
7363      N={$#-1} W=$-1 H=0
7364    else # No dimensions specified
7365      N={$#} W,H=0
7366    fi
7367  onfail N={$#} W,H=0
7368  endl
7369
7370  # Check validity of given arguments.
7371  $=arg
7372  samples=${-__sample}
7373  M=${"arg2var _sp_name",$samples}
7374  is_arg=1
7375  repeat $N
7376    arg=${arg{1+$>}} is_rand{1+$>}=0
7377    if ['$arg']=='?' arg={round(u(1,$M))} is_rand{1+$>}=1
7378    elif !isnum($arg)" || "!isint($arg)
7379      repeat $M name=${_sp_name{1+$>}}
7380        if '$name'!=0" && "'$name'==['$arg'] arg={1+$>} break fi
7381      done
7382    else arg={($arg%$M)+1} fi
7383    if !isnum($arg)" || "!isint($arg) is_arg=0 break fi
7384    arg{1+$>}=$arg
7385  done
7386  if !$is_arg N=1 W=0 H=0 arg1={round(u(1,$M))} fi
7387
7388  # Input sample images.
7389  repeat $N
7390    name=${_sp_name${arg{1+$>}}}
7391
7392    # Retrieve image data.
7393    filename=${-path_cache}sample_$name.png
7394    url=https://gmic.eu/img/sample_$name.png
7395    if isfile(['{/$filename}']) $filename
7396    else
7397      if ${is_rand{1+$>}} l[] $url o. $filename onfail testimage2d {m=max($W,$H);m>0?m:400} endl
7398      else $url o. $filename
7399      fi
7400    fi
7401
7402    # Resize to desired dimensions.
7403    if $W>0" && "$H==0 r2dx. $W round.
7404    elif $W==0" && "$H>0 r2dy. $H round.
7405    elif $W>0" && "$H>0
7406      if w/$W>h/$H r2dy. $H else r2dx. $W fi
7407      r. $W,$H,1,100%,0,0,0.5,0.5 round.
7408    fi
7409    nm. $name
7410    e[0--4] "Input sample image '"{n}"' (1 image "{w}x{h}x{d}x{s}")."
7411  done
7412  +. 0 u $is_arg
7413
7414#@cli srand : value : (no arg) : (+)
7415#@cli : Set random generator seed.
7416#@cli : If no argument is specified, a random value is used as the random generator seed.
7417
7418#@cli store : _is_compressed={ 0 | 1 },variable_name1,_variable_name2,... : (+)
7419#@cli : Store selected images into one or several named variables.
7420#@cli : Selected images are transferred to the variables, and are so removed from the image list.
7421#@cli : (except if the prepended variant of the command '+store[selection]' is used).
7422#@cli : If a single variable name is specified, all images of the selection are assigned
7423#@cli : to the named variable. Otherwise, there must be as many variable names as images
7424#@cli : in the selection, and each selected image is assigned to each specified named variable.
7425#@cli : Use command `input $variable_name` to bring the stored images back in the list.
7426#@cli : Default value: 'is_compressed=0'.
7427#@cli : $ sample eagle,earth store img1,img2 input $img2 $img1
7428#@cli : $$
7429
7430#@cli testimage2d : _width>0,_height>0,_spectrum>0
7431#@cli : Input a 2D synthetic image.
7432#@cli : Default values: 'width=512', 'height=width' and 'spectrum=3'.
7433#@cli : $ testimage2d 512
7434testimage2d : check "${1=512}>0 && ${2=$1}>0 && ${3=3}>0"
7435  e[^-1] "Input 2D synthetic image of size $1x$2x$3."
7436  Dmax2={0.15*min($1,$2)^2}
7437  $1,$2,1,$3,"
7438    X = x - w/2;
7439    Y = y - h/2;
7440    a = atan2(Y,X);
7441    if (X^2 + Y^2<="$Dmax2",255*abs(cos(c+200*(x/w-0.5)*(y/h-0.5))),850*(a%(0.1*(c+1))))"
7442  polygon. 4,20%,20%,60%,20%,70%,70%,35%,45%,0.9,0,255,0
7443  torus3d {$1/7},{$1/20} r3d. 0,1,1,-80 col3d. 128,200,255
7444  j3d.. .,30%,70%,0,1,5,0,0 rm. round. 1
7445  nm. "[2D test image]"
7446
7447#@cli um : eq. to 'uncommand'.
7448um :
7449  v + uncommand $* v -
7450
7451#@cli uncommand : command_name[,_command_name2,...] : * : (+)
7452#@cli : Discard definition of specified custom commands.
7453#@cli : Set argument to '*' for discarding all existing custom commands.
7454#@cli : (eq. to 'um').
7455
7456#@cli uniform_distribution : nb_levels>=1,spectrum>=1
7457#@cli : Input set of uniformly distributed spectrum-d points in [0,1]^spectrum.
7458#@cli : $ uniform_distribution 64,3 * 255 +distribution3d circles3d[-1] 10
7459uniform_distribution : check "isint($1) && $1>0 && isint($2) && $2>0"
7460  e[^1] "Input set of $1 uniformly distributed $2-d points in [0,1]^$2."
7461  n={round($1^(1/$2),1,1)}
7462  (0,1) r. $n,1,1,1,3
7463  repeat $2-1 +channels. 100% r. {$n*w},1,1,1,1 r.. .,1,1,100%,0,2 a[-2,-1] c done
7464  r. $1,1,1,$2,1 nm. "[uniform $2D distribution]"
7465
7466#@cli unserialize : : (+)
7467#@cli : Recreate lists of images from serialized image buffers, obtained with command 'serialize'.
7468
7469#@cli up : eq. to 'update'.
7470up :
7471  v + _update
7472
7473#@cli update
7474#@cli : Update commands from the latest definition file on the G'MIC server.
7475#@cli : (eq. to 'up').
7476update :
7477  v + _$0
7478
7479_update :
7480  out=${_path_rc}update$_version.gmic
7481  e[0--3] "Update command definition file '"{/$out}"' from the G\47MIC server."
7482  l[] cimgz:https://gmic.eu/update$_version.gmic
7483    if h>7" && "same([{^}],'#@gmic',6) ot $out fi
7484    rm
7485  onfail error[0--3] "Command 'update' : Unreachable update file."
7486  endl
7487  m $out
7488
7489# Function that inserts a new 'Update information' filter for a previous version number,
7490# passed as an argument.
7491update_deprecate : check "isint($1) && $1>0 && $1<999"
7492  e[^-1] "Deprecate filter updates for G'MIC version $1."
7493  l[]
7494  file=${_path_rc}update$1.gmic
7495  i cimgz:"https://gmic.eu/update$1.gmic",uchar
7496  +l. s +,{'"UPDATE"" INFORMATION"'} is_deprecated={$!>1} rm endl # Check if deprecation has not been already done.
7497  if $is_deprecated
7498    e[0--4] "  > Version $1 already deprecated -> Removing deprecation."
7499    off1={"ref(crop(),data); find(data,'\n#@gui !<b><i>&gt;&gt; UPDATE"" INFORMATION</i></b>')"}
7500    if $off1>0
7501      +rows $off1,100% rows[0] 0,{$off1-1}
7502      off2={"ref(crop(),data); find(data,'\n\n')"}
7503      if $off2>0
7504        rows. {$off2+2},100% a y
7505        o cimgz:$file,uchar
7506        _upload[] $file
7507        e[0--6] "  > Done! Version $1 is not deprecated anymore."
7508      fi
7509    fi
7510  else
7511    s +,{'"#@cli :: "'}
7512    i[1] ('"\n\
7513           \#@gui !<b><i>&gt;&gt; UPDATE"" INFORMATION</i></b> : _none_, _none_\n\
7514           \#@gui : note = note{\"A <b>new version</b> of the <i>G\47MIC</i> plug-in is available!\\n\\n\n\
7515           \#@gui : You are strongly encouraged to upgrade your version,
7516           by visiting our <i>Download page</i>:\\n\\n\"}\n\
7517           \#@gui : url = link{\"Visit G\47MIC Download Page\",\"https://gmic.eu/download.html\"}\n\
7518           \#@gui : note = note{\"\\nOf course, your plug-in will continue to work, but please note that we
7519           won\47t be able\n\
7520           \#@gui : to provide filter updates anymore for your current plug-in version.\\n\\n\n\
7521           \#@gui : Best regards,\\n\\n <i>The G\47MIC team.</i>\"}\n\n"')
7522    y[1] a y
7523    o cimgz:$file,uchar
7524    _upload[] $file
7525    e[0--4] "  > Done! Version $1 is now deprecated."
7526  fi
7527  rm endl
7528
7529# update_download_version.
7530# Update file versions on G'MIC download page, with specified version number.
7531# $1 = version number (e.g. '2.9.8').
7532update_download_version : check "
7533    is_digit(c)=(c>=_'0' && c<=_'9');
7534    ver = ['${1=undefined}'];
7535    size(ver)==5 && is_digit(ver[0]) && ver[1]==_'.' && is_digit(ver[2]) && ver[3]==_'.' && is_digit(ver[4])"
7536  e[^-1] "Replace version number on G'MIC download page to '$1'."
7537  rm filename=$HOME/work/src/gmic/html/download.html it[] $filename
7538  eval "
7539    is_digit(c) = (c>=_'0' && c<=_'9');
7540    ver = ['$1'];
7541    ref(crop(),data);
7542    p = stop = N = 0;
7543    while (!stop,
7544      p = find(data,'?dwl=gmic_',p);
7545      p<0?(stop=1):(
7546        str = data[p + 10,5];
7547        is_digit(str[0]) && str[1]==_'.' && is_digit(str[2]) && str[3]=='.' && is_digit(str[4])?(
7548          copy(data[p + 10],ver,size(ver));
7549          ++N;
7550        );
7551        ++p;
7552      );
7553    );
7554    p = find(data,'/source/gmic_',0);
7555    p>0?(
7556      str = data[p + 13,5];
7557      is_digit(str[0]) && str[1]==_'.' && is_digit(str[2]) && str[3]=='.' && is_digit(str[4])?(
7558        copy(data[p + 13],ver,size(ver));
7559        ++N;
7560      );
7561    );
7562    copy(i[0],data,size(data));
7563    echo('           -> ',N,' occurrences found.');
7564    "
7565  ot $filename
7566
7567# Generate images used in the G'MIC installer for Windows (InnoSetup).
7568update_instimg :
7569  sp gmicky,600
7570
7571  # Large image.
7572  +l
7573    r2dx 164
7574    0 t. "G'MIC-Qt for GIMP",0,0,96,1,1
7575    0 t. "(version "${-strver}")",0,0,64,1,1
7576    a[-2,-1] y,0.5 rows. -5,100%
7577    *. -1 n. 0,255 to_rgb. r2dx. {0,0.95*w}
7578    - 255 a y,0.5 + 255
7579    - 255 r 164,314,1,3,0,0,0.5,0.25 + 255
7580    o $HOME/work/src/gmic/resources/gmic_instimg.bmp
7581    rm
7582  endl
7583
7584  # Small image.
7585  r2dx 55 - 255 r 55,55,1,3,0,0,0.5,0.5 + 255
7586  o $HOME/work/src/gmic/resources/gmic_instimg_small.bmp
7587  rm
7588
7589# Generate Color Presets page for the G'MIC website.
7590# Input images : samples you want to generate
7591# $1 = upload to G'MIC server, can be { 0 | 1 }.
7592update_color_presets_html : check "isbool(${1=0})"
7593  use_vt100
7594  path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/color_presets/
7595  if ['$path_current']!=['$path_ok']
7596    error[0--3] "Command 'update_color_presets_html: Command run from wrong path: '"$path_current"', "\
7597                "should be '"$path_ok"'."
7598  fi
7599
7600  # Load default set of images if none selected.
7601  if !$!
7602    files ../img/color_presets_sample* files=${}
7603    repeat narg($files) arg 1+$>,$files ${} done
7604  fi
7605
7606  # Init variables.
7607  jpeg_quality=70   # Set JPEG quality of output images.
7608  thumb_width=180   # Set thumbnail width.
7609  thumb_height=90   # Set mini thumbnail height.
7610  categories=creative,picturefx,pixlsus,abigailgonzalez,alexjordan,berat,ericellerbrock,inavision,jtsemple,\
7611             kylerholland,ohadperetz,shamoonabbasi,youssefhossam,others,\
7612             bw,instant_consumer,instant_pro,fujixtransiii,negative_color,negative_new,negative_old,print,colorslide
7613  category_names="Creative Pack",\
7614                 "PictureFX",\
7615                 "PIXLS.US",\
7616                 "Abigail Gonzalez's CLUTs",\
7617                 "Alex Jordan's CLUTs",\
7618                 "Berat's CLUTs",\
7619                 "Eric Ellerbrock's CLUTs",\
7620                 "InAvision's CLUTs",\
7621                 "J.T. Semple's CLUTs",\
7622                 "Kyler Holland's CLUTs",\
7623                 "Ohad Peretz's CLUTs",\
7624                 "Shamoon Abbasi's CLUTs",\
7625                 "Youssef Hossam's CLUTs",\
7626                 "Others",\
7627                 "Films: Black and White",\
7628                 "Films: Instant [Consumer]",\
7629                 "Films: Instant [Pro]",\
7630                 "Films: Fuji XTrans III",\
7631                 "Films: Negative [Color]",\
7632                 "Films: Negative [New]",\
7633                 "Films: Negative [Old]",\
7634                 "Films: Print Films",\
7635                 "Films: Slide [Color]"
7636  category_authors="RawTherapee",\
7637                   "Marc Roovers",\
7638                   "PIXLS.US contributors",\
7639                   "Abigail Gonzalez",\
7640                   "Alex Jordan",\
7641                   "Berat",\
7642                   "Eric Ellerbrock",\
7643                   "InAvision",\
7644                   "J.T. Semple",\
7645                   "Kyler Holland",\
7646                   "Ohad Peretz",\
7647                   "Shamoon Abbasi",\
7648                   "Youssef Hossam",\
7649                   0,\
7650                   "Pat David",\
7651                   "Pat David",\
7652                   "Pat David",\
7653                   "Stuart Sowerby",\
7654                   "Pat David",\
7655                   "Pat David",\
7656                   "Pat David",\
7657                   "Juan Melara",\
7658                   "Pat David"
7659  category_authors_url="https://rawpedia.rawtherapee.com/Film_Simulation",\
7660                       "https://marcrphoto.wordpress.com/specials/698-2/",\
7661                       "https://discuss.pixls.us/t/help-to-create-a-set-of-pixls-us-color-luts",\
7662                       "https://www.abigailgonzalez.com/",\
7663                       "https://freshluts.com/users/1",\
7664                       "https://freshluts.com/users/10185",\
7665                       "https://www.facebook.com/eric.ellerbrockom",\
7666                       "https://freshluts.com/users/64078",\
7667                       "https://freshluts.com/users/120",\
7668                       "https://sellfy.com/p/SGnG/",\
7669                       "https://freshluts.com/users/992",\
7670                       "https://www.facebook.com/shamoon",\
7671                       "https://freshluts.com/users/52679",\
7672                       0,\
7673                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
7674                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
7675                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
7676                       "http://blog.sowerby.me/fuji-film-simulation-profiles",\
7677                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
7678                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
7679                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
7680                       "http://juanmelara.com.au/print-film-emulation-luts-for-download",\
7681                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html"
7682
7683  m "_thumb : frame 3%,3%,255 to_rgba drop_shadow 2,2,3 repeat $! l[$>] i[0] 100%,100%,1,3,245 blend alpha endl done"
7684  m "_title : ('\"$""*\"') replace_str. \"_\",\" \" replace_str. \"iii\",\"III\" f. \"(!y || j(0,-1)==32) &&
7685     i>=_'a' && i<=_'z'?uppercase(i):i\" u {t} rm."
7686
7687  nb_presets=0
7688  repeat narg($categories)
7689    category=${arg\ {1+$>},$categories}
7690    presets=${-_fx_cluts_$category}
7691    nb_presets+={narg({/$presets})}
7692  done
7693
7694  e[] "\n > Update 'Color Presets' pages, for "$!" image samples and "$nb_presets" presets."
7695
7696  # Prepare folder structure.
7697  e[] $_vt100_g"\n  * Prepare folder structure."$_vt100_n
7698  x "mkdir -p img"
7699
7700  # Generate thumbnails.
7701  e[] $_vt100_g"\n  * Generate thumbnails from samples.\n"$_vt100_n
7702  x "mkdir -p original"
7703  nb_samples=$!
7704  to_rgb repeat $nb_samples l[$<]
7705    nm[0] sample_{1+$<}
7706    basename={0,b} basename$<=$basename
7707    e[] "    - "$basename
7708    +r2dx $thumb_width
7709    +to[0] "Reference",4,{0,h-28},20,2 frame. 1,1,0
7710    o. original/$basename.jpg,$jpeg_quality rm.
7711    +_thumb[1] o. original/thumb_$basename.jpg,$jpeg_quality rm.
7712    +r2dy[0] $thumb_height r. {h},{h},1,100%,0,0,0.5,0.5 _thumb.
7713    o. original/minithumb_$basename.jpg,$jpeg_quality rm.
7714  endl done
7715
7716  # Generate color grading data and rendering on each input sample.
7717  ind_preset=0
7718  repeat narg($categories)
7719    category=${arg\ {1+$>},$categories}
7720    presets=${-_fx_cluts_$category}
7721
7722    e[] $_vt100_g"\n  * Category ""#"{1+$>}": "$_vt100_b$category"\n"$_vt100_n
7723    x "mkdir -p "$category
7724    x "mkdir -p "$category/clut
7725
7726    repeat narg({/$presets})
7727      preset=${arg\ {1+$>},$presets}
7728      e[] "    - "$preset
7729      clut $preset,64 to_rgb.
7730      s={sqrt(w*h*d)} +r. $s,$s,1,3,-1
7731      if iM<=255 *. 257 fi  # Force PNG to be saved in 16bits.
7732      o. $category/clut/$preset.png # Save as HaldCLUT
7733      rm.
7734
7735      +r3dx. 13
7736      o. $category/clut/$preset.cube # Save as .cube
7737      x 0,"rm -f "$category/clut/$preset.zip
7738      x 0,"zip -j -9 "$category/clut/$preset.zip" "$category/clut/$preset.cube
7739      rm.
7740
7741      repeat $nb_samples l[{2*$>},{2*$>+1},-1]
7742        basename={0,b}
7743        if !isfile(['{/$category/$basename/$preset.jpg}'])
7744          x "mkdir -p \""$category/$basename"\""
7745          +map_clut[^-1] .
7746          _title $preset
7747          to.. ${},4,{-2,h-28},20,2 frame.. 1,1,0
7748          o.. $category/$basename/$preset.jpg,$jpeg_quality
7749          _thumb. o. $category/$basename/thumb_$preset.jpg,$jpeg_quality
7750          rm[-2,-1]
7751        fi
7752      endl done
7753
7754      rm.
7755
7756      ind_preset+=1
7757    done
7758    x 0,"zip -rj9 "$category/${category}_cube.zip" "$category/clut/*.cube
7759    x 0,"zip -rj9 "$category/${category}_haldclut.zip" "$category/clut/*.png
7760  done
7761
7762  rm[1--1:2]  # Remove thumbnails.
7763
7764  # Generate html gallery code.
7765  e[] "\n  * Generate html code.\n"
7766
7767  repeat narg($categories)
7768    category=${arg\ {1+$>},$categories}
7769    arg {1+$>},$category_names
7770    category_name=${}
7771    presets=${-_fx_cluts_$category}
7772    x 0,"rm -f "$category/clut/"*.cube"
7773
7774    repeat $nb_samples
7775      width={$>,64+w} height={$>,64+h}
7776      basename={$>,b}
7777      e[] "    - "$category_name" / "$basename
7778
7779      ('"<!DOCTYPE html>"\n\
7780"<html lang=\"en\">"\n\
7781"  <head>"\n\
7782"    <meta charset=\"utf-8\">"\n\
7783"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
7784"    <link rel=\"stylesheet\" href=\"../highslide/highslide.css\"/>"\n\
7785"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
7786"- Color Presets</title>"\n\
7787"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
7788"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
7789"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
7790"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
7791"    <script src=\"../highslide/highslide-full.js\"></script>"\n\
7792"    <script>"\n\
7793"      hs.graphicsDir = '../highslide/graphics/';"\n\
7794"      hs.wrapperClassName = 'wide-border';"\n\
7795"      hs.showCredits = 'false';"\n\
7796"    </script>"\n\
7797"  </head>"\n\n\
7798"  <body>"\n\
7799"    <div id=\"include_header\"></div>"\n\n\
7800"    <div class=\"section_title\"><p>Color Presets</p></div><div class=\"section_content\">"\n\n\
7801"      <p>Among all features available in <span class=\"gmd_gmic\">G&apos;MIC</span>, our <b>Color Presets</b> and "\
7802"<b>Simulate Film</b> filters are able to apply various pre-defined <b>color CLUTs</b> on your images "\
7803"(<b>600+</b> color CLUTs available).</p>"\n\
7804"      <p>Below, you can navigate through the different proposed presets and see how they modify the colors "\
7805"of some sample images."\n\
7806"        You can also download each color preset separately as its corresponding "\
7807"<a href=\"http://www.quelsolaar.com/technology/clut.html\">HaldCLUT</a> file (in <b>.png</b> format), to use it in "\
7808" other software that support this feature.</p>"\n\n\
7809"      <p><b>Image credits:</b> Sample images below have been borrowed from "\
7810"<a href=\"https://www.flickr.com/photos/patdavid/\">Patrick David</a>, "\
7811"<a href=\"https://www.flickr.com/photos/davelau/\">Chi King</a>, "\
7812"<a href=\"https://pixabay.com/en/tulips-tulip-field-tulpenbluete-3359902/\">Capri23auto</a> "\
7813"and <a href=\"https://pixabay.com/en/girl-face-colorful-colors-artistic-2696947/\">ivanovgood</a>,"\n\
7814"        distributed a minima under "\
7815"<a href=\"https://creativecommons.org/licenses/by-sa/2.0/\">CC-by-SA 2.0</a>.</p>"\n\n\
7816"      <p class=\"colorpresets_disclaimer\"><b>Disclaimer:</b><br/>"\n\
7817"        The trademarked names which may appear in the filenames of the HaldCLUT images are there for "\
7818"informational purposes only. They serve only to inform the user which film stock the given HaldCLUT image"\n\
7819"        is designed to approximate. As there is no way to convey this information other than by using the "\
7820"trademarked name, we believe this constitutes fair use. Neither the publisher nor the authors are affiliated"\n\
7821"        with or endorsed by the companies that own the trademarks.</p>"\n\n\
7822"<!-- end_content -->"\n\n\
7823"    </div><div class=\"section_end\"></div>"\n\n\
7824"    <a name="browse"></a><div class=\"section_title\"><p>Browse</p></div><div class=\"section_content\">"\n\n\
7825"      <table><tr>"\n\
7826"          <td style=\"width: 400px\">Select category:"\n\
7827"            <ul>\n"')
7828      repeat narg($categories)
7829        _category=${arg\ {1+$>},$categories}
7830        arg {1+$>},$category_names
7831        _category_name=${}
7832
7833        arg {1+$>},$category_authors_url
7834        if isnum(${}) _category_author_url_start= _category_author_url_end=
7835        else _category_author_url_start="<a href=\""${}"\">" _category_author_url_end="</a>"
7836        fi
7837
7838        arg {1+$>},$category_authors
7839        if isnum(${}) _category_author=""
7840        else _category_author=$_category_author_url_start${}$_category_author_url_end
7841        fi
7842
7843        if narg($_category_author)
7844          _category_author="<span class=\"smaller\">(by "$_category_author")</span>"
7845        fi
7846
7847        if ['$_category']==['$category']
7848          ('"              <li><span class=\"gmd_bold_a\"><b>"$_category_name"</b> "\
7849            $_category_author"</span></li>"\n')
7850        else
7851          ('"              <li><a href=\""${_category}_$basename.html"#browse\">"$_category_name"</a> "\
7852            $_category_author"</li>\n"')
7853        fi
7854      done
7855
7856      ('"            </ul>\n"\
7857        "          </td>"\n\
7858        "          <td>Select sample image:<br/><br/>\n"')
7859      repeat $nb_samples
7860        _basename=${basename$>}
7861        if ['$_basename']==['$basename']
7862          ('"            <a href=\""${category}_$_basename.html"#browse\">"\
7863            "<img alt=\"\" style=\"border: 3px solid \#cc7700\" "\
7864            "src=\""original/minithumb_$_basename.jpg"\"/></a>\n"')
7865        else
7866          ('"            <a href=\""${category}_$_basename.html"#browse\"><img alt=\"\" "\
7867            "src=\""original/minithumb_$_basename.jpg"\"/></a>\n"')
7868        fi
7869      done
7870      ('"          </td>"\n\
7871        "      </tr></table>"\n\
7872        "    </div><div class=\"section_end\"></div>"\n\n')
7873      ('"    <div class=\"section_title\"><p>Presets</p></div><div class=\"section_content\">"\n\n\
7874        "      <table class=\"colorpresets_table\">"\n\
7875        "        <tr><td>"\n\
7876        "            <div><a href=\"original/"$basename".jpg\" class=\"highslide\" "\
7877        "onclick=\"return hs.expand(this)\"><img alt=\"\" "\
7878        "src=\"original/thumb_"$basename".jpg\" /></a>"\n\
7879        "              <div class=\"highslide-caption\"><b>Reference Image</b></div></div>"\n\
7880        "        </td><td colspan=\"3\"></td></tr>"\n\
7881        "        <tr><td><b>Reference Image</b></td><td colspan=\"3\"></td></tr>"\n\n')
7882
7883      repeat narg({/$presets})
7884        preset=${arg\ {1+$>},$presets}
7885
7886        ('$preset') replace_seq. 39,"92,92,39" preset_esc={t} rm.
7887        _title $preset title=${}
7888        if $>%4==0 if $> ('"        </tr>\n"') fi ('"        <tr>\n"') fi
7889
7890        ('"          <td><table>"\n\
7891          "              <tr><td>"\n\
7892          "                  <div><a href=\"#\" onclick=\"return hs.htmlExpand(this, "\
7893          "{ width: "$width", height: "$height" })\">"\n\
7894          "                      <img alt=\"\" src=\""$category/$basename/thumb_$preset".jpg\" /></a>"\n\
7895          "                    <div class=\"highslide-maincontent\" style=\"text-align: center\">"\n\
7896          "                      <img alt=\"\" src=\""$category/$basename/$preset.jpg"\" "\
7897          "onclick=\"hs.close()\" "\
7898          "onmouseover=\"javascript:this.src='"$category/$basename/$preset_esc.jpg"';\" "\
7899          "onmouseout=\"javascript:this.src='original/"$basename".jpg';\" /></div>"\n\
7900          "                    <div class=\"highslide-caption\"><b>Preset:</b> <i>"$title"</i> "\
7901          "[<a href=\""$category/clut/$preset.png"\">Download .png HaldCLUT</a>] "\
7902          "or [<a href=\""$category/clut/$preset.zip"\">Download .cube</a>]"\
7903          "</div></div>"\n\
7904          "              </td></tr>"\n\
7905          "              <tr><td><span class=\"smaller\">"$title"</span></td></tr>"\n\
7906          "          </table></td>\n\n"')
7907      done
7908      r={narg({/$presets})%4}
7909      if $r%4 ('"          <td colspan=\""{4-$r}"\"></td>\n"') fi
7910      ('"      </tr></table>"\n\n\
7911        "      <hr/><p style=\"text-align: center\">"\n\
7912        "        <a href=\""$category/${category}_cube.zip"\"><img src=\"../img/menu_download.png\" /></a>&nbsp; "\
7913        "<span class=\"gmd_bold_a\">Get the Full Pack &quot;<i>"${category_name}"</i>&quot;:</span> "\
7914        "[<b><a href=\""$category/${category}_haldclut.zip"\">Download .png HaldCLUT</a></b>] "\
7915        "or [<b><a href=\""$category/${category}_cube.zip"\">Download .cube</a></b>]"\
7916        "&nbsp; <a href=\""$category/${category}_cube.zip"\"><img src=\"../img/menu_download.png\" /></a>"\n\
7917        "      </p><hr/>"\n\
7918        "<!-- end_content -->"\n\n\
7919        "    </div><div class=\"section_end\"></div>"\n\
7920        "    <div id=\"include_footer\"></div>"\n\
7921        "  </body>"')
7922      a[$nb_samples--1] x ot. ${category}_$basename.html rm.
7923    done
7924
7925  done
7926
7927  # All done, exiting.
7928  rm
7929  category=${arg\ 1,$categories}
7930  x "ln -fs "${category}_$basename0.html" index.html"
7931  if $1
7932    e[] "\n  * Transfer color presets on G'MIC server.\n"
7933    x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -eRL . /www/gmic/color_presets ; quit\""
7934  fi
7935  e[] "\n > All done, for "$nb_presets" presets.\n"
7936
7937# Generate gallery page for the G'MIC website.
7938# $1 = upload to G'MIC server, can be { 0 | 1 }.
7939update_gallery_html : check "isbool(${1=0})"
7940  e[^-1] "Generate gallery pages for the G'MIC website."
7941  use_vt100
7942  path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/gallery/
7943  if ['$path_current']!=['$path_ok']
7944    error[0--3] "Command 'update_gallery_html: Command run from wrong path: '"$path_current"', "\
7945                "should be '"$path_ok"'."
7946  fi
7947  x "rm -rf img && mkdir -p img"
7948
7949  # Define categories and sample pictures.
7950  categories=arrays,artistic,blackandwhite,colors,deformations,filtering,patterns,3dmeshes,stylization,codesamples
7951  nb_categories={narg($categories)}
7952  commands_arrays=_gallery_arrays,array,array_fade,array_mirror,frame_blur,frame_cube,frame_painting,img2ascii,\
7953                  imagegrid,rotate_tiles,vignette,tunnel
7954  commands_artistic=_gallery_artistic,boxfitting,cartoon,cubism,draw_whirl,fractalize,halftone,sketchbw,light_relief,\
7955                    mosaic,linify,polaroid,polygonize,poster_hope,rodilius,stencil,stained_glass
7956  commands_blackandwhite=_gallery_blackandwhite,pencilbw,old_photo,sepia
7957  commands_colors=_gallery_colors,solarize,hsv2rgb,transfer_rgb
7958  commands_deformations=_gallery_deformations,deform,map_sphere,seamcarve,spherize,twirl,warp_perspective,water,wave
7959  commands_filtering=_gallery_filtering,blur_angular,blur_linear,blur_radial,glow,smooth,bilateral,dog,deriche,\
7960                     distance,gradient_norm,normalize_local,sharpen,solidify
7961  commands_patterns=_gallery_patterns,tsp,chessboard,mandelbrot,maze_mask,plasma,shape_fern,shape_snowflake,\
7962                    sierpinski,syntexturize_matchpatch,truchet,turbulence,watermark_visible,weave
7963  commands_3dmeshes=_gallery_3dmeshes,add3d,distribution3d,elevation3d,gmic3d,imageblocks3d,imagerubik3d,\
7964                    skeleton3d,spherical3d,tensors3d,text3d,torus3d,weird3d
7965  commands_stylization=_gallery_stylization
7966  commands_codesamples=_gallery_codesamples
7967
7968  pics=apples,bottles,butterfly,car,cat,cliff,david,dog,duck,eagle,elephant,earth,flower,fruits,greece,gummy,inside,\
7969       landscape,leaf,leno,lion,mandrill,monalisa,parrots,pencils,rooster,rose,square,teddy,tiger,wall,waterfall,zelda
7970
7971  # Generate html pages.
7972  it[] $HOME/work/src/gmic/src/gmic_stdlib.gmic
7973  merge_multiline_comments. s -,10
7974  ('\n') a[0--3] .,y rm. a y
7975  nb_cols=3
7976
7977  repeat $nb_categories,ncat
7978    category=${arg\ 1+$>,$categories}
7979    if isfile('{/$category.html}') continue fi
7980    e[] "\n  * Process category '"$_vt100_b$category$_vt100_n"'."
7981
7982    commands=${commands_$category}
7983    nb_commands={narg($commands)}
7984    col,nex=0
7985
7986    repeat $nb_categories
7987      cat=${arg\ 1+$>,$categories}
7988      if '$cat'=='$category' td_$cat="<td style=\"background-color: \#dddddd\">"
7989      else td_$cat="<td>"
7990      fi
7991    done
7992
7993  html="<!DOCTYPE html>"\n\
7994"<html lang=\"en\">"\n\
7995"  <head>"\n\
7996"    <meta charset=\"utf-8\">"\n\
7997"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
7998"    <link rel=\"stylesheet\" href=\"../highslide/highslide.css\"/>"\n\
7999"    <link rel=\"stylesheet\" href=\"https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css\">"\n\n\
8000"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
8001"- Gallery</title>"\n\
8002"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
8003"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
8004"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
8005"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
8006"    <script src=\"https://code.jquery.com/jquery-1.12.4.js\"></script>"\n\
8007"    <script src=\"https://code.jquery.com/ui/1.12.1/jquery-ui.js\"></script>"\n\n\
8008"    <script src=\"../highslide/highslide-full.js\"></script>"\n\
8009"    <script>"\n\
8010"      hs.graphicsDir = '../highslide/graphics/';"\n\
8011"      hs.wrapperClassName = 'wide-border';"\n\
8012"      hs.showCredits = 'false';"\n\
8013"    </script>"\n\
8014"  </head>"\n\n\
8015"  <body>"\n\
8016"    <div id=\"include_header\"></div>"\n\n\
8017"    <div class=\"section_title\"><p>Image Gallery</p></div><div class=\"section_content\">"\n\n\
8018"      <p>This gallery gives a quick overview of the kind of features and generic filters available in the "\
8019"<span class=\"gmd_gmic\">G&apos;MIC</span> open-source image processing framework.</p>"\n\
8020"      <p>All the images below have been processed by the CLI interface "\
8021"<samp><a href=\"reference/\">gmic</a></samp>"\
8022"  of G&apos;MIC, from a set of initial 2D color images."\n\
8023"        Click on an image to enlarge it and display the G&apos;MIC command-line "\
8024"used for the processing (<i>note: to reproduce this, you may have to escape some characters, "\
8025"according to the type of shell you use!</i>).</p>"\n\
8026"      <p>Remember, G&apos;MIC lets you define your own image pipelines through"\
8027"custom <a href=\"https://gmic.eu/gmic_stdlib.gmic\">command files</a>."\n\
8028"        Your custom filters can be easily added afterwards in the plug-in for "\
8029"<a href=\"http://www.gimp.org\">GIMP</a> or <a href=\"http://www.krita.org\">Krita</a>.</p>"\n\
8030"      <p>For more details, visit the <a href=\"https://gmic.eu/tutorial\">tutorial pages</a> as well as the "\
8031"<a href=\"https://gmic.eu/reference/\">technical reference</a> to get a full documentation on this "\
8032"software.</p>"\n
8033
8034  html_menu="      <table class=\"gallery_navigation\"><tr>\n          "\
8035            $td_arrays"<a href=\"arrays.html#menu\">Arrays &amp; Frames</a></td>"\
8036            $td_artistic"<a href=\"artistic.html#menu\">Artistic</a></td>"\
8037            $td_blackandwhite"<a href=\"blackandwhite.html#menu\">B&amp;W</a></td>"\
8038            $td_colors"<a href=\"colors.html#menu\">Colors</a></td>"\
8039            $td_deformations"<a href=\"deformations.html#menu\">Deformations</a></td>"\
8040            $td_filtering"<a href=\"filtering.html#menu\">Filtering</a></td>"\
8041            $td_patterns"<a href=\"patterns.html#menu\">Patterns</a></td>"\
8042            $td_3dmeshes"<a href=\"3dmeshes.html#menu\">3D Meshes</a></td>"\
8043            $td_stylization"<a href=\"stylization.html#menu\">Stylization</a></td>"\
8044            $td_codesamples"<a href=\"codesamples.html#menu\">Code samples</a></td>"\n\
8045            "      </tr></table><br/>\n\n"
8046
8047  html=${html}${html_menu}"      <table class=\"gallery_table\">\n"
8048
8049  repeat $nb_commands
8050    command=${"arg "1+$>,$commands}
8051      is_stylization={['$command']=='_gallery_stylization'}
8052
8053      e[] $_vt100_m"    - Command '"$_vt100_b$command$_vt100_n"' ["{1+$>}/$nb_commands"]."
8054      +l # Parse command definition and examples
8055        s -,{'"#@cli "$command" :"'}
8056        if $!<2 s -,{'"#@cli "$command"\n"'} i[1] ('\n') a[-2,-1] y fi
8057        if $!<2 warn[] "    ** Command '"$command"' not found! **"
8058        else
8059          k.
8060          eval "
8061            ref(crop(#-1),str);
8062            ind = 0;
8063            while ((ind = find(str,'\n#@cli ',ind))>=0,
8064              ++ind;
8065              str[ind+6]!=_':' ? break() :
8066              str[ind+7]==_' ' && str[ind+8]==_'$' && str[ind+9]==_' '?(
8067                beg = ind + 10;
8068                end = find(str,_'\n',beg) - 1;
8069                ref(vector1024(0),com);
8070                run('+rows[0] ',beg,',',end);
8071              );
8072            );
8073          ";
8074          rm[0]
8075
8076          nb_examples=$!
8077          repeat $nb_examples # For each example found
8078            example$nex={$>,t}
8079
8080            e[] $_vt100_g"        $ "${example$nex}$_vt100_n
8081            sample=${"arg 1+"{($nex+8*$ncat)%narg($pics)},$pics}
8082            is_codesample=0
8083
8084            l[] # Create a html representation of the command
8085              ('${example$nex}') y
8086              is_input={"find(#-1,'image.jpg')>=0"}
8087
8088              if !$is_input" && find(#-1,'sample ')>=0"
8089                 s +,{'"sample "'}
8090                 if {0,crop()=='"sample "'" && "$!>1} l[1]
8091                   s +,{'" "'}
8092                   if "find(#0,_',')==-1" sample={0,t} is_input=1 fi
8093                   a y
8094                 endl fi
8095                 a y
8096              fi
8097              replace_str. "image.jpg",\
8098                           "<a href=\"../img/sample_"$sample".png\" target=\"_blank\">"$sample".png</a>"
8099              replace_str. "_output_mode=1",""
8100              # Discard '_fps=?' and '_label=?'
8101              l. s +,{'" "'} repeat $! if {$<,crop(0,0,1,5)=='_fps='||crop(0,0,1,7)=='_label='}
8102                rm[$<]
8103              fi done a y endl
8104              l. s +,{'" "'} repeat $! if {$<,crop(0,0,1,8)=='https://'} l[$<] # Add hyperlink
8105                if crop(0,0,1,24)=='https://gmic.eu/samples/'
8106                  is_codesample=1
8107                  basename_codesample={`crop(0,24,1,h-24)`}
8108                  filename_codesample="../../resources/samples"/$basename_codesample
8109                  url_codesample="https://gmic.eu/samples/"$basename_codesample
8110                  if $1 x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"put -O /www/gmic/samples \\\""\
8111                          $filename_codesample"\\\"; quit\" >/dev/null" fi
8112                  i[0] ('"<a onclick=\"javascript:$( '\#dialog"$nex"' ).dialog( 'open' );\"
8113                          style=\"cursor: pointer;\">"')
8114                else i[0] ('"<a href=\""{$<,t}"\" target=\"_blank\">"')
8115                fi
8116                ('</a>') y a y
8117              endl fi done a y endl
8118              replaced_example$nex={t} rm
8119            endl
8120            ('${example$nex}') replace_str. "https://gmic.eu/samples/","../../resources/samples/" example$nex={t} rm.
8121            m "_run : _preview_width,_preview_height=450,300 "${example$nex}
8122
8123            if $is_input sample_=${sample}_ else sample_= fi
8124            filename_original=img/${category}_${sample_}original_$nex.jpg
8125            filename_thumb_original=img/${category}_${sample_}thumb_original_$nex.jpg
8126            if "find(['"${example$nex}"'],' _fps=')>=0"
8127              filename_full=img/${category}_${sample_}full_$nex.gif
8128              filename_thumb=img/${category}_${sample_}thumb_$nex.gif
8129              _is_animated=1
8130            else
8131              filename_full=img/${category}_${sample_}full_$nex.jpg
8132              filename_thumb=img/${category}_${sample_}thumb_$nex.jpg
8133              _is_animated=0
8134            fi
8135
8136            etime=
8137            _label=
8138            if !isfile('{/$filename_thumb}') l[]
8139              if $is_input sp $sample,600 o image.jpg rm fi
8140
8141              db3d m3d md3d f3d l3d sl3d ss3d 0.8 srand 512
8142              etime=$| _run etime={_round($|-$etime,0.01)}
8143
8144              if $is_stylization
8145                +l _gallery o $filename_full,70 rm endl k.
8146                _gallery
8147                width,height={[w,h]}
8148              else
8149                _gallery
8150                width,height={[w,h]}
8151                if $_is_animated o $filename_full,$_fps else o $filename_full,70 fi
8152              fi
8153              e[] "\r"$_vt100_g"        $ "${example$nex}" (done in "$_vt100_n${etime}"s"$_vt100_g")."$_vt100_n
8154
8155              crop 5,5,{w-6},{h-6} # Remove frame added by '_gallery'
8156              frame 3%,3%,255 rr2d 440,440,0,3 drop_shadow 2,2,2
8157              if $_is_animated rr2d 230,230,0,3 else rr2d 300,300,0,3 fi
8158              100%,100%,1,3,245 blend[^-1] .,alpha,1,1 rm.
8159              if $_is_animated o $filename_thumb,$_fps else o $filename_thumb,60 fi
8160              rm
8161
8162              if $is_input
8163                image.jpg _gallery
8164                rr2d $width,$height,0,5 c 0,255
8165                to "Input",2%,2%,6%
8166                - 255 r $width,$height,1,3,0,0,0.5,0.5 + 255
8167                o $filename_original,60
8168
8169                crop 5,5,{w-6},{h-6} # Remove frame added by '_gallery'
8170                frame 3%,3%,255 rr2d 440,440,0,3 drop_shadow 2,2,2
8171                if $_is_animated rr2d 230,230,0,3 else rr2d 300,300,0,3 fi
8172                100%,100%,1,3,245 blend[^-1] .,alpha,1,1 rm.
8173                o $filename_thumb_original,60
8174                rm
8175              fi
8176            endl fi
8177
8178            is_samesize=0
8179            l[]
8180              $filename_full
8181              width,height={round([w,h]*(max(w,h)<300?1.75:1))}
8182              if isfile(['{/${filename}_original}']) $filename_original is_samesize={w==w#0" && "h==h#0} fi
8183              rm
8184            endl
8185            if !$is_samesize filename_original=$filename_full fi
8186            if !($col%$nb_cols) html=${html}"        <tr>\n" fi
8187            if $nb_examples==1 counter=
8188            else counter=" <span class=\"gallery_caption\">"[{$>+1}/$nb_examples]"</span>"
8189            fi
8190            html_etime=
8191            if narg($etime) html_etime="<br/><span class=\"gallery_caption\">(generated in "${etime}"s)</span>" fi
8192
8193            html_codesample=
8194            if $is_codesample
8195               html_codesample="\n                  <div id=\"dialog"$nex"\" title=\""samples/$basename_codesample"\">"\
8196                               "\n                    <pre class>\n"
8197               it[] $url_codesample html_codesample=${html_codesample}{t} rm.
8198               html_codesample=${html_codesample}"\n</pre></div>\n"\
8199                               "<br/><script>$( function() { $( \"#dialog"$nex"\" )."\
8200                               "dialog({ autoOpen: false, width:600 }); } );</script>\n
8201                                <a onclick=\"javascript:$( '#dialog"$nex"' ).dialog( 'open' );\"
8202                                style=\"cursor: pointer;\"><span class=\"gallery_caption\">[ Source code ]</p></a>\n"
8203            fi
8204            if ['$_label']==0 _label=$command$counter else ('$_label') replace_str. "~"," " _label={t} rm. fi
8205            if $is_input
8206              html=${html}\
8207                  "          <td><div><a href=\"#\" onclick=\"return hs.htmlExpand(this, { width: "{$width+64}","\
8208                  "height: "{$height+64}" })\">\n"\
8209                  "                <img alt=\""gallery_$command$nex"\" src=\""$filename_thumb"\" "\
8210                  "onmouseover=\"javascript:this.src='"$filename_thumb_original"';\"
8211                  onmouseout=\"javascript:this.src='"$filename_thumb"';\"/><br/><b>"\
8212                  ${_label}${html_codesample}"</b></a>\n"\
8213                  "              <div class=\"highslide-maincontent\">\n"\
8214                  "                <img alt=\""gallery_$command$nex"\" width=\""$width"\" "\
8215                  "src=\""$filename_full"\"
8216                  onclick=\"hs.close()\" onmouseover=\"javascript:this.src='"$filename_full"';\"
8217                  onmouseout=\"javascript:this.src='"$filename_original"';\"/>\n"\
8218                  "              </div>\n"\
8219                  "              <div class=\"highslide-caption\">Command: "\
8220                  "<samp>$ gmic "${replaced_example$nex}"</samp>"\
8221                  ${html_etime}"</div></div></td>\n"
8222            else
8223              html=${html}\
8224                  "          <td><div><a href=\"#\" onclick=\"return hs.htmlExpand(this, { width: "{$width+64}","\
8225                  "height: "{$height+64}" })\">\n"\
8226                  "                <img alt=\""gallery_$command$nex"\" src=\""$filename_thumb"\" />"\
8227                  "<br/><b>"\
8228                  ${_label}${html_codesample}"</b></a>\n"\
8229                  "              <div class=\"highslide-maincontent\">\n"\
8230                  "                <img alt=\""gallery_$command$nex"\" width=\""$width"\" "\
8231                  "src=\""$filename_full"\" onclick=\"hs.close()\" />\n"\
8232                  "              </div>\n"\
8233                  "              <div class=\"highslide-caption\">Command: "\
8234                  "<samp>$ gmic "${replaced_example$nex}"</samp>"\
8235                  ${html_etime}"</div></div></td>\n"
8236            fi
8237            nex+=1
8238            col={($col+1)%$nb_cols}
8239            if !$col html=${html}"        </tr>\n" fi
8240          done
8241        fi
8242        rm
8243      endl
8244    done
8245    if $col html=${html}"          <td colspan=\""{$nb_cols-$col}"\"></td></tr>\n" fi
8246    html=${html}"      </table><br/>\n"\
8247         ${html_menu}\
8248         "    </div><div class=\"section_end\"></div>"\n"    <div id=\"include_footer\"></div>"\n"  </body>"
8249    ('$html') ot. $category.html rm.
8250  done
8251  rm
8252
8253  # Transfer on G'MIC server
8254  x "ln -fs artistic.html index.html"
8255  if $1
8256    e[] "\n  * Transfer gallery on G'MIC server.\n"
8257    x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -eRL . /www/gmic/gallery ; quit\""
8258  fi
8259  e[] "  * All done!\n"
8260
8261# Generate a single image from a list of images, for the gallery.
8262_gallery :
8263  repeat $! l[$>] W$>={w} H$>={h} D$>={d} S$>={s} IS_3D$>=${-_is_3d} endl done
8264  repeat $! l[$>]
8265    if ${IS_3D$>} r3d 1,1,0,-80 r3d 0,1,0,80 snapshot3d 400
8266    else if w>8192 z 0,8191 elif h>8192 rows 0,8191 fi n 0,255
8267    fi
8268  endl done
8269
8270  if !$_is_animated
8271    +__gallery
8272    if w>1024 r={round(1024*100/w,0.1)} r[^-1] $r%,$r%,1,100%,2 fi rm.
8273  fi
8274
8275  repeat $! l[$>]
8276    if s==1 r {w},{h},1,3
8277    elif s==4 drgba
8278    else r {w},{h},1,3,0 fi
8279    if w<=h" && "h<256 r2dy 256,2 elif h<=w" && "w<256 r2dx 256,2 fi
8280    if w<=h" && "h>620 r2dy 620,2 elif h<=w" && "w>620 r2dx 620,2 fi
8281    if h<48 r 100%,48 fi
8282    if w<48 r 48,100% fi
8283    if $_is_animated" && "(w>480" || "h>480) rr2d 480,480,0,2 fi
8284    frame 1,1,0 frame 4,4,255
8285  endl done
8286
8287  if $_is_animated
8288    - 255 r ${-max_wh},1,3,0,0,0.5,0.5 + 255
8289  else
8290    - 255 __gallery + 255
8291    if w<256 - 255 r 256,100%,1,100%,0,0,0.5,0.5 + 255 fi
8292    if h<256 - 255 r 100%,256,1,100%,0,0,0.5,0.5 + 255 fi
8293  fi
8294
8295__gallery :
8296  if $!==2 if w>h a y else a x fi
8297  else montage A # append_tiles 2
8298  fi
8299
8300# Generate reference documentation pages for the G'MIC website.
8301# $1 = upload to G'MIC server, can be { 0 | 1 }.
8302update_reference_html : check "isbool(${1=0})"
8303  path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/reference/
8304  if ['$path_current']!=['$path_ok']
8305    error[0--3] "Command 'update_reference_html: Command run from wrong path: '"$path_current"', "\
8306                "should be '"$path_ok"'."
8307  fi
8308  x "rm -f *.pdf"
8309
8310  rm
8311  e[^-1] "Generate reference documentation pages for the G'MIC website."
8312  it $HOME/work/src/gmic/src/gmic_stdlib.gmic
8313  a y
8314
8315  # Generate image of examples.
8316  x "mkdir -p img"
8317  _parse_cli_images_path="img/"
8318  +parse_cli images
8319  parse_cli html
8320
8321  # Generate reference pages in html.
8322  rm
8323  x "cp -rf "$HOME/work/src/gmic-community/reference/images" ."
8324  reference html,$HOME/work/src/gmic-community/reference
8325
8326  # Upload to G'MIC server.
8327  if $1
8328    e[] "\n > Transfer reference documentation on G'MIC server.\n"
8329    x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -RL . /www/gmic/reference ; quit\""
8330  fi
8331  e[] "\n > All done.\n"
8332
8333# Generate tutorial pages for the G'MIC website.
8334# $1 = upload to G'MIC server, can be { 0 | 1 }.
8335update_tutorial_html : check "isbool(${1=0})"
8336  path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/tutorial/
8337  if ['$path_current']!=['$path_ok']
8338    error[0--3] "Command 'update_tutorial_html: Command run from wrong path: '"$path_current"', "\
8339                "should be '"$path_ok"'."
8340  fi
8341
8342  rm
8343  e[^-1] "Generate tutorial pages for the G'MIC website."
8344  path_tutorial=$HOME/work/src/gmic-community/tutorial
8345  use_vt100
8346
8347  # Build directory structure.
8348  e[] " > Build directory structure."
8349  x "mkdir -p images scripts"
8350  files 5,$path_tutorial/*
8351  l[]
8352    ({'${}'}:y) s -,{','}
8353    for $!
8354      file={0,t} rm[0]
8355      basename $file basename=${} 0 nm. $basename ext={`lowercase(['{x}'])`} rm.
8356      if ['$basename']!='img'
8357        if isdir(['$file']) files 5,$file/* ({'${}'}:y) s. -,{','} # Folder -> Recursively add files
8358        elif s=['$ext'];s=='png'||s=='jpg'||s=='jpeg'||s=='gif'||s=='mp4'||s=='svg'
8359          x "cp -f \""$file"\" images/" # Image/videos: Copy to sub-folder 'images/'
8360        elif s=['$ext'];s=='gmic'
8361          x "cp -f \""$file"\" scripts/" # Scripts: Copy to sub-folder 'scripts/'
8362        elif s=['$ext'];s=='gmd' # G'MIC Markdown -> Copy to current folder './'
8363          it[] $file
8364          ot. $basename rm.
8365        fi
8366      fi
8367    done
8368  endl
8369
8370  # Generate html pages.
8371  files 0,*.gmd files=${}
8372  repeat narg({/$files})
8373    arg0 $>,$files file=${}
8374    e[] " > Generate '"$_vt100_c$file$_vt100_n"'."
8375    t0=$|
8376    it $file
8377    replace_str. "../listmanip/",""
8378    gmd2html 2
8379    ot {n} rm.
8380    t1=$|
8381    e[] "\r > Generate '"$_vt100_c$file$_vt100_n"' (done in "$_vt100_g{_round($t1-$t0,0.1)}"s"$_vt100_n")."
8382  done
8383  x "rm -f *.gmd"
8384
8385  # Upload to G'MIC server.
8386  if $1
8387    e[] "\n > Transfer tutorial pages on G'MIC server.\n"
8388    x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -RL . /www/gmic/tutorial ; quit\""
8389  fi
8390  e[] "\n > All done.\n"
8391
8392# string2ts : ts_file.ts,strings_en.txt,strings_fr.txt
8393# Regenerate .ts file from new translated strings (separated by newline in files strings_*).
8394# Specified .ts file can be an empty argument ("").
8395strings2ts : skip "${1=}"
8396  e[^-1] "Regenerate translation file by merging file '$1' and source/translated strings '$2/$3'."
8397
8398  # Import data and extract original/translated strings.
8399  if narg($1)
8400    l[] it "$1"
8401      lang={`"
8402        lang = vector256();
8403        p = find(#-1,'language=\"');
8404        p>=0?(
8405          p+=10;
8406          q = find(#-1,'\">',p);
8407          q>=0?copy(lang,i[p],q - p);
8408        ); lang"`}
8409      e[] "  > File '$1', detected language : "$lang.
8410      s -,10
8411      N0=0
8412      repeat $! l[$>]
8413        autocrop {'" "'}
8414        if s=crop();find(s,'<source>')>=0" && "find(s,'</source>')>=0
8415          discard {'<source>'} discard {'</source>'} src$N0={t}
8416        elif s=crop();find(s,'<translation>')>=0" && "find(s,'</translation>')>=0
8417          discard {'<translation>'} discard {'</translation>'} dest$N0={t}
8418          N0+=1
8419        fi
8420      endl done
8421      rm
8422    endl
8423  else N0=0 lang=
8424  fi
8425  if $N0 e[] "  > File '$1' contains "$N0" strings." fi
8426  l[] it "$2" s -,10 N1=$! repeat $! nsrc$>={$>,t} done rm endl
8427  l[] it "$3" replace_str " ;",";" s -,10 N2=$! repeat $! ndest$>={$>,t} done rm endl
8428  if $N1!=$N2 error[0--3] "Command 'strings2ts': Number of lines do not match in files '$2' and '$3'." fi
8429  e[] "  > Files '$2' and '$3' contain "$N1" strings."
8430
8431  # Merge all strings together (set higher priority to strings that were already in the .ts file).
8432  l[] repeat $N1 ({'${ndest$>}'}) nm. ${nsrc$>} done y endl
8433  repeat $N0 rmn ${src$>} done # Discard already-translated strings in the .ts file
8434  if $N0 i[0] 0 l[0] rm repeat $N0 i ({'${dest$>}'}) nm. ${src$>} done y endl fi
8435  repeat $! l[$<] src={n} dest={t}
8436    if lowercase(['$src'])==lowercase(['$dest']) rm fi
8437  endl done
8438  sort_list +,n
8439  e[] "  > "$!" strings remain after cleaning/merging ("{$!-$N0}" new)."
8440
8441  # Generate .ts file.
8442  repeat $! l[$>]
8443    _strings2ts_src {n} src=${}
8444    _strings2ts_dest {t} dest=${}
8445    rm
8446    ({'"    <message>"\n\
8447       "      <source>"$src"</source>"\n\
8448       "      <translation>"$dest"</translation>"\n\
8449       "    </message>"\n\n'})
8450  endl done
8451  i[0] ({'"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\n\
8452     "<!DOCTYPE TS>"\n\
8453     "<TS version=\"2.1\" language=\""$lang"\">"\n\
8454     "  <context>"\n\
8455     "    <name>FilterTextTranslator</name>"\n\n'})
8456  ({'"  </context>"\n\
8457     "</TS>"\n'})
8458  y a y
8459
8460_strings2ts_src :
8461  ({'"$*"'})
8462  replace_str. "&deg;","°"
8463  replace_str. "&agrave;","à"
8464  replace_str. "&","&amp;"
8465  replace_str. "<","&lt;"
8466  replace_str. ">","&gt;"
8467  replace_str. "\"","&quot;"
8468  replace_str. "'","&apos;"
8469  u {t}
8470  rm.
8471
8472_strings2ts_dest :
8473  ({'"$*"'}) html2utf8.
8474  replace_str. "&deg;","°"
8475  replace_str. "&agrave;","à"
8476  replace_str. "&","&amp;"
8477  replace_str. "<","&lt;"
8478  replace_str. ">","&gt;"
8479  replace_str. "\"","&quot;"
8480  replace_str. "'","&apos;"
8481  u {t}
8482  rm.
8483
8484#
8485# Output mode 'zart' (output in stdout).
8486#
8487parse_gui_parseparams_zart : u 1 # Tell parser to not parse filter parameters
8488parse_gui_trigger_zart :
8489  repeat $! l[$>] # Keep only 1-level folders
8490    nm {`s=[['{n}'],0];p=find(s,_'/',size(s)-1,0);p>=0?(p=find(s,_'/',p-1,0);p>0?copy(s,s[p+1],size(s)-p));s`}
8491  endl done
8492  sort_list +,n
8493
8494parse_gui_zart :
8495  e[] "  >> Generate output, in 'zart' mode.\n"
8496  +e[] "<?xml version=\"1.0\" ?>"
8497  +e[] "<document name=\"Presets\">\n"
8498
8499  current_group=
8500  N={$_nbfilters-1}
8501  repeat $_nbfilters,f
8502    e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
8503
8504    # Manage groups.
8505    0 nm. {`s=['${_f${f}_path}'];s[0,size(s)-1]`} path={b} rm.
8506    if ['$current_group']!=['$path']
8507      if ['$current_group']!=0 +e[] "</preset_group>\n" fi
8508      +e[] "<!-- ******** \n\n    "$path"\n\n ******** -->"
8509      +e[] "<preset_group name=\""$path"\">\n"
8510      current_group=$path
8511    fi
8512
8513    # Filter definition.
8514    _parse_gui_zart[] ${_f${f}_name} fname=${}
8515
8516    +e[] "<!-- "$path/$fname" -->"
8517    +e[] "<preset name=\""$fname"\">"
8518    +e[] "  <command>"${_f${f}_commandpreview}" $""*</command>"
8519    repeat ${_f${f}_nbparams},p
8520      _parse_gui_zart ${_f${f}_p{$p}_name} name=${}
8521      type=${_f${f}_p{$p}_type}
8522      nbargs=${_f${f}_p{$p}_nbargs}
8523      arg0= repeat $nbargs ('${_f${f}_p${p}_a$>}') autocrop. {'\"'} _parse_gui_zart {t} arg$>=${} rm. done
8524
8525      if ['$type']=='bool'
8526        if lowercase(['$arg0'])=='false' arg0=0 elif lowercase(['$arg0'])=='true' arg0=1 elif !isnum($arg0) arg0=0 fi
8527        +e[] "  <bool name=\""$name"\" default=\""$arg0"\" />"
8528
8529      elif ['$type']=='choice'
8530        default=0 n=0 args=
8531        l[] if isint($arg0) default=$arg0 n+=1 fi onfail endl
8532        c= repeat $nbargs-$n,a args.=${c}"choice"$>"=\""${arg{$n+$a}}"\"" c=" " done
8533        +e[] "  <choice name=\""$name"\" default=\""$default"\" "$args" />"
8534
8535      elif ['$type']=='color'
8536        args= c= repeat $nbargs,a args.=$c${arg$a} c="," done
8537        +e[] "  <color name=\""$name"\" default=\""$args"\" />"
8538
8539      elif s=['$type'];s=='int'" || "s=='float'
8540        +e[] "  <"$type" name=\""$name"\" default=\""$arg0"\" min=\""$arg1"\" max=\""$arg2"\" />"
8541
8542      elif ['$type']=='file'" || "['$type']=='filein'" || "['$type']=='fileout'
8543        +e[] "  <file name=\""$name"\" default=\""$arg0"\" />"
8544
8545      elif ['$type']=='folder'
8546        +e[] "  <folder name=\""$name"\" default=\""$arg0"\" />"
8547
8548      elif ['$type']=='link'
8549        align=-1 name= url= n=0
8550        l[] if isnum($arg0) align=$arg0 n+=1 fi onfail endl
8551        if $nbargs-$n>1 name=${arg$n} url=${arg{$n+1}}
8552        else url,name=${arg$n}
8553        fi
8554        if $align==0 align=left elif $align==1 align=right else align=center fi
8555        +e[] "  <link name=\""$name"\" url=\""$url"\" align=\""$align"\" />"
8556
8557      elif ['$type']=='note'
8558        text={/$arg0}
8559        +e[] "  <note text=\""$text"\" />"
8560
8561      elif ['$type']=='point'
8562        +e[] "  <point name=\""$name"\" position=\""$arg0,$arg1"\" />"
8563
8564      elif ['$type']=='separator'
8565        +e[] "  <separator />"
8566
8567      elif ['$type']=='text'
8568        +e[] "  <text name=\""$name"\" default=\""$arg0"\" />"
8569
8570      elif ['$type']=='value'
8571        +e[] "  <value value=\""$arg0"\" />"
8572
8573      else # Unsupported parameter
8574        +e[] "  <value value=\""$arg0"\" />"
8575      fi
8576
8577    done
8578    +e[] "</preset>\n"
8579  done
8580  if narg($current_group) +e[] "</preset_group>" fi
8581  +e[] "</document>"
8582  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
8583
8584_parse_gui_zart :
8585  l[]
8586    ('"$*"')
8587    replace_str "&amp;","#amp;"
8588    replace_str. "&","&amp;"
8589    replace_str. "<","&lt;"
8590    replace_str. ">","&gt;"
8591    replace_str. "\"","&quot;"
8592    replace_str "#amp;","&amp;"
8593    u {t} rm.
8594  onfail u "" endl
8595
8596# Convert last ascii buffer with html codes (e.g. &#37197;), as an UTF-8 encoded buffer.
8597html2utf8 :
8598  if h
8599    eval "
8600      ref(crop(),source);
8601      ref(vector8(),svalue);
8602      p = 0;
8603      while (1,
8604        p = p0 = find(source,'&#',p);
8605        p<0?break();
8606        p+=2;
8607        p>=size(source)?break();
8608        q = find(source,';',p);
8609        q>p && q<=p+8 && !isnan(val=stov(source,p))?(
8610          copy(svalue,source[p],q-p); svalue[q-p] = 0;
8611          val = stov(svalue,0,1);
8612          !isnan(val) && isint(val) && val>0?(
8613
8614            # Encode in UTF-8.
8615            val<=0x007f?( # 7 bits or less -> 1 byte
8616              i[p0++] = val;
8617            ):val<=0x07ff?( # 11 bits or less -> 2 bytes
8618              i[p0++] = (val>>6)|0xc0;
8619              i[p0++] = (val&0x3f)|0x80;
8620            ):val<=0xffff?( # 16 bits or less -> 3 bytes
8621              i[p0++] = (val>>12)|0xe0;
8622              i[p0++] = ((val>>6)&0x3f)|0x80;
8623              i[p0++] = (val&0x3f)|0x80;
8624            ):( # 21 bits or less -> 4 bytes
8625              i[p0++] = (val>>18)|0xf0;
8626              i[p0++] = ((val>>12)&0x3f)|0x80;
8627              i[p0++] = ((val>>6)&0x3f)|0x80;
8628              i[p0++] = (val&0x3f)|0x80;
8629            );
8630            copy(i[p0],-1,q-p0+1,1,0);
8631            p = q + 1;
8632          );
8633        );
8634      )"
8635    discard. -1
8636  fi
8637
8638# Convert last UTF-8 encoded buffer to ascii buffer with html codes.
8639utf82html :
8640  if h
8641    eval "
8642      write_seq() = ( copy(res[q],'&#'); q+=2; s = vtos(val); l = find(s,0); copy(res[q],s,l); q+=l; res[q++]=_';' );
8643      ref(vector(#4*wh),res);
8644      q = 0;
8645      repeat (wh,p,
8646        i = i[p];
8647        !(i&0x80)?( # 1-byte
8648          res[q++] = i;
8649        ):(i&0xe0)==0xc0?( # 2-bytes
8650          val = (i&0x1f)<<6; i = i[p + 1]; val|= (i&0x3f);
8651          write_seq();
8652        ):(i&0xf0)==0xe0?( # 3-bytes
8653          val = (i&0xf)<<12; i = i[p + 1]; val|= (i&0x3f)<<6; i = i[p + 2]; val|= (i&0x3f);
8654          write_seq();
8655        ):(i&0xf8)==0xf0?( # 4-bytes
8656          val = (i&0x7)<<18; i = i[p + 1]; val|= (i&0x3f)<<12; i = i[p + 2]; val|= (i&0x3f)<<6;
8657          i = i[p + 3]; val|= (i&0x3f);
8658          write_seq();
8659        );
8660      );
8661      store(res,'res',1,q)"
8662    rm. $res
8663  fi
8664
8665# Force certain filters to move from one path to another.
8666_upload_filters_move :
8667  m "move_filter : skip \"${""2=}\" nmd 3,\"$""1\" ind=${}
8668     if $ind basename \"$""1\" nm[$ind] \"$""2\"/${}
8669     else v 0 e[0--4] \"Cannot move unknown filter '\"${_vt100_r}\"$""1\"${_vt100_n}\"'
8670     to '\"${_vt100_g}\"$""2\"${_vt100_n}\"'\" fi"
8671
8672  sort_list +,n
8673  move_filter "Testing/Garagecoder/Anti Alias","Repair"
8674  move_filter "Testing/Garagecoder/Auto Balance","Colors"
8675  move_filter "Testing/Garagecoder/Compression Blur","Repair"
8676  move_filter "Testing/Garagecoder/Emboss","Black & White"
8677  move_filter "Testing/Garagecoder/JPEG Smooth","Repair"
8678  move_filter "Testing/Garagecoder/Quick Tonemap","Details"
8679  move_filter "Testing/Garagecoder/Normalize Brightness","Colors"
8680  move_filter "Testing/Garagecoder/Sharpen [Gradient]","Details"
8681  move_filter "Testing/Garagecoder/Sharpen [Tones]","Details"
8682  move_filter "Testing/Garagecoder/Temperature Balance","Colors"
8683  move_filter "Testing/Garagecoder/Unquantize [JPEG Smooth]","Repair"
8684  move_filter "Testing/Garagecoder/Upscale [Edge]","Repair"
8685  move_filter "Testing/Garagecoder/Wiremap","Rendering"
8686  move_filter "Testing/Garagecoder/Smooth [Geometric-Median]","Repair"
8687  move_filter "Testing/Gmic Tutorials/Hedcut (Experimental)","Patterns"
8688  move_filter "Testing/Iain Fergusson/Easy Skin Retouch","Details"
8689  move_filter "Testing/Iain Fergusson/Moire Removal","Repair"
8690  move_filter "Testing/Iain Fergusson/Halftone Shapes","Patterns"
8691  move_filter "Testing/Iain Fergusson/Simple Local Contrast","Details"
8692  move_filter "Testing/Iain Fergusson/Turbulent Halftone","Patterns"
8693  move_filter "Testing/Joan Rake/Deformations/Ultrawarp++++","Degradations"
8694  move_filter "Testing/Naggobot/Blockism","Artistic"
8695  move_filter "Testing/Reptorian/Blur [Splinter]","Degradations"
8696  move_filter "Testing/Reptorian/Construction Material Texture","Rendering"
8697  move_filter "Testing/Reptorian/Emboss-Relief","Details"
8698  move_filter "Testing/Reptorian/Fragment Blur","Degradations"
8699  move_filter "Testing/Reptorian/Kaleidoscope [Reptorian-Polar]","Deformations"
8700  move_filter "Testing/Reptorian/Logarithmic Distortion","Deformations"
8701  move_filter "Testing/Reptorian/Nebulous","Rendering"
8702  move_filter "Testing/Reptorian/Pixel Push","Deformations"
8703  move_filter "Testing/Reptorian/Point Warp","Deformations"
8704  move_filter "Testing/Reptorian/Popcorn Fractal","Rendering"
8705  move_filter "Testing/Reptorian/Pseudorandom Noise","Rendering"
8706  move_filter "Testing/Reptorian/Sinusoidal Water Distortion","Deformations"
8707  move_filter "Testing/Samj/Arrays & Tiles/Annular Steiner Chain Round Tile","Arrays & Tiles"
8708  move_filter "Testing/Samj/Arrays & Tiles/Reptile","Patterns"
8709  move_filter "Testing/Samj/Artistic/Angoisse Anguish","Artistic"
8710  move_filter "Testing/Samj/Artistic/Chalk It Up [Fr]","Artistic"
8711  move_filter "Testing/Samj/Artistic/Barbouillage Paint Daub","Artistic"
8712  move_filter "Testing/Samj/Artistic/Skeletik","Artistic"
8713  move_filter "Testing/Samj/Patterns/Denim Texture","Patterns"
8714  move_filter "Testing/Samj/Patterns/Soft Random Shades","Patterns"
8715  move_filter "Testing/Samj/Rendering/Pythagoras Tree","Rendering"
8716  move_filter "Testing/Samj/Rendering/Snowflake 2","Rendering"
8717  move_filter "Testing/Samj/Rendering/Twisted Rays","Rendering"
8718  move_filter "Testing/Souphead/Disco","Rendering"
8719  move_filter "Testing/Souphead/Moon2panorama","Deformations"
8720  move_filter "Testing/Souphead/Spiral RGB","Rendering"
8721  move_filter "Testing/Souphead/Kitaoka Spin Illusion","Rendering"
8722  move_filter "Testing/Zonderr/Spiral","Rendering"
8723  um move_filter
8724
8725upload_filters :
8726  e[^-1] "Upload filter definition file on G'MIC server.\n"
8727  rm
8728
8729  # Be sure to get the latest version of filters.
8730  x "cd "$HOME"/work/src/gmic && git pull"
8731  x "cd "$HOME"/work/src/gmic-community && git pull"
8732
8733  # Define the list of compatible versions.
8734  (298,$_version) store. compat
8735
8736  # Import filters from stdlib and community.
8737  files $HOME/work/src/gmic-community/include/*.gmic
8738  files=${},$HOME/work/src/gmic/src/gmic_stdlib.gmic
8739  repeat narg($files) l[]
8740    file=${"arg 1+$>,"$files}
8741    it[] $file
8742    basename $file basename=${}
8743    if ['$basename']=='sylvie_alexandre.gmic'
8744      s +,{'"#@gui <b>"'} i[1--2:2] ('"#@gui ________<b>Testing<b>\n#@gui <i>Samj</i>\n"') y a y
8745    elif s=['$basename'];s=='template.gmic'
8746      rm 0
8747    fi
8748  endl done
8749  i[1--2] ('"#@gui _________________\n"') y a y
8750
8751  # Create update file.
8752  +l.
8753    e[] "** Generate filter update file."
8754    m "parse_gui_trigger_update : _upload_filters_move"
8755    v + parse_gui. update v -
8756    um parse_gui_trigger_update
8757
8758    # Upload filter files on G'MIC server.
8759    e[] "** Upload filter update."
8760    ot ${_path_rc}update$_version.gmic
8761    if "d = date(3); h = date(4); h>=7 && d>=1 && d<=5" url=http://bit.ly/2CmhX65 # url=http://bit.ly/2uaBRMB
8762    else url=https://bit.ly/33lRzX4 # https://bit.ly/2WeKVPv url=http://bit.ly/2uaBRMB #
8763    fi
8764    replace_str "<i>David Tschumperlé</i>","<i><a href=\""$url"\">David Tschumperlé</a></i>"
8765    o cimgz:/tmp/update$_version.gmic,uchar
8766
8767    $compat
8768    repeat w
8769      v={i[$>]}
8770      _upload[] ${_path_rc}update$_version.gmic,plain_update$v.gmic,1
8771      _upload[] /tmp/update$_version.gmic,update$v.gmic,1
8772    done
8773    rm
8774  endl
8775
8776  # Create JSON file.
8777  +l.
8778    e[] "** Generate JSON filters file."
8779    v + parse_gui. json v -
8780
8781    # Upload filter files on G'MIC server.
8782    e[] "** Upload JSON filters."
8783    ot ${_path_rc}/update$_version.json
8784
8785    $compat
8786    repeat w
8787      v={i[$>]}
8788      _upload[] ${_path_rc}/update$_version.json,update$v.json,1
8789    done
8790    rm
8791  endl
8792
8793  # Create filter listing.
8794  +l.
8795    e[] "** Generate filter listing."
8796    v + parse_gui. list v -
8797    ot /tmp/gui_filters.txt
8798    rm
8799  endl
8800  rm
8801
8802_upload : skip "${2=""}","${3=0}"
8803  if narg("$2") out="$2" else basename "$1" out=${} fi
8804  if !narg($GMIC_LOGIN)
8805    GMIC_LOGIN=${"gmic_ftp 0"}
8806    GMIC_PASSWD=${"gmic_ftp 1"}
8807    GMIC_FTP=${"gmic_ftp 2"}
8808  fi
8809  if narg($GMIC_LOGIN)
8810    x $3,"lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"put -O /www/gmic/ \\\"$1\\\" -o \\\""$out"\\\";
8811          quit\" >/dev/null"
8812  fi
8813
8814# Upload or list released binaries on the G'MIC web server.
8815# $1=version number (e.g "1.6.7_pre")
8816# $2={ 0=print list of URLs | 1=upload files }
8817upload_binaries : check "isbool(${2=1})"
8818  is_pre=${"strcontains $1,_pre"}
8819  N=0
8820  file$N=gmic_$1_debian9-2_stretch_amd64.deb            N+=1
8821  file$N=gmic_$1_debian10_buster_amd64.deb              N+=1
8822  file$N=gmic_$1_ubuntu18-04_bionic_amd64.deb           N+=1
8823  file$N=gmic_$1_ubuntu18-10_cosmic_amd64.deb           N+=1
8824  file$N=gmic_$1_ubuntu19-04_disco_amd64.deb            N+=1
8825  file$N=gmic_$1_ubuntu19-10_eoan_amd64.deb             N+=1
8826  file$N=gmic_$1_ubuntu20-04_focal_amd64.deb            N+=1
8827  file$N=gmic_$1_ubuntu20-10_groovy_amd64.deb           N+=1
8828  file$N=gmic_$1_ubuntu21-04_hirsute_amd64.deb          N+=1
8829
8830  file$N=gmic_$1_gimp2.8_debian9-2_stretch_amd64.zip    N+=1
8831  file$N=gmic_$1_gimp2.10_debian10_buster_amd64.zip     N+=1
8832  file$N=gmic_$1_gimp2.8_ubuntu18-04_bionic_amd64.zip   N+=1
8833  file$N=gmic_$1_gimp2.10_ubuntu18-10_cosmic_amd64.zip  N+=1
8834  file$N=gmic_$1_gimp2.10_ubuntu19-04_disco_amd64.zip   N+=1
8835  file$N=gmic_$1_gimp2.10_ubuntu19-10_eoan_amd64.zip    N+=1
8836  file$N=gmic_$1_gimp2.10_ubuntu20-04_focal_amd64.zip   N+=1
8837  file$N=gmic_$1_gimp2.10_ubuntu20-10_groovy_amd64.zip  N+=1
8838  file$N=gmic_$1_gimp2.10_ubuntu21-04_hirsute_amd64.zip N+=1
8839
8840  file$N=gmic_$1_krita_debian9-2_stretch_amd64.zip      N+=1
8841  file$N=gmic_$1_krita_debian10_buster_amd64.zip        N+=1
8842  file$N=gmic_$1_krita_ubuntu18-04_bionic_amd64.zip     N+=1
8843  file$N=gmic_$1_krita_ubuntu18-10_cosmic_amd64.zip     N+=1
8844  file$N=gmic_$1_krita_ubuntu19-04_disco_amd64.zip      N+=1
8845  file$N=gmic_$1_krita_ubuntu19-10_eoan_amd64.zip       N+=1
8846  file$N=gmic_$1_krita_ubuntu20-04_focal_amd64.zip      N+=1
8847  file$N=gmic_$1_krita_ubuntu20-10_groovy_amd64.zip     N+=1
8848  file$N=gmic_$1_krita_ubuntu21-04_hirsute_amd64.zip    N+=1
8849
8850  file$N=gmic_$1_cli_win64.zip                          N+=1
8851  file$N=gmic_$1_lib_win64.zip                          N+=1
8852  file$N=gmic_$1_qt_win64.zip                           N+=1
8853  file$N=gmic_$1_gimp2.10_win64.zip                     N+=1
8854  file$N=gmic_$1_gimp2.10_win64.exe                     N+=1
8855  file$N=gmic_$1_krita_win64.zip                        N+=1
8856
8857  if !$2 # Only get list of URLs
8858    e[0--3] "List URLs of released binaries ($1) from the G'MIC web server.\n"
8859    repeat $N
8860      file=${file$>}
8861      is_win=${strcontains[]" "$file,win}
8862      if $is_win folder="windows" else folder="linux" fi
8863      e[] "http://gmic.eu/files/"$folder/$file
8864    done
8865    e[] ""
8866
8867  else # Upload files
8868    e[0--3] "Upload released binaries ($1) on the G'MIC web server."
8869
8870    t0=$| n=0 t=0
8871    e[] "- Waiting for binary files to be build."
8872    do
8873      repeat $N
8874        file=${file$>}
8875        if isfile(['{/$file}'])
8876          strreplace $file,_$1_,_
8877          file_short=${}
8878          is_win=${strcontains[]" "$file,win}
8879          if $is_win folder="windows" else folder="linux" fi
8880          e[] "- Upload file '"$file"' to 'https://gmic.eu/files/prerelease/"$file_short"'."
8881          _upload[] $file,"files/prerelease/"$file_short
8882          if !$is_pre
8883            e[] "- Upload file '"$file"' to 'https://gmic.eu/files/"$folder/$file"'."
8884            _upload[] $file,"files/"$folder/$file
8885          fi
8886
8887          file$>= n+=1
8888        fi
8889      done
8890      if $n<$N
8891        if !($t%4)
8892          remaining= sep=
8893          repeat $N if narg(${file$>}) remaining.=${sep}${file$>} sep=", " fi done
8894          e[] "- Waiting for files: "$remaining"."
8895        fi
8896        wait 30000 t+=1
8897      fi
8898    while $n<$N" && "$|<$t0+60*60*6
8899    if $n<$N e[] "- Partial uploads done (timeout reached)."
8900    else e[] "- All uploads done !"
8901    fi
8902  fi
8903
8904# Update version number in html header file.
8905# (works both for CImg and G'MIC !)
8906# $1 = filename
8907# $2 = version number
8908# $3 = is_prerelease = { 0 | 1 }
8909_update_header_html : check "narg(${1=}) && ${2=0}>0 && isbool(${3=0})"
8910  filename="$1"
8911  l[]
8912    it[] $filename
8913    s +,{'\n'}
8914    repeat $! if {$>,h>=64} l[$>]
8915      +autocrop {'" "'} autocrop. {'\t'}
8916      if "find(#0,['Latest stable version: '])>=0"
8917        is_gmic={"find(#-1,'gmic.eu')>=0"}
8918        is_cimg={"find(#-1,'cimg.eu')>=0"}
8919        if !$is_gmic" && "!$is_cimg error "Cannot determine CImg or G'MIC header file." fi
8920
8921        # Retrieve latest stable version specified in the header file.
8922        +l. s -,{'>'}
8923          if {2,"i[0]>=_'0' && i[0]<=_'9' && i[1]==_'.' && i[2]>=_'0' && i[2]<=_'9' &&
8924                 i[3]==_'.' && i[4]>=_'0' && i[4]<=_'9'"}
8925            sta={2,`crop(0,0,1,5)`}
8926          fi
8927          if $3 pre=${strver\ $2} else sta=${strver\ $2} fi
8928        rm endl
8929        rm[0]
8930        if $is_gmic
8931          i[0] ('"        Latest stable version: <b>"\
8932                 "<a href=\"https://gmic.eu/download.html\">"$sta"</a></b>"')
8933          if $3
8934            i[1] ('" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "\
8935                   "Current pre-release: <b><a href=\"https://gmic.eu/files/prerelease\">"$pre"</a></b>"')
8936          fi
8937        else
8938          i[0] ('"        Latest stable version: "\
8939                 "<b><a href=\"http://cimg.eu/files/CImg_"$sta".zip\">"$sta"</a></b>"')
8940          if $3
8941            i[1] ('" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "\
8942                   "Current pre-release: <b><a href=\"http://cimg.eu/files/CImg_latest.zip\">"$pre"</a></b>"')
8943          fi
8944        fi
8945        y
8946      fi
8947      rm.
8948    endl fi done
8949    a y
8950    ot $filename
8951    rm
8952  endl
8953
8954#@cli v : eq. to 'verbose'. : (+)
8955
8956#@cli verbose : level : { + | - } : (+)
8957#@cli : Set or increment/decrement the verbosity level. Default level is 0.
8958#@cli : (eq. to 'v').\n
8959#@cli : When 'level'>0, G'MIC log messages are displayed on the standard error (stderr).
8960#@cli : Default value: 'level=1'.
8961
8962#@cli wait : delay : (no arg) : (+)
8963#@cli : Wait for a given delay (in ms), optionally since the last call to 'wait'.
8964#@cli : or wait for a user event occurring on the selected instant display windows.
8965#@cli : 'delay' can be { <0=delay+flush events | 0=event | >0=delay }.
8966#@cli : Command selection (if any) stands for instant display window indices instead of image indices.
8967#@cli : If no window indices are specified and if 'delay' is positive, the command results
8968#@cli : in a 'hard' sleep during specified delay.
8969#@cli : Default value: 'delay=0'.
8970
8971#@cli warn : _force_visible={ 0 | 1 },_message : (+)
8972#@cli : Print specified warning message, on the standard error (stderr).
8973#@cli : Command selection (if any) stands for displayed call stack subset instead of image indices.
8974
8975#@cli w : eq. to 'window'. : (+)
8976
8977#@cli window : _width[%]>=-1,_height[%]>=-1,_normalization,_fullscreen,_pos_x[%],_pos_y[%],_title : (+)
8978#@cli : Display selected images into an instant display window with specified size, normalization type,
8979#@cli : fullscreen mode and title.
8980#@cli : (eq. to 'w').\n
8981#@cli : If 'width' or 'height' is set to -1, the corresponding dimension is adjusted to the window
8982#@cli : or image size.
8983#@cli : Specify 'pos_x' and 'pos_y' arguments only if the window has to be moved to the specified
8984#@cli : coordinates. Otherwise, they can be avoided.
8985#@cli : 'width'=0 or 'height'=0 closes the instant display window.
8986#@cli : 'normalization' can be { -1=keep same | 0=none | 1=always | 2=1st-time | 3=auto }.
8987#@cli : 'fullscreen' can be { -1=keep same | 0=no | 1=yes }.
8988#@cli : You can manage up to 10 different instant display windows by using the numbered variants
8989#@cli : 'w0' (default, eq. to 'w'),'w1',...,'w9' of the command 'w'.
8990#@cli : Invoke 'window' with no selection to make the window visible, if it has been closed by the user.
8991#@cli : Default values: 'width=height=normalization=fullscreen=-1' and 'title=(undefined)'.
8992
8993#---------------------------------
8994#
8995#@cli :: List Manipulation
8996#
8997#---------------------------------
8998
8999#@cli k : eq. to 'keep'. : (+)
9000
9001#@cli keep : (+)
9002#@cli : Keep only selected images.
9003#@cli : (eq. to 'k').
9004#@cli : $ image.jpg split x keep[0-50%:2] append x
9005#@cli : $ image.jpg split x keep[^30%-70%] append x
9006
9007#@cli mv : eq. to 'move'. : (+)
9008
9009#@cli move : position[%] : (+)
9010#@cli : Move selected images at specified position.
9011#@cli : (eq. to 'mv').
9012#@cli : $ image.jpg split x,3 move[1] 0
9013#@cli : $ image.jpg split x move[50%--1:2] 0 append x
9014
9015#@cli nm : eq. to 'name'. : (+)
9016
9017#@cli name : "name1","name2",... : (+)
9018#@cli : Set names of selected images.
9019#@cli : - If the selection contains a single image, then it is assumed the command has a single name
9020#@cli :  argument (possibly containing multiple comas).
9021#@cli : - If the selection contains more than one image, each command argument defines a single image
9022#@cli :  name for each image of the selection.
9023#@cli : (eq. to 'nm').
9024#@cli : $ image.jpg name image blur[image] 2
9025#@cli : $$
9026
9027#@cli rm : eq. to 'remove'. : (+)
9028
9029#@cli remove : (+)
9030#@cli : Remove selected images.
9031#@cli : (eq. to 'rm').
9032#@cli : $ image.jpg split x remove[30%-70%] append x
9033#@cli : $ image.jpg split x remove[0-50%:2] append x
9034
9035#@cli remove_duplicates
9036#@cli : Remove duplicates images in the selected images list.
9037#@cli : $ (1,2,3,4,2,4,3,1,3,4,2,1) split x remove_duplicates append x
9038remove_duplicates :
9039  e[^-1] "Remove duplicates images in selected list of image$?."
9040  repeat $!,base
9041    off=0
9042    repeat $!-$>-1
9043      comp={$base+1+$>-$off}
9044      if $comp>=$! break fi
9045      +-[$base,$comp] abs. is_duplicate={!is} rm.
9046      if $is_duplicate rm[$comp] off+=1 fi
9047    done
9048  done
9049
9050#@cli remove_empty
9051#@cli : Remove empty images in the selected image list.
9052remove_empty :
9053  e[^-1] "Remove empty images in selected list of image$?."
9054  $!,1,1,1,"!w#x?x:-1" discard. -1 rm[{^},-1]
9055
9056#@cli rmn : eq. to 'remove_named'.
9057rmn :
9058  e[^-1] "Remove images named '$*'."
9059  nmd $"*" rm[${}]
9060
9061#@cli remove_named : "name1","name2",...
9062#@cli : Remove all images with specified names from the list of images.
9063#@cli : Does nothing if no images with those names exist.
9064#@cli : (eq. to 'rmn').
9065remove_named :
9066  e[^-1] "Remove images named '$*'."
9067  nmd $"*" rm[${}]
9068
9069#@cli rv : eq. to 'reverse'. : (+)
9070
9071#@cli reverse : (+)
9072#@cli : Reverse positions of selected images.
9073#@cli : (eq. to 'rv').
9074#@cli : $ image.jpg split x,3 reverse[-2,-1]
9075#@cli : $ image.jpg split x,-16 reverse[50%-100%] append x
9076
9077#@cli sort_list : _ordering={ + | - },_criterion
9078#@cli : Sort list of selected images according to the specified image criterion.
9079#@cli : Default values: 'ordering=+', 'criterion=i'.
9080#@cli : $ (1;4;7;3;9;2;4;7;6;3;9;1;0;3;3;2) split y sort_list +,i append y
9081sort_list : skip ${1=+},${2=i}
9082  s0="descending" s1="ascending"
9083  e[^-1] "Sort list of image$? in "${s{_'+'=='$1'}}" order, according to the image criterion '$2'."
9084  if $!
9085    if '$2'=='n' # Special case : lexicographic order from image names
9086      op={`;'$1'=='-'?_'>':_'<'`}
9087      $!,1,1,1,"n = name(#x,1024); find(n,0)%1025" slen={iM} rm. # Largest name length.
9088      eval "
9089        const lm1 = l - 1;
9090        const slen = "$slen";
9091        strcmp(n0,n1) = (for (k = 0, k<slen && !(diff = lowercase(n0[k]) - lowercase(n1[k])), ++k); diff);
9092        quicksort(lo0,hi0) = (
9093          stack = vector(#2*l);
9094          stacksize = 0;
9095          push(elt0,elt1) = (stack[stacksize++] = elt0; stack[stacksize++] = elt1);
9096          pop() = (_s1 = stack[--stacksize]; _s0 = stack[--stacksize]; [_s0,_s1]);
9097          push(lo0,hi0);
9098          while (stacksize>0,
9099            range = pop();
9100            lo = range[0];
9101            hi = range[1];
9102            pivot = int((lo + hi)/2);
9103            ref(name(#pivot,slen),npivot);
9104            while (lo<hi,
9105              while ((ref(name(#lo,slen),nlo);strcmp(nlo,npivot)<0), ++lo);
9106              while ((ref(name(#hi,slen),nhi);strcmp(npivot,nhi)<0), --hi);
9107              lo<=hi?(lo!=hi?run('rv[',lo,',',hi,']'); ++lo; --hi);
9108            );
9109            if (range[0]<hi,push(range[0],hi));
9110            if (lo<range[1],push(lo,range[1]));
9111          )
9112        );
9113        quicksort(0,lm1)"
9114
9115    else # Generic case
9116      i=$! repeat $! ({$>,$2}) done a[$i--1] y +f. 'y' a[-2,-1] x sort. $1,y z. 1,1
9117      repeat h nm$>={$>,n} nm[$>] sortlist$> done
9118      repeat h mv[sortlist{i(0,$>)}] -1 done
9119      repeat h nm[$>] ${nm{i(0,$>)}} done
9120      rm.
9121    fi
9122  fi
9123
9124_sort_list_lexi :
9125  u {"
9126       str1 = ['$1'];
9127       str2 = ['$2'];
9128       siz = min(size(str1),size(str2));
9129       lex = -1;
9130       for (i = 0, i<siz && lex<0, ++i,
9131         s1 = lowercase(str1[i]);
9132         s2 = lowercase(str2[i]);
9133         lex = s1"$3"s2?1:s2"$3"s1?0:-1;
9134       );
9135       lex<0?size(str1)"$3"size(str2):lex;
9136     "}
9137
9138#---------------------------------
9139#
9140#@cli :: Mathematical Operators
9141#
9142#---------------------------------
9143
9144#@cli abs : (+)
9145#@cli : Compute the pointwise absolute values of selected images.
9146#@cli : $ image.jpg +sub {ia} abs[-1]
9147#@cli : $ 300,1,1,1,'cos(20*x/w)' +abs display_graph 400,300
9148
9149#@cli acos : (+)
9150#@cli : Compute the pointwise arccosine of selected images.
9151#@cli : $ image.jpg +normalize -1,1 acos[-1]
9152#@cli : $ 300,1,1,1,'cut(x/w+0.1*u,0,1)' +acos display_graph 400,300
9153#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
9154
9155#@cli acosh : (+)
9156#@cli : Compute the pointwise hyperbolic arccosine of selected images.
9157
9158#@cli + : eq. to 'add'. : (+)
9159
9160#@cli add : value[%] : [image] : 'formula' : (no arg) : (+)
9161#@cli : Add specified value, image or mathematical expression to selected images, \
9162# or compute the pointwise sum of selected images.
9163#@cli : (eq. to '+').
9164#@cli : $ image.jpg +add 30% cut 0,255
9165#@cli : $ image.jpg +blur 5 normalize 0,255 add[1] [0]
9166#@cli : $ image.jpg add '80*cos(80*(x/w-0.5)*(y/w-0.5)+c)' cut 0,255
9167#@cli : $ image.jpg repeat 9 +rotate[0] {$>*36},1,0,50%,50% done add div 10
9168
9169#@cli & : eq. to 'and'. : (+)
9170
9171#@cli and : value[%] : [image] : 'formula' : (no arg) : (+)
9172#@cli : Compute the bitwise AND of selected images with specified value, image or mathematical \
9173# expression, or compute the pointwise sequential bitwise AND of selected images.
9174#@cli : (eq. to '&').
9175#@cli : $ image.jpg and {128+64}
9176#@cli : $ image.jpg +mirror x and
9177
9178#@cli argmax
9179#@cli : Compute the argmax of selected images. Returns a single image
9180#@cli : with each pixel value being the index of the input image with maximal value.
9181#@cli : $ image.jpg sample lena,lion,square +argmax
9182argmax :
9183  e[^-1] "Compute argmax of image$?."
9184  if !$! return fi
9185  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
9186  ${-max_whds},"argmax("$str")" k. nm [argmax]
9187
9188#@cli argmaxabs
9189#@cli : Compute the argmaxabs of selected images. Returns a single image
9190#@cli : with each pixel value being the index of the input image with maxabs value.
9191argmaxabs :
9192  e[^-1] "Compute argmaxabs of image$?."
9193  if !$! return fi
9194  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
9195  ${-max_whds},"argmaxabs("$str")" k. nm [argmaxabs]
9196
9197#@cli argmin
9198#@cli : Compute the argmin of selected images. Returns a single image
9199#@cli : with each pixel value being the index of the input image with minimal value.
9200#@cli : $ image.jpg sample lena,lion,square +argmin
9201argmin :
9202  e[^-1] "Compute argmin of image$?."
9203  if !$! return fi
9204  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
9205  ${-max_whds},"argmin("$str")" k. nm [argmin]
9206
9207#@cli argminabs
9208#@cli : Compute the argminabs of selected images. Returns a single image
9209#@cli : with each pixel value being the index of the input image with minabs value.
9210argminabs :
9211  e[^-1] "Compute argminabs of image$?."
9212  if !$! return fi
9213  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
9214  ${-max_whds},"argminabs("$str")" k. nm [argminabs]
9215
9216#@cli asin : (+)
9217#@cli : Compute the pointwise arcsine of selected images.
9218#@cli : $ image.jpg +normalize -1,1 asin[-1]
9219#@cli : $ 300,1,1,1,'cut(x/w+0.1*u,0,1)' +asin display_graph 400,300
9220#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
9221
9222#@cli asinh : (+)
9223#@cli : Compute the pointwise hyperbolic arcsine of selected images.
9224
9225#@cli atan : (+)
9226#@cli : Compute the pointwise arctangent of selected images.
9227#@cli : $ image.jpg +normalize 0,8 atan[-1]
9228#@cli : $ 300,1,1,1,'4*x/w+u' +atan display_graph 400,300
9229#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
9230
9231#@cli atan2 : [x_argument] : (+)
9232#@cli : Compute the pointwise oriented arctangent of selected images.
9233#@cli : Each selected image is regarded as the y-argument of the arctangent function, while the
9234#@cli : specified image gives the corresponding x-argument.
9235#@cli : $ (-1,1) (-1;1) resize 400,400,1,1,3 atan2[1] [0] keep[1] mod {pi/8}
9236#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
9237
9238#@cli atanh : (+)
9239#@cli : Compute the pointwise hyperbolic arctangent of selected images.
9240
9241#@cli << : eq. to 'bsl'. : (+)
9242
9243#@cli bsl : value[%] : [image] : 'formula' : (no arg) : (+)
9244#@cli : Compute the bitwise left shift of selected images with specified value, image or \
9245# mathematical expression, or compute the pointwise sequential bitwise left shift of \
9246# selected images.
9247#@cli : (eq. to '<<').
9248#@cli : $ image.jpg bsl 'round(3*x/w,0)' cut 0,255
9249
9250#@cli >> : eq. to 'bsr'. : (+)
9251
9252#@cli bsr : value[%] : [image] : 'formula' : (no arg) : (+)
9253#@cli : Compute the bitwise right shift of selected images with specified value, image or \
9254# mathematical expression, or compute the pointwise sequential bitwise right shift of \
9255# selected images.
9256#@cli : (eq. to '>>').
9257#@cli : $ image.jpg bsr 'round(3*x/w,0)' cut 0,255
9258
9259#@cli cos : (+)
9260#@cli : Compute the pointwise cosine of selected images.
9261#@cli : $ image.jpg +normalize 0,{2*pi} cos[-1]
9262#@cli : $ 300,1,1,1,'20*x/w+u' +cos display_graph 400,300
9263#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
9264
9265#@cli cosh : (+)
9266#@cli : Compute the pointwise hyperbolic cosine of selected images.
9267#@cli : $ image.jpg +normalize -3,3 cosh[-1]
9268#@cli : $ 300,1,1,1,'4*x/w+u' +cosh display_graph 400,300
9269
9270#@cli deg2rad
9271#@cli : Convert pointwise angle values of selected images, from degrees to radians (apply 'i*pi/180').
9272deg2rad :
9273  e[^-1] "Convert pointwise angle values of image$?, from degrees to radians."
9274  * 0.017453292519943295
9275
9276#@cli / : eq. to 'div'. : (+)
9277
9278#@cli div : value[%] : [image] : 'formula' : (no arg) : (+)
9279#@cli : Divide selected images by specified value, image or mathematical expression, \
9280# or compute the pointwise quotient of selected images.
9281#@cli : (eq. to '/').
9282#@cli : $ image.jpg div '1+abs(cos(x/10)*sin(y/10))'
9283#@cli : $ image.jpg +norm add[-1] 1 +div
9284
9285#@cli div_complex : [divider_real,divider_imag],_epsilon>=0
9286#@cli : Perform division of the selected complex pairs (real1,imag1,...,realN,imagN) of images by
9287#@cli : specified complex pair of images (divider_real,divider_imag).
9288#@cli : In complex pairs, the real image must be always located before the imaginary image in the image list.
9289#@cli : Default value: 'epsilon=1e-8'.
9290div_complex : check ${3=1e-8}>=0
9291  e[^-1] "Divide complex pair$? by complex pair "${"pass$1,$2 -1"}" (with epsilon $3)."
9292  repeat int($!/2) pass${1,2} 0 l[$>,{$>+1},-2,-1]
9293    +*[1,2] +*[0,3] -[-2,-1] # bc-ad
9294    *[0] [2] *[1] [3] +[0,1] # ac+bd
9295    sqr[1,2] +[1,2] +[1] $3  # c^2+d^2
9296    /[2] [1] /[0,1]
9297  endl done
9298
9299#@cli == : eq. to 'eq'. : (+)
9300
9301#@cli eq : value[%] : [image] : 'formula' : (no arg) : (+)
9302#@cli : Compute the boolean equality of selected images with specified value, image or \
9303# mathematical expression, or compute the boolean equality of selected images.
9304#@cli : (eq. to '==').
9305#@cli : $ image.jpg round 40 eq {round(ia,40)}
9306#@cli : $ image.jpg +mirror x eq
9307
9308#@cli erf : (+)
9309#@cli : Compute the pointwise error function of selected images.
9310#@cli : $ image.jpg +normalize 0,2 erf[-1]
9311#@cli : $ 300,1,1,1,'7*x/w-3.5+u' +erf display_graph 400,300
9312
9313#@cli exp : (+)
9314#@cli : Compute the pointwise exponential of selected images.
9315#@cli : $ image.jpg +normalize 0,2 exp[-1]
9316#@cli : $ 300,1,1,1,'7*x/w+u' +exp display_graph 400,300
9317
9318#@cli >= : eq. to 'ge'. : (+)
9319
9320#@cli ge : value[%] : [image] : 'formula' : (no arg) : (+)
9321#@cli : Compute the boolean 'greater or equal than' of selected images with specified value, image
9322#@cli : or mathematical expression, or compute the boolean 'greater or equal than' of selected images.
9323#@cli : (eq. to '>=').
9324#@cli : $ image.jpg ge {ia}
9325#@cli : $ image.jpg +mirror x ge
9326
9327#@cli > : eq. to 'gt'. : (+)
9328
9329#@cli gt : value[%] : [image] : 'formula' : (no arg) : (+)
9330#@cli : Compute the boolean 'greater than' of selected images with specified value, image or \
9331# mathematical expression, or compute the boolean 'greater than' of selected images.
9332#@cli : (eq. to '>').
9333#@cli : $ image.jpg gt {ia}
9334#@cli : $ image.jpg +mirror x gt
9335
9336#@cli <= : eq. to 'le'. : (+)
9337
9338#@cli le : value[%] : [image] : 'formula' : (no arg) : (+)
9339#@cli : Compute the boolean 'less or equal than' of selected images with specified value, image or \
9340# mathematical expression, or compute the boolean 'less or equal than' of selected images.
9341#@cli : (eq. to '<=').
9342#@cli : $ image.jpg le {ia}
9343#@cli : $ image.jpg +mirror x le
9344
9345#@cli < : eq. to 'lt'. : (+)
9346
9347#@cli lt : value[%] : [image] : 'formula' : (no arg) : (+)
9348#@cli : Compute the boolean 'less than' of selected images with specified value, image or \
9349# mathematical expression, or compute the boolean 'less than' of selected images.
9350#@cli : (eq. to '<').
9351#@cli : $ image.jpg lt {ia}
9352#@cli : $ image.jpg +mirror x lt
9353
9354#@cli log : (+)
9355#@cli : Compute the pointwise base-e logarithm of selected images.
9356#@cli : $ image.jpg +add 1 log[-1]
9357#@cli : $ 300,1,1,1,'7*x/w+u' +log display_graph 400,300
9358
9359#@cli log10 : (+)
9360#@cli : Compute the pointwise base-10 logarithm of selected images.
9361#@cli : $ image.jpg +add 1 log10[-1]
9362#@cli : $ 300,1,1,1,'7*x/w+u' +log10 display_graph 400,300
9363
9364#@cli log2 : (+)
9365#@cli : Compute the pointwise base-2 logarithm of selected images
9366#@cli : $ image.jpg +add 1 log2[-1]
9367#@cli : $ 300,1,1,1,'7*x/w+u' +log2 display_graph 400,300
9368
9369#@cli max : value[%] : [image] : 'formula' : (no arg) : (+)
9370#@cli : Compute the maximum between selected images and specified value, image or \
9371# mathematical expression, or compute the pointwise maxima between selected images.
9372#@cli : $ image.jpg +mirror x max
9373#@cli : $ image.jpg max 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R'
9374
9375#@cli maxabs : value[%] : [image] : 'formula' : (no arg) : (+)
9376#@cli : Compute the maxabs between selected images and specified value, image or \
9377# mathematical expression, or compute the pointwise maxabs between selected images.
9378
9379#@cli m/ : eq. to 'mdiv'. : (+)
9380
9381#@cli mdiv : value[%] : [image] : 'formula' : (no arg) : (+)
9382#@cli : Compute the matrix division of selected matrices/vectors by specified value, image or \
9383# mathematical expression, or compute the matrix division of selected images.
9384#@cli : (eq. to 'm/').
9385
9386#@cli med
9387#@cli : Compute the median of selected images.
9388#@cli : $ image.jpg sample lena,lion,square +med
9389med :
9390  e[^-1] "Compute median of image$?."
9391  if !$! return fi
9392  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
9393  ${-max_whds},"med("$str")" k. nm [med]
9394
9395#@cli min : value[%] : [image] : 'formula' : (no arg) : (+)
9396#@cli : Compute the minimum between selected images and specified value, image or \
9397# mathematical expression, or compute the pointwise minima between selected images.
9398#@cli : $ image.jpg +mirror x min
9399#@cli : $ image.jpg min 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R'
9400
9401#@cli minabs : value[%] : [image] : 'formula' : (no arg) : (+)
9402#@cli : Compute the minabs between selected images and specified value, image or \
9403# mathematical expression, or compute the pointwise minabs between selected images.
9404
9405#@cli % : eq. to 'mod'. : (+)
9406
9407#@cli mod : value[%] : [image] : 'formula' : (no arg) : (+)
9408#@cli : Compute the modulo of selected images with specified value, image or mathematical \
9409# expression, or compute the pointwise sequential modulo of selected images.
9410#@cli : (eq. to '%').
9411#@cli : $ image.jpg +mirror x mod
9412#@cli : $ image.jpg mod 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R'
9413
9414#@cli m* : eq. to 'mmul'. : (+)
9415
9416#@cli mmul : value[%] : [image] : 'formula' : (no arg) : (+)
9417#@cli : Compute the matrix right multiplication of selected matrices/vectors by specified value, image or \
9418# mathematical expression, or compute the matrix right multiplication of selected images.
9419#@cli : (eq. to 'm*').
9420#@cli : $ (0,1,0;0,0,1;1,0,0) (1;2;3) +mmul
9421
9422#@cli * : eq. to 'mul'. : (+)
9423
9424#@cli mul : value[%] : [image] : 'formula' : (no arg) : (+)
9425#@cli : Multiply selected images by specified value, image or mathematical expression, \
9426# or compute the pointwise product of selected images.
9427#@cli : (eq. to '*').
9428#@cli : See also: ''add'', ''sub'', ''div''.
9429#@cli : $ image.jpg +mul 2 cut 0,255
9430#@cli : $ image.jpg (1,2,3,4,5,6,7,8) ri[-1] [0] mul[0] [-1]
9431#@cli : $ image.jpg mul '1-3*abs(x/w-0.5)' cut 0,255
9432#@cli : $ image.jpg +luminance negate[-1] +mul
9433
9434#@cli mul_channels : value1,_value2,...,_valueN
9435#@cli : Multiply channels of selected images by specified sequence of values.
9436#@cli : $ image.jpg +mul_channels 1,0.5,0.8
9437mul_channels :
9438  e[^-1] "Multiply channels of image$? by value sequence ($*)."
9439  $=arg repeat $#,i
9440    fact=${arg{1+($>%$#)}}
9441    repeat $! if {$>,$i<s} sh[$>] $i *. $fact rm. fi done
9442  done
9443
9444#@cli mul_complex : [multiplier_real,multiplier_imag]
9445#@cli : Perform multiplication of the selected complex pairs (real1,imag1,...,realN,imagN) of images by
9446#@cli : specified complex pair of images (multiplier_real,multiplier_imag).
9447#@cli : In complex pairs, the real image must be always located before the imaginary image in the image list.
9448mul_complex :
9449  e[^-1] "Multiply complex pair$? by complex pair "${"pass$1,$2 -1"}"."
9450  repeat int($!/2) pass${1,2} 0 l[$>,{$>+1},-2,-1]
9451    +*[0,3] +*[1,2] +[-2,-1] # ad+bc
9452    *[0,2] *[1,2] -[0,1] # ac-bd
9453  endl done
9454
9455#@cli != : eq. to 'neq'. : (+)
9456
9457#@cli neq : value[%] : [image] : 'formula' : (no arg) : (+)
9458#@cli : Compute the boolean inequality of selected images with specified value, image or \
9459# mathematical expression, or compute the boolean inequality of selected images.
9460#@cli : (eq. to '!=').
9461#@cli : $ image.jpg round 40 neq {round(ia,40)}
9462
9463#@cli | : eq. to 'or'. : (+)
9464
9465#@cli or : value[%] : [image] : 'formula' : (no arg) : (+)
9466#@cli : Compute the bitwise OR of selected images with specified value, image or mathematical \
9467# expression, or compute the pointwise sequential bitwise OR of selected images.
9468#@cli : (eq. to '|').
9469#@cli : $ image.jpg or 128
9470#@cli : $ image.jpg +mirror x or
9471
9472#@cli ^ : eq. to 'pow'. : (+)
9473
9474#@cli pow : value[%] : [image] : 'formula' : (no arg) : (+)
9475#@cli : Raise selected images to the power of specified value, image or mathematical \
9476# expression, or compute the pointwise sequential powers of selected images.
9477#@cli : (eq. to '^').
9478#@cli : $ image.jpg div 255 +pow 0.5 mul 255
9479#@cli : $ image.jpg gradient pow 2 add pow 0.2
9480
9481#@cli rad2deg
9482#@cli : Convert pointwise angle values of selected images, from radians to degrees (apply 'i*180/pi').
9483rad2deg :
9484  e[^-1] "Convert pointwise angle values of image$?, from radians to degrees."
9485  * 57.295779513082323
9486
9487#@cli rol : value[%] : [image] : 'formula' : (no arg) : (+)
9488#@cli : Compute the bitwise left rotation of selected images with specified value, image or \
9489# mathematical expression, or compute the pointwise sequential bitwise left rotation of \
9490# selected images.
9491#@cli : $ image.jpg rol 'round(3*x/w,0)' cut 0,255
9492
9493#@cli ror : value[%] : [image] : 'formula' : (no arg) : (+)
9494#@cli : Compute the bitwise right rotation of selected images with specified value, image or \
9495# mathematical expression, or compute the pointwise sequential bitwise right rotation of \
9496# selected images.
9497#@cli : $ image.jpg ror 'round(3*x/w,0)' cut 0,255
9498
9499#@cli sign : (+)
9500#@cli : Compute the pointwise sign of selected images.
9501#@cli : $ image.jpg +sub {ia} sign[-1]
9502#@cli : $ 300,1,1,1,'cos(20*x/w+u)' +sign display_graph 400,300
9503
9504#@cli sin : (+)
9505#@cli : Compute the pointwise sine of selected images.
9506#@cli : $ image.jpg +normalize 0,{2*pi} sin[-1]
9507#@cli : $ 300,1,1,1,'20*x/w+u' +sin display_graph 400,300
9508#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
9509
9510#@cli sinc : (+)
9511#@cli : Compute the pointwise sinc function of selected images.
9512#@cli : $ image.jpg +normalize {-2*pi},{2*pi} sinc[-1]
9513#@cli : $ 300,1,1,1,'20*x/w+u' +sinc display_graph 400,300
9514
9515#@cli sinh : (+)
9516#@cli : Compute the pointwise hyperbolic sine of selected images.
9517#@cli : $ image.jpg +normalize -3,3 sinh[-1]
9518#@cli : $ 300,1,1,1,'4*x/w+u' +sinh display_graph 400,300
9519
9520#@cli sqr : (+)
9521#@cli : Compute the pointwise square function of selected images.
9522#@cli : $ image.jpg +sqr
9523#@cli : $ 300,1,1,1,'40*x/w+u' +sqr display_graph 400,300
9524
9525#@cli sqrt : (+)
9526#@cli : Compute the pointwise square root of selected images.
9527#@cli : $ image.jpg +sqrt
9528#@cli : $ 300,1,1,1,'40*x/w+u' +sqrt display_graph 400,300
9529
9530#@cli - : eq. to 'sub'. : (+)
9531
9532#@cli sub : value[%] : [image] : 'formula' : (no arg) : (+)
9533#@cli : Subtract specified value, image or mathematical expression to selected images, \
9534# or compute the pointwise difference of selected images.
9535#@cli : (eq. to '-').
9536#@cli : $ image.jpg +sub 30% cut 0,255
9537#@cli : $ image.jpg +mirror x sub[-1] [0]
9538#@cli : $ image.jpg sub 'i(w/2+0.9*(x-w/2),y)'
9539#@cli : $ image.jpg +mirror x sub
9540
9541#@cli tan : (+)
9542#@cli : Compute the pointwise tangent of selected images.
9543#@cli : $ image.jpg +normalize {-0.47*pi},{0.47*pi} tan[-1]
9544#@cli : $ 300,1,1,1,'20*x/w+u' +tan display_graph 400,300
9545#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
9546
9547#@cli tanh : (+)
9548#@cli : Compute the pointwise hyperbolic tangent of selected images.
9549#@cli : $ image.jpg +normalize -3,3 tanh[-1]
9550#@cli : $ 300,1,1,1,'4*x/w+u' +tanh display_graph 400,300
9551
9552#@cli xor : value[%] : [image] : 'formula' : (no arg) : (+)
9553#@cli : Compute the bitwise XOR of selected images with specified value, image or mathematical \
9554# expression, or compute the pointwise sequential bitwise XOR of selected images.
9555#@cli : $ image.jpg xor 128
9556#@cli : $ image.jpg +mirror x xor
9557
9558#---------------------------------
9559#
9560#@cli :: Values Manipulation
9561#
9562#---------------------------------
9563
9564#@cli apply_curve : 0<=smoothness<=1,x0,y0,x1,y1,x2,y2,...,xN,yN
9565#@cli : Apply curve transformation to image values.
9566#@cli : Default values: 'smoothness=1', 'x0=0', 'y0=100'.
9567#@cli : $ image.jpg +apply_curve 1,0,0,128,255,255,0
9568apply_curve : check "${1=1}>=0 && $1<=1" skip ${2=0},${3=100}
9569  e[^-1] "Apply intensity curve with smoothness $1 and keypoints (${2--1}) on image$?."
9570
9571  # Determine value mapping.
9572  (${^0,1}) r. 2,{w/2},1,1,-1 +z. 0,0 vm,vM={[im,iM]} n. 0,8191 j.. . function1d[] $1,{-2,^} rm[-3,-2]
9573
9574  # Apply value mapping.
9575  -[^-1] $vm *[^-1] {8191/($vM-$vm)} map[^-1] .,1 rm.
9576
9577#@cli apply_gamma : gamma>=0
9578#@cli : Apply gamma correction to selected images.
9579#@cli : $ image.jpg +apply_gamma 2
9580apply_gamma : check $1>=0
9581  e[^-1] "Apply Gamma-correction to image$?, with gamma $1."
9582  if $1==1 return fi
9583  repeat $! l[$>] mM={[im,iM]} n 0,1 ^ {1/$1} n $mM endl done
9584
9585#@cli balance_gamma : _ref_color1,...
9586#@cli : Compute gamma-corrected color balance of selected image, with respect to specified reference color.
9587#@cli : Default value: 'ref_color1=128'.
9588#@cli : $ image.jpg +balance_gamma 128,64,64
9589balance_gamma : check "isnum(${1=128})"
9590  e[^-1] "Apply gamma-corrected color balance of image$?, with reference color ("${^0}")."
9591  repeat $! l[$>]
9592    (${^0}) r. {-2,s},1,1,1,0,1 s.. c /. 255
9593    repeat $!-1 /[$>] 255 ^[$>] {log({@$>})/log({$>,ia})} *[$>] 255 done
9594    rm. a c c 0,255
9595  endl done
9596
9597#@cli cast : datatype_source,datatype_target
9598#@cli : Cast datatype of image buffer from specified source type to specified target type.
9599#@cli : 'datatype_source' and 'datatype_target' can be \
9600# { uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }.
9601cast :
9602  e[^-1] "Cast datatype of image buffer$? from '$1' to '$2'."
9603  stype="$1"
9604  if s='$stype';s[0]==_'u'" && "s[1]!=_'n' stype="unsigned_"{`s='$stype';s[1,size(s)-1]`}
9605  elif s='$stype';s[0]==_'u'" && "s[0,9]=='"unsigned "' stype="unsigned_"{`s='$stype';s[9,size(s)-9]`}
9606  fi
9607  dtype="$2"
9608  if s='$dtype';s[0]==_'u'" && "s[1]!=_'n' dtype="unsigned_"{`s='$dtype';s[1,size(s)-1]`}
9609  elif s='$dtype';s[0]==_'u'" && "s[0,9]=='"unsigned "' dtype="unsigned_"{`s='$dtype';s[9,size(s)-9]`}
9610  fi
9611  ssize={s='$stype';s=='"unsigned_char"'||s=='char'?1:s=='"unsigned_short"'||s=='short'?2:s=='"unsigned_int"'||\
9612         s=='int'||s=='float'?4:8}
9613  dsize={s='$dtype';s=='"unsigned_char"'||s=='char'?1:s=='"unsigned_short"'||s=='short'?2:s=='"unsigned_int"'||\
9614         s=='int'||s=='float'?4:8}
9615  repeat $! l[$>]
9616    w,h,d,s={[w,h,d,s]}
9617    serialize $1,0,0
9618    s -,{'\n$w\ $h\ $d\ $s\n'}
9619    i[1] ('\n1\ {int($w*$h*$d*$s*$ssize/$dsize)}\ 1\ 1\n') y[1]
9620    replace_str[0] $stype,$dtype
9621    a y unserialize
9622  endl done
9623
9624#@cli complex2polar
9625#@cli : Compute complex to polar transforms of selected images.
9626#@cli : $ image.jpg +fft complex2polar[-2,-1] log[-2] shift[-2] 50%,50%,0,0,2 remove[-1]
9627complex2polar :
9628  e[^-1] "Compute complex to polar transforms of image$?."
9629  repeat int($!/2) l[{2*$>},{2*$>+1}]
9630    ri[1] [0],3 +atan2[1] [0] nm. {1,n} sqr[-3,-2] +[-3,-2] sqrt..
9631  endl done
9632
9633#@cli compress_clut : _max_error>0,_avg_error>0,_max_nbpoints>=8 | 0 (unlimited),\
9634# _error_metric={ 0=L2-norm | 1=deltaE_1976 | 2=deltaE_2000 },_reconstruction_colorspace={ 0=srgb | 1=rgb | 2=lab },\
9635# _try_rbf_first={ 0 | 1 }
9636#@cli : Compress selected color LUTs as sequences of colored keypoints.
9637#@cli : Default values: 'max_error=1.5', 'avg_error=0.75', 'max_nb_points=2048', 'error_metric=2', \
9638# 'reconstruction_colorspace=0' and 'try_rbf_first=1'.
9639compress_clut : check "${1=1.5}>0 && ${2=0.75}>0 && isint(${3=2048}) && (!$3 || $3>=8) && isin(${4=2},0,1,2) && "\
9640                      "isin(${5=0},0,1,2) && isbool(${6=1})"
9641  e[^-1] "Compress color LUT$? as a set of colored keypoints, with maximum error $1, average error $2, "\
9642         "$3 maximum keypoints, "${"s0,s1,s2=L2-RGB,DeltaE_1976,DeltaE_2000 u $s$4"}" metric and "\
9643         ${"s0,s1,s2=srgb,rgb,lab u $s$5"}" colorspace for reconstruction."
9644  v 0
9645  max_error,avg_error,max_keypoints,metric,colorspace,try_rbf=${1-6}
9646  if $try_rbf method=rbf else method=pde fi
9647  repeat $! l[$>] nm={b}
9648    if iM>1024 / 257 fi # When input is 16bits
9649    if d==1 S={round(cbrt(wh))} r $S,$S,$S,100%,-1 fi # When input is a 2D haldclut image
9650    e[] "\n* Process CLUT '"$nm"' ("{w}"x"{h}"x"{d}")."
9651
9652    # Detect B&W cluts.
9653    if "s==3 &&
9654        crop(0,0,0,0,w,h,d,1)==crop(0,0,0,1,w,h,d,1) &&
9655        crop(0,0,0,0,w,h,d,1)==crop(0,0,0,2,w,h,d,1)"
9656      channels 0
9657    fi
9658
9659    _metric={s==3?$metric:0}
9660    if $_metric +srgb2lab fi
9661
9662    # Initialize keypoints (corners of the RGB cube).
9663    1,8,1,{s+3}
9664    eval "
9665      coords = [ 0,0,0, 255,0,0, 255,255,0, 0,255,0, 0,0,255, 255,0,255, 255,255,255, 0,255,255 ];
9666      repeat (size(coords)/3,k,
9667        P = coords[3*k,3];
9668        I[k] = [ P, I(#0,round(P*([w#0,h#0,d#0]-1)/255)) ]
9669      )"
9670
9671    # Iteratively add keypoints.
9672    sep="\n"
9673    do
9674      +decompress_clut_$method. {0,[w,h,d]},$colorspace
9675      if !$_metric -. [0] norm. else srgb2lab. deltaE. [1],{$_metric-1}," " fi
9676      emax,eavg={[iM,ia]}
9677      e[] "\r"$sep"  > Add [#"{-2,h}"] Max_Err = "{_$emax}", Avg_Err = "{_$eavg}"         " sep=""
9678      if $emax<=$max_error" && "$eavg<=$avg_error rm. break fi
9679      1,1,1,{0,s+3},{"P = [ xM,yM,zM ]; [ P*255/[max(1,w#0-1),max(1,h#0-1),max(1,d#0-1)], I(#0,P) ]"} rm..
9680      a[-2,-1] y
9681      if $max_keypoints" && "h>=320" && "'$method'=='rbf' method=pde rows. 0,7 fi # RBF failed, switch to PDE method.
9682    while h<($max_keypoints?$max_keypoints:inf)
9683
9684    # Iteratively remove keypoints.
9685    if h>8
9686      if $_metric max_rounding,avg_rounding=0.1,0.025
9687      else max_rounding,avg_rounding=1,0.25
9688      fi
9689      if $emax>$max_error" || "$eavg>$avg_error
9690        max_error=round($max_error,$max_rounding,1)
9691        avg_error=round($avg_error,$avg_rounding,1)
9692      fi
9693      index=8 sep="\n"
9694      do
9695        +l. s y rm[$index] a y endl # Remove kth keypoint
9696        +decompress_clut_$method. {0,[w,h,d]},$colorspace
9697        if $_metric==0 -. [0] norm. else srgb2lab. deltaE. [1],{$_metric-1}," " fi
9698        emax,eavg={[iM,ia]} rm.
9699
9700        if $emax<=$max_error" && "$eavg<=$avg_error rv[-2,-1] else index+=1 fi
9701        e[] "\r"$sep"  > Rem [#"{min($index,h-1)}"/"{h}"] Max_Err = "{_$emax}", Avg_Err = "{_$eavg}"       "
9702        sep=""
9703        rm.
9704      while $index<h
9705    fi
9706    k.
9707
9708    # Sort keypoints in lexicographic order (increasing order for RBF, decreasing for PDE).
9709    1,100%,1,1,"P = I(#0); P[0]*65536 + P[1]*256 + P[2]" rv a x sort {`'$method'=='rbf'?_'+':_'-'`},y z. 1,100%
9710
9711    to_clutname $nm nm ${}
9712  endl done
9713
9714# compress_cluts : {"file_pattern" | "file_list.txt"},_max_error>0,_avg_error>0,_max_nb_points>=8
9715# Batch compress CLUT files.
9716compress_cluts : check "${2=1.5}>0 && ${3=0.75}>0 && isint(${4=2048}) && $4>8"
9717  rm
9718  if isfile(['{/"$1"}']) it[] "$1" s -,{'\n'}
9719  else files "$1" ('${}') s -,{','}
9720  fi
9721  rv
9722  repeat $! l[$<]
9723    filename={t} 0 nm. $filename ext={x} rm
9724    basename $filename
9725    l[] ('${}') replace_str .$ext,"" basename={t} rm endl
9726    need_compression=1
9727    cclut=cclut_$basename.gmz
9728
9729    if isfile(['{/$cclut}'])
9730      i $cclut
9731      if "dat = (date(5) + 60*(date(4) + date(2)*24));
9732          fdat = (date(5,'"{/$cclut}"') + 60*(date(4,'"{/$cclut}"') + date(2,'"{/$cclut}"')*24));
9733          !h && dat-fdat<30"
9734        e[] "* Skip file '"$filename"' (CLUT already being compressed)."
9735        need_compression=0
9736      elif h>0" && "h!=2048
9737        e[] "* Skip file '"$filename"' (CLUT already compressed)."
9738        need_compression=0
9739      fi
9740      rm.
9741    fi
9742
9743    if $need_compression
9744      0 o. $cclut rm. # Lock current file
9745      if lowercase(['$ext'])=='cube' input_cube $filename c. 0,255
9746      elif lowercase(['$ext'])=='png' i $filename S={round(cbrt(wh))} r $S,$S,$S,100%,-1
9747      else e[] "* Skip file '"$filename"' (unknown CLUT format)." continue
9748      fi
9749
9750      e[] "* Compress file '"$filename"'."
9751      if w>33 r3dx 33 fi
9752      to_rgb
9753      tic compress_clut $2,$3,$4 toc
9754      o $cclut
9755    fi
9756    rm
9757  endl done
9758
9759# cluts2libclut : "compressed_CLUT_collection.gmz"
9760# Convert specified compressed CLUT collection for C++ 'libclut'
9761# (https://framagit.org/dtschump/libclut)
9762cluts2libclut : skip ${1=$HOME/work/src/gmic/resources/gmic_cluts.gmz}
9763  e[] "Convert compressed CLUT collection '$1' to .png/.ppm/.txt for C++ libclut."
9764  l[]
9765    i $1
9766    0 nm. "$1" basename={b} rm.
9767    list=
9768    repeat $! l[$>] if s<6 r 100%,100%,1,6,0,1 fi transpose list.={n}"\n" endl done s c,2 a y
9769    o. $basename.png
9770    o. $basename.ppm
9771    ('$list') ot. $basename.txt
9772    rm
9773  endl
9774
9775#@cli compress_rle : _is_binary_data={ 0 | 1 },_maximum_sequence_length>=0
9776#@cli : Compress selected images as 2xN data matrices, using RLE algorithm.
9777#@cli : Set 'maximum_sequence_length=0' to disable maximum length constraint.
9778#@cli : Default values: 'is_binary_data=0' and 'maximum_sequence_length=0'.
9779#@cli : $ image.jpg resize2dy 100 quantize 4 round +compress_rle , +decompress_rle[-1]
9780compress_rle : check "isbool(${1=0}) && isint(${2=0}) && $2>=0"
9781  s0=" for binary data" s1=""
9782  if $2 s=", with maximal sequence length "$2 else s="" fi
9783  e[^-1] "Compress image$? using RLE algorithm"${s{!$1}}$s"."
9784  repeat $! l[$>] nm={0,n} im={im} header={w};{h};{d};{s};$im;{$1!=0}
9785    - $im y x ({{0,@-1}+1}) a x r 100%,3
9786    f '>!y?i:y==1?(i(x,0)==i(x+1,0)?-1:x):(i(x-1,1)==-1?i(x-1,2)+1:1)'
9787    if $2 # Constrain maximum sequence length.
9788      transpose mirror x
9789      f. '>x==2?i:!x?(j(0,-1)==$2?1:i!=1?j(0,-1)+1:1):(i==-1&&j(-1)==$2?y:i)'
9790      mirror x transpose
9791    fi
9792    z 0,{w-2} s y,3 y[1] discard[1] -1 warp[0,2] [1],0,0 rm[1]
9793    if $1 # Encode for binary data.
9794      !=[0] 0 *[0] 2 -[0] 1 *
9795    else # Encode for arbitrary data.
9796      *. -1 rv a x y discard -1 f '>i(0,y-1)<0&&i==0&&i(0,y+1)<0?-1:i' discard -1
9797    fi
9798    i[0] ($header) a y nm $nm
9799  endl done
9800
9801#@cli cumulate : { x | y | z | c }...{ x | y | z | c } : (no arg) : (+)
9802#@cli : Compute the cumulative function of specified image data, optionally along the specified axes.
9803#@cli : $ image.jpg +histogram +cumulate[-1] display_graph[-2,-1] 400,300,3
9804
9805#@cli c : eq. to 'cut'. : (+)
9806
9807#@cli cut : { value0[%] | [image0] },{ value1[%] | [image1] } : [image] : (+)
9808#@cli : Cut values of selected images in specified range.
9809#@cli : (eq. to 'c').\n
9810#@cli : $ image.jpg +add 30% cut[-1] 0,255
9811#@cli : $ image.jpg +cut 25%,75%
9812
9813#@cli decompress_clut : _width>0,_height>0,_depth>0,_reconstruction_colorspace={ 0=srgb | 1=rgb | 2=lab }
9814#@cli : Decompress selected colored keypoints into 3D CLUTs, using a mixed RBF/PDE approach.
9815#@cli : Default values: 'width=height=depth=33' and 'reconstruction_colorspace=0'.
9816decompress_clut : check "isint(${1=33}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=$1}) && $3>0 && "\
9817                        "isin(${4=0},0,1,2)"
9818  e[^-1] "Decompress colored keypoint$? into $1x$2x$3 CLUTs, using "\
9819         ${"s0,s1=srgb,rgb u $s$4"}" colorspace for reconstruction."
9820  repeat $! l[$>]
9821    if "h>=320 || (P0 = I[0]; P1 = I[h - 1]; val(P) = (P[0]*65536 + P[1]*256 + P[2]); val(P0)>val(P1))"
9822      decompress_clut_pde. ${1-4}
9823    else
9824      decompress_clut_rbf. ${1-4}
9825    fi
9826  endl done
9827
9828#@cli decompress_clut_rbf : _width>0,_height>0,_depth>0,_reconstruction_colorspace={ 0=srgb | 1=rgb | 2=lab }
9829#@cli : Decompress selected colored keypoints into 3D CLUTs, using RBF thin plate spline interpolation.
9830#@cli : Default value: 'width=height=depth=33' and 'reconstruction_colorspace=0'.
9831decompress_clut_rbf : check "isint(${1=33}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=$1}) && $3>0 && "\
9832                            "isin(${4=0},0,1,2)"
9833  e[^-1] "Decompress colored keypoint$? into $1x$2x$3 CLUTs (RBF approach), using "\
9834         ${"s0,s1,s2=srgb,rgb,lab u $s$4"}" colorspace for reconstruction."
9835  repeat $! l[$>]
9836    if $4 s c,-3 srgb2${"arg $4,rgb,lab"}. a c fi
9837    rbf $1,$2,$3,0,0,0,255,255,255
9838    if $4 ${"arg $4,rgb,lab"}2srgb fi
9839  endl done
9840
9841#@cli decompress_clut_pde : _width>0,_height>0,_depth>0,_reconstruction_colorspace={ 0=srgb | 1=rgb | 2=lab }
9842#@cli : Decompress selected colored keypoints into 3D CLUTs, using multiscale diffusion PDE's.
9843#@cli : Default values: 'width=height=depth=33' and 'reconstruction_colorspace=0'.
9844decompress_clut_pde : check "isint(${1=33}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=$1}) && $3>0 && "\
9845                            "isin(${4=0},0,1,2)"
9846  e[^-1] "Decompress colored keypoint$? into $1x$2x$3 CLUTs (PDE approach), using "\
9847         ${"s0,s1,s2=srgb,rgb,lab u $s$4"}" colorspace for reconstruction."
9848  repeat $! l[$>] nm={n}
9849    if $4 s c,-3 srgb2${"arg $4,rgb,lab"}. a c fi
9850    2,2,2,{s-3}
9851    do
9852      +f. 0 .,.,.,1
9853      eval[0] "begin(fact = ([w#1,h#1,d#1] - 1)/255); PC = I; P = PC[0,3]; X = round(P*fact);
9854               I(#2,X)+=PC[3,s-3]; ++i(#3,X); I"
9855      f. "*i?(I(#2)/=i;1):0"
9856      if im rm[-3,-1] # No missing data so far
9857      else # Reconstruct missing data by anisotropic diffusion scheme
9858        +distance. 1 .,.,.,3
9859        eval.. "* # Specific gradient discretization for distance function
9860          const boundary = 1;
9861          maxabs(a,b) = (abs(a)>abs(b)?a:b);
9862          I(#-1) = [ maxabs(j(1) - i,i - j(-1)),
9863                     maxabs(j(0,1) - i,i - j(0,-1)),
9864                     maxabs(j(0,0,1) - i,i - j(0,0,-1)) ]"
9865        orientation. rm..
9866        repeat 20
9867          j[-4] ...,0,0,0,0,1,..
9868          +warp[-4] .,1,2,1 *.. -1 warp[-5] ..,1,2,1 +[-5,-1] /[-4] 2
9869        done
9870        j[-4] ...,0,0,0,0,1,.. k[0,1]
9871      fi
9872      if "w<$1 || h<$2 || d<$3" r. {[min($1,2*w),min($2,2*h),min($3,2*d)]},100%,3 else break fi
9873    while 1
9874    k.
9875    if $4 ${"arg $4,rgb,lab"}2srgb fi
9876    nm $nm
9877  endl done
9878  um srgb2srgb
9879
9880#@cli decompress_rle
9881#@cli : Decompress selected data vectors, using RLE algorithm.
9882decompress_rle :
9883  e[^-1] "Decompress data vector$?, using RLE algorithm."
9884  repeat $! l[$>]
9885
9886    # Retrieve original data dimension and min value.
9887    y whds={0,@0-3} im={0,@4} is_binary_data={0,@5} rows 6,100%
9888
9889    # Transform RLE data to list of pairs {nb_occurrences,value}.
9890    if $is_binary_data  # Decode for binary data.
9891      +>= 0 abs[0] a x
9892    else # Decode for arbitrary data
9893      +<. 0
9894      (0;1;1)
9895      erode.. .,0 rm. -. 1 a x discard -1 # Get back compressed '0' (minimum) values.
9896      +< 0 (0;1;1) dilate.. . rm. *. -2 +. 1 # Get back singletons.
9897      rv abs. a x discard -1
9898      r 2,{h/2},1,1,-1
9899    fi
9900
9901    # Decompress, using 3D objects.
9902    s y,-256
9903    repeat $! l[$>]
9904      i[0] ('CImg3d') +[0] 0.5
9905      i[1] ({2*h};{h})
9906      s. x,2 cumulate.. siz={-2,@-1}
9907      +shift.. 0,1 -... 1 rv[-3,-1] z[-3,-1] 0,2 a[-3,-1] x
9908      i[3] (2,0,1;2,{2*h-2},{2*h-1}) r[3] 3,{h},1,1,3 round[3]
9909      r[4] 3 1,100%,1,1,1 y a y
9910      $siz j3d. ..,0,0,0,1,2,0,0 rm..
9911    endl done
9912    a x r $whds,-1 + $im
9913  endl done
9914
9915#@cli discard : _value1,_value2,... : { x | y | z | c}...{ x | y | z | c},_value1,_value2,... : (no arg) : (+)
9916#@cli : Discard specified values in selected images or discard neighboring duplicate values,
9917#@cli : optionally only for the values along the first of a specified axis.
9918#@cli : If no arguments are specified, neighboring duplicate values are discarded.
9919#@cli : If all pixels of a selected image are discarded, an empty image is returned.
9920#@cli : $ (1;2;3;4;3;2;1) +discard 2
9921#@cli : $ (1,2,2,3,3,3,4,4,4,4) +discard x
9922
9923#@cli eigen2tensor
9924#@cli : Recompose selected pairs of eigenvalues/eigenvectors as 2x2 or 3x3 tensor fields.
9925#@cli : $$
9926eigen2tensor :
9927  e[^-1] "Recompose pairs in eigen field$? as 2x2 or 3x3 tensor fields."
9928  repeat $!/2 l[$>,{$>+1}] nm={0,n}
9929    if s==2 # 2D tensors.
9930      s. c
9931      +sqr. *.. ... sqr... # u^2 uv v^2
9932      sh. +*... -1
9933      sh[-5]     # v^2 -uv u^2
9934      a[-3--1] c a[-4--2] c
9935      sh... 0 *[-3,-1]          # l1*(u^2;uv;v^2)
9936      sh... 1 *[-2,-1]          # l2*(v^2;-uv;u^2)
9937      rm... +[-2,-1]
9938    elif s==6 # 3D tensors.
9939      s. c
9940      l[-6--4] +sqr.. +*[-2,-3] +sqr... *[-5] [-6] *[-4] [-6] sqr[-6] a c endl
9941      l[-3--1] +sqr.. +*[-2,-3] +sqr... *[-5] [-6] *[-4] [-6] sqr[-6] a c endl
9942      s... c
9943      -[-5] ... -[-4] ... *.. [-5] *. [-4]
9944      (1^0^0^1^0^1) ri. ... *. [-4] rm[-6--4] +[-3--1]
9945    else error[0--3] "Command '$0': Invalid image ["{$!-$>-1}"] : Dimensions "{w}","{h}","{d}","{s}" does
9946                          not represent a field of 2D or 3D eigenvectors."
9947    fi
9948  nm $nm endl done
9949
9950#@cli endian : _datatype : (+)
9951#@cli : Reverse data endianness of selected images, eventually considering the pixel being of the specified datatype.
9952#@cli : 'datatype' can be { bool | uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }.
9953#@cli : This command does nothing for 'bool', 'uchar' and 'char' datatypes.
9954
9955#@cli equalize : _nb_levels>0[%],_value_min[%],_value_max[%] : (+)
9956#@cli : Equalize histograms of selected images.
9957#@cli : If value range is specified, the equalization is done only for pixels in the specified
9958#@cli : value range.
9959#@cli : Default values: 'nb_levels=256', 'value_min=0%' and 'value_max=100%'.
9960#@cli : $ image.jpg +equalize
9961#@cli : $ image.jpg +equalize 4,0,128
9962
9963#@cli f : eq. to 'fill'. : (+)
9964
9965#@cli fill : value1,_value2,... : [image] : 'formula' : (+)
9966#@cli : Fill selected images with values read from the specified value list, existing image
9967#@cli : or mathematical expression. Single quotes may be omitted in 'formula'.
9968#@cli : (eq. to 'f').
9969#@cli : $ 4,4 fill 1,2,3,4,5,6,7
9970#@cli : $ 4,4 (1,2,3,4,5,6,7) fill[-2] [-1]
9971#@cli : $ 400,400,1,3 fill "X=x-w/2; Y=y-h/2; R=sqrt(X^2+Y^2); a=atan2(Y,X); \
9972# if(R<=180,255*abs(cos(c+200*(x/w-0.5)*(y/h-0.5))),850*(a%(0.1*(c+1))))"
9973#@cli : $$
9974
9975#@cli index : { [palette] | palette_name },0<=_dithering<=1,_map_palette={ 0 | 1 } : (+)
9976#@cli : Index selected vector-valued images by specified vector-valued palette.
9977#@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | algae | amp |\
9978# balance | curl | deep | delta | dense | diff | haline | ice | matter | oxy | phase | rain | solar | speed | tarn |\
9979# tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | uzebox }
9980#@cli : Default values: 'dithering=0' and 'map_palette=0'.
9981#@cli : $ image.jpg +index 1,1,1
9982#@cli : $ image.jpg (0;255;255^0;128;255^0;0;255) +index[-2] [-1],1,1
9983#@cli : $$ https://gmic.eu/tutorial/gindex
9984index : check "${2=0}>=0 && $2<=1 && isbool(${3=0})"
9985  names=${-_palette_names} N={narg($names)}
9986  l[] if isint("$1") name=${"arg 1+($1%"$N"),"$names} else name="$1" fi onfail name="$1" endl
9987  e[^-1] "Index values in image$? by color LUT '"$name"', with dithering level $2."
9988  palette $1 index[^-1] .,$2,$3 rm.
9989
9990#@cli ir : eq. to 'inrange'.
9991ir : check "isbool(${3=1}) && isbool(${4=$3})"
9992  _gmic_s="$?" v + _inrange $*
9993
9994#@cli inrange : min[%],max[%],_include_min_boundary={ 0=no | 1=yes },_include_max_boundary={ 0=no | 1=yes }
9995#@cli : Detect pixels whose values are in specified range `[min,max]`, in selected images.
9996#@cli : (eq. to 'ir').
9997#@cli : Default value: 'include_min_boundary=include_max_boundary=1'.
9998#@cli : $ image.jpg +inrange 25%,75%
9999inrange : check "isbool(${3=1}) && isbool(${4=$3})"
10000  _gmic_s="$?" v + _$0 $*
10001
10002_inrange : skip "${3=1},${4=$3}"
10003  v={$3+2*$4}
10004  s=${"arg 1+"$v",out,\" min\",\" max\",\" min and max\""}
10005  if $v%3 b="y" else b="ies" fi
10006  e[0--3] "Extract pixel values in range [$1,$2] in image"$_gmic_s", with"$s" boundar"$b" included."
10007  repeat $! l[$>]
10008    m=$1 M=$2
10009    if ${is_percent\ $1} m={im+(iM-im)*$1} fi
10010    if ${is_percent\ $2} M={im+(iM-im)*$2} fi
10011    f. inrange(i,$m,$M,$3)
10012  endl done
10013
10014#@cli map : [palette],_boundary_conditions : palette_name,_boundary_conditions : (+)
10015#@cli : Map specified vector-valued palette to selected indexed scalar images.
10016#@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | algae | amp | \
10017# balance | curl | deep | delta | dense | diff | gray | haline | ice | matter | oxy | phase | rain | solar | speed | \
10018# tarn | tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | uzebox }
10019#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
10020#@cli : Default value: 'boundary_conditions=0'.
10021#@cli : $ image.jpg +luminance map[-1] 3
10022#@cli : $ image.jpg +rgb2ycbcr split[-1] c (0,255,0) resize[-1] 256,1,1,1,3 map[-4] [-1] remove[-1] append[-3--1] c \
10023# ycbcr2rgb[-1]
10024#@cli : $$
10025map : check "isint(${2=0}) && $2>=0 && $2<=3"
10026  s0,s1,s2,s3=dirichlet,neumann,periodic,mirror boundary=${s$2}
10027  names=${-_palette_names} N={narg($names)}
10028  l[] if isint("$1") name=${"arg 1+($1%"$N"),"$names} else name="$1" fi onfail name="$1" endl
10029  e[^-1] "Map color LUT '"$name"' on image$?, with "$boundary" boundary conditions."
10030  palette $1 map[^-1] .,$2 rm.
10031
10032#@cli mix_channels : (a00,...,aMN) : [matrix]
10033#@cli : Apply specified matrix to channels of selected images.
10034#@cli : $ image.jpg +mix_channels (0,1,0;1,0,0;0,0,1)
10035mix_channels :
10036  e[^-1] "Apply matrix $1 to channels of image$?."
10037  if ${"is_image_arg $1"} pass$1 1 else i ${^0} fi
10038  repeat $!-1 l[$>] nm={n}
10039    whd={[w,h,d]} r. {[whd,s]},1,1,-1
10040    pass. 0 mv. 0 m* r $whd,{h},-1
10041  nm $nm endl done rm.
10042
10043#@cli negate : base_value : (no arg)
10044#@cli : Negate image values.
10045#@cli : Default value: 'base_value=(undefined)'.
10046#@cli : $ image.jpg +negate
10047negate : skip "${1=,}"
10048  if isnum("$*")
10049    e[0--3] "Negate values of image$?, according to base value $*."
10050    - {"$*"} * -1
10051  else
10052    e[0--3] "Negate values of image$?."
10053    repeat $! -[$>] {$>,iM} done * -1
10054    if ['"$1"']!=',' noarg fi
10055  fi
10056
10057#@cli noise : std_deviation>=0[%],_noise_type : (+)
10058#@cli : Add random noise to selected images.
10059#@cli : 'noise_type' can be { 0=gaussian | 1=uniform | 2=salt&pepper | 3=poisson | 4=rice }.
10060#@cli : Default value: 'noise_type=0'.
10061#@cli : $ image.jpg +noise[0] 50,0 +noise[0] 50,1 +noise[0] 10,2 cut 0,255
10062#@cli : $ 300,300,1,3 [0] noise[0] 20,0 noise[1] 20,1 +histogram 100 display_graph[-2,-1] 400,300,3
10063
10064#@cli noise_perlin : _scale_x[%]>0,_scale_y[%]>0,_scale_z[%]>0,_seed_x,_seed_y,_seed_z
10065#@cli : Render 2D or 3D Perlin noise on selected images, from specified coordinates.
10066#@cli : The Perlin noise is a specific type of smooth noise,
10067#@cli : described here : <https://en.wikipedia.org/wiki/Perlin_noise>.
10068#@cli : Default values: 'scale_x=scale_y=scale_z=16' and 'seed_x=seed_y=seed_z=0'.
10069#@cli : $ 500,500,1,3 noise_perlin ,
10070noise_perlin : check "${1=16}>0 && ${2=$1}>0 && ${3=$1}>0 && isnum(${4=0}) && isnum(${5=0}) && isnum(${6=0})"
10071  e[^-1] "Render Perlin noise on image$?, with scales (${1-3}) and seeds (${4-6})."
10072
10073  init="permutation = [ 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,
10074                        21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,
10075                        237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,
10076                        83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,
10077                        216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,
10078                        173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,
10079                        16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,
10080                        43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,
10081                        34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,
10082                        181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,
10083                        24,72,243,141,128,195,78,66,215,61,156,180 ];
10084        p = [ permutation,permutation ];
10085        fade(t) = (t*t*t*(t*(t*6 - 15) + 10));
10086        _lerp(t,a,b) = lerp(a,b,t);
10087        pcmod255 = vectors(); repeat (s,k,pcmod255[k] = p[k%255]);
10088        sx = ['$1']; is_px = sx[size(sx) - 1]==_'%';
10089        sy = ['$2']; is_py = sy[size(sy) - 1]==_'%';
10090        sz = ['$3']; is_pz = sz[size(sz) - 1]==_'%';
10091        x0 = $4;
10092        y0 = $5;
10093        z0 = $6;
10094        x1 = x0 + (is_px?1/$1:w/$1);
10095        y1 = y0 + (is_py?1/$2:h/$2);
10096        z1 = z0 + (is_pz?1/$3:d/$3);
10097        fw = (x1 - x0)/max(w - 1,1);
10098        fh = (y1 - y0)/max(h - 1,1);
10099        fd = (z1 - z0)/max(d - 1,1);"
10100
10101  repeat $! if {$>,d>1} # 3D version
10102    f[$>] "*begin("$init"
10103         grad(hash,x,y,z) = ( gh = hash&15; gu = gh<8?x:y; gv = gh<4?y:gh==12 || gh==14?x:z;
10104                             (!(gh&1)?gu:-gu) + (!(gh&2)?gv:-gv))
10105       );
10106       x = x0 + x*fw + pcmod255[c]; y = y0 + y*fh + pcmod255[c]; z = z0 + z*fd + pcmod255[c];
10107       ix = floor(x); iy = floor(y); iz = floor(z);
10108       X = ix&255; Y = iy&255; Z = iz&255;
10109       fx = x - ix; fy = y - iy; fz = z - iz;
10110       u = fade(fx); v = fade(fy); w = fade(fz);
10111       A = p[X] + Y; AA = p[A] + Z; AB = p[A + 1] + Z;
10112       B = p[X + 1] + Y; BA = p[B] + Z; BB = p[B + 1] + Z;
10113       fx1 = fx - 1; fy1 = fy - 1; fz1 = fz - 1;
10114       lerp(lerp(lerp(grad(p[AA],fx,fy,fz),
10115                      grad(p[BA],fx1,fy,fz),u),
10116                 lerp(grad(p[AB],fx,fy1,fz),
10117                      grad(p[BB],fx1,fy1,fz),u),v),
10118            lerp(lerp(grad(p[AA + 1],fx,fy,fz1),
10119                      grad(p[BA + 1],fx1,fy,fz1,u),
10120                 lerp(grad(p[AB + 1],fx,fy1,fz1),
10121                      grad(p[BB+1],fx1,fy1,fz1),u),v),w)"
10122  else # 2D version
10123    f[$>] "*begin("$init"
10124         grad(hash,x,y) = ( gh = hash&15; gu = gh<8?x:y; gv = gh<4?y:gh==12 || gh==14?x:0;
10125                            (!(gh&1)?gu:-gu) + (!(gh&2)?gv:-gv))
10126       );
10127       x = x0 + x*fw + pcmod255[c]; y = y0 + y*fh + pcmod255[c];
10128       ix = floor(x); iy = floor(y);
10129       X = ix&255; Y = iy&255;
10130       fx = x - ix; fy = y - iy;
10131       u = fade(fx); v = fade(fy);
10132       A = p[X] + Y; B = p[X + 1] + Y;
10133       fx1 = fx - 1; fy1 = fy - 1;
10134       lerp(lerp(grad(p[A],fx,fy),
10135                 grad(p[B],fx1,fy),u),
10136            lerp(grad(p[A + 1],fx,fy1),
10137                 grad(p[B + 1],fx1,fy1),u),v)"
10138  fi done
10139
10140#@cli noise_poissondisk : _radius[%]>0,_max_sample_attempts>0
10141#@cli : Add poisson disk sampling noise to selected images.
10142#@cli : Implements the algorithm from the article "Fast Poisson Disk Sampling in Arbitrary Dimensions",
10143#@cli : by Robert Bridson (SIGGRAPH'2007).
10144#@cli : Default values: 'radius=8' and 'max_sample_attempts=30'.
10145#@cli : $ 300,300 noise_poissondisk 8
10146##### : Original G'MIC code by Garagecoder (Andy Kelday)
10147noise_poissondisk : check "${1=8}>0 && ${2=30}>0"
10148  e[^-1] "Add poisson disk sampling points to image$?, with radius $1 and max sample attempts $2."
10149  repeat $! l[$>]
10150    R={${"is_percent $1"}?max(w,h,d)*$1:$1} # [0] input image to draw samples on
10151    dim={d>1?3:h>1?2:1} cw={0.999*$R/sqrt($dim)} # dimensions, grid cell width
10152    ({[w,h,d,1]}) y. c  # [1] image dimensions vector
10153    {[ceil(I/$cw)]}     # [2] "accelerator" grid/cells
10154    r[1] 1,1,1,$dim,-1  # keep only used dimensions in [1]
10155    1,1,1,$dim 1,1,1,1  # [3] samples list, [4] active list
10156    {vector$dim(2*ceil(sqrt($dim))+1)} r. 100%,100%,100%,2 # [5] cell proximity kernel
10157    f. "P=[x,y,z]-int([w/2,h/2,d/2]);[sum(sqr(P)),dot(P,[1,w#2,w#2*h#2])]"
10158    r. {[whd,s,1,1,-1]} sort. +,x z. 0,1,100%,100% y. c # sort kernel by distance
10159    nm[1] dims nm[2] grid nm[3] samples nm[4] active nm[5] prox
10160    eval ${-math_lib}"
10161      begin(
10162        dotoff = resize([ 1,w#2,w#2*h#2 ],d#2>1?3:h#2>1?2:1,0);
10163      );
10164      const N = "$dim";
10165      const radius = "$R";
10166      const grid_cw = "$cw";
10167      const max_sample_attempts = $2;
10168      const value = iM#0 + (im#0==iM#0);
10169      mag2(vec) = sum(sqr(vec));
10170      prox = I#5;
10171      lim = I#1;
10172
10173      dar_insert(#3,I#1,0);               # dummy sample to simplify bounds checks
10174      dar_insert(#3,u(I#1),1);            # add initial sample to list
10175      dar_insert(#4,1,0);                 # add its index to active list
10176      I(#2,int(I[#3,1]/grid_cw)) = 1;     # add its index to grid cell
10177      I(#0,I[#3,1]) = value;              # draw the point
10178
10179      while (dar_size(#4)>0,
10180        R = int(u(dar_size(#4)-1e-4));    # choose a random active list index
10181        P = i[#4,R];                      # get the index of that sample
10182        T = I[#3,P];                      # position vector of that sample
10183
10184        repeat (max_sample_attempts,attempts,
10185          do (S = 4*(u(vectorN(1)) - 0.5); M = mag2(S), M <= 1 || M > 4);
10186
10187          X = T + radius * S;             # potential sample from annulus around T
10188          if (min(X)<0 || min(lim-X)<0, continue()); # check within bounds
10189
10190          # check proximity of surrounding points
10191          G = int(X/grid_cw);             # grid cell position vector
10192          GI = dot(G,dotoff);    # grid cell direct buffer index
10193
10194          for (K = 0; rejected = 0, K<size(prox), ++K,
10195            V = i[#2,GI+prox[K]];         # sample index from grid to check
10196            if (V>0 && mag2(I[#3,V]-X)<sqr(radius), rejected = 1; break())
10197          );
10198
10199          if (!rejected,
10200            Q = dar_size(#3);               # sample found, get new index
10201            dar_insert(#3,X);               # insert into samples list
10202            dar_insert(#4,Q);               # insert its index into active
10203            I(#2,G) = Q;                    # insert its index into grid
10204            I(#0,X) = value;                # draw the point
10205            break();
10206          );
10207        );
10208        if (attempts==max_sample_attempts, dar_remove(#4,R));
10209      )"
10210    k[0]
10211  endl done
10212
10213#@cli normp : p>=0
10214#@cli : Compute the pointwise Lp-norm norm of vector-valued pixels in selected images.
10215#@cli : Default value: 'p=2'.
10216#@cli : $ image.jpg +normp[0] 0 +normp[0] 1 +normp[0] 2 +normp[0] inf
10217normp : check "isnum(${1==2}) && $1>=0"
10218  e[^-1] "Compute pointwise L"$1"-norm of vectors, in image$?."
10219  if $1==0 != 0 compose_channels +
10220  elif $1==1 abs compose_channels +
10221  elif $1==2 norm
10222  elif $1==inf abs compose_channels max
10223  else ^ $1 compose_channels + ^ {1/$1}
10224  fi
10225
10226#@cli norm
10227#@cli : Compute the pointwise euclidean norm of vector-valued pixels in selected images.
10228#@cli : $ image.jpg +norm
10229#@cli : $$
10230norm :
10231  e[^-1] "Compute pointwise euclidean norm of vectors, in image$?."
10232  sqr compose_channels + sqrt
10233
10234#@cli n : eq. to 'normalize'. : (+)
10235
10236#@cli normalize : { value0[%] | [image0] },{ value1[%] | [image1] },_constant_case_ratio : [image] : (+)
10237#@cli : Linearly normalize values of selected images in specified range.
10238#@cli : (eq. to 'n').
10239#@cli : $ image.jpg split x,2 normalize[-1] 64,196 append x
10240#@cli : $$
10241
10242#@cli normalize_l2
10243#@cli : Normalize selected images such that they have a unit L2 norm.
10244normalize_l2 :
10245  e[^-1] "Normalize image$?, s.t they have a unit L2 norm."
10246  repeat $! /[$>] {norm={$>,in};if(norm!=0,norm,1)} done
10247
10248#@cli normalize_sum
10249#@cli : Normalize selected images such that they have a unit sum.
10250#@cli : $ image.jpg +histogram normalize_sum[-1] display_graph[-1] 400,300
10251normalize_sum :
10252  e[^-1] "Normalize image$?, s.t they have a unit sum."
10253  repeat $! /[$>] {sum={$>,is};if(sum!=0,sum,1)} done
10254
10255#@cli not
10256#@cli : Apply boolean not operation on selected images.
10257#@cli : $ image.jpg +ge 50% +not[-1]
10258not :
10259  e[^-1] "Apply boolean not operation on image$?."
10260  == 0
10261
10262#@cli orientation
10263#@cli : Compute the pointwise orientation of vector-valued pixels in selected images.
10264#@cli : $ image.jpg +orientation +norm[-2] negate[-1] mul[-2] [-1] reverse[-2,-1]
10265#@cli : $$
10266orientation :
10267  e[^-1] "Compute pointwise orientation vectors, in image$?."
10268  repeat $! +norm[$>] replace. 0,1 /[$>,-1] done
10269
10270#@cli oneminus
10271#@cli : For each selected image, compute one minus image.
10272#@cli : $ image.jpg normalize 0,1 +oneminus
10273oneminus :
10274  e[^-1] "Compute one minus selected images$?."
10275  * -1 + 1
10276
10277#@cli otsu : _nb_levels>0
10278#@cli : Hard-threshold selected images using Otsu's method.
10279#@cli : The computed thresholds are returned as a list of values in the status.
10280#@cli : Default value: 'nb_levels=256'.
10281#@cli : $ image.jpg luminance +otsu ,
10282otsu : check "isint(${1=256}) && $1>0"
10283  e[^-1] "Hard-threshold image$? using Otsu\47s method, with $1 histogram levels."
10284  repeat $! l[$>]
10285    imM={[im,iM]} +histogram $1,$imM
10286    otsu={"
10287      sum = sumB = wB = best_variance = best_t = 0;
10288      repeat (w,t,sum+=t*i[t]);
10289      repeat (w,t,
10290        wB+=i[t];
10291        if (!wB, continue());
10292        wF = whds#-2 - wB;
10293        if (!wF, break());
10294        sumB+=t*i[t];
10295        mB = sumB/wB;
10296        mF = (sum - sumB)/wF;
10297        variance = wB*wF*(mB - mF)^2;
10298        if (variance>best_variance, best_variance = variance; best_t = t);
10299      );
10300      imM = ["$imM"];
10301      imM[0] + best_t*(imM[1] - imM[0])/(w - 1)"}
10302    rm. >=. $otsu
10303    if $> u ${},$otsu else u $otsu fi
10304  endl done
10305
10306#@cli polar2complex
10307#@cli : Compute polar to complex transforms of selected images.
10308polar2complex :
10309  e[^-1] "Compute polar to complex transforms of image$?."
10310  repeat int($!/2) l[{2*$>},{2*$>+1}]
10311    ri[1] [0],3 +sin. cos.. *. ... *[-3,-2]
10312  endl done
10313
10314#@cli quantize : nb_levels>=1,_keep_values={ 0 | 1 },_quantization_type={ -1=median-cut | 0=k-means | 1=uniform }
10315#@cli : Quantize selected images.
10316#@cli : Default value: 'keep_values=1' and 'quantization_type=0'.
10317#@cli : $ image.jpg luminance +quantize 3
10318#@cli : $ 200,200,1,1,'cos(x/10)*sin(y/10)' +quantize[0] 6 +quantize[0] 4 +quantize[0] 3 +quantize[0] 2
10319quantize : check "isint($1) && $1>=1 && isbool(${2=1}) && isint(${3=0}) && $3>=-1 && $3<=1"
10320  e[^-1] "Quantize image$? using $1 levels, "${arg\ 1+!$2,with,without}" keeping value range."
10321  repeat $! l[$>]
10322    if $3==1 # Uniform quantization.
10323      if s==1 # Greyscale image.
10324        if $2 mM={[im,iM]} n 0,$1 round 1,-1 min {$1-1} n $mM
10325        else n 0,$1 round 1,-1 min {$1-1} fi
10326      else mM={[im,iM]} uniform_distribution $1,{s} n. $mM index.. .,0,$2 rm.
10327      fi
10328    else +colormap $1,{!$3},1 index.. .,0,$2 rm. # Non-uniform quantization.
10329    fi
10330  endl done
10331
10332#@cli quantize_area : _min_area>0
10333#@cli : Quantize selected images such that each flat region has an area greater or equal to 'min_area'.
10334#@cli : Default value: 'min_area=10'.
10335#@cli : $ image.jpg quantize 3 +blur 1 round[-1] +quantize_area[-1] 2
10336quantize_area : check "${1=10}>0"
10337  e[^-1] "Quantize image$? by regions of areas greater than $1."
10338  if $1==1 return fi
10339  repeat $! l[$>]
10340    if s>1 +f. "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm. round. 0.01 else [0] fi
10341    area. 0,0 <. $1
10342    do
10343      [0]
10344      f.. "*
10345        begin(
10346          const boundary = 1;
10347          offx = [ -1,1,0,0,0,0 ];
10348          offy = [ 0,0,-1,1,0,0 ];
10349          offz = [ 0,0,0,0,-1,1 ];
10350          nb_offs = d>1?6:h>1?4:2;
10351        );
10352        is_neighbor = j(-1) && j(-1);
10353        if (h>1, is_neighbor&=j(1,0) && j(0,1));
10354        if (d>1, is_neighbor&=j(0,0,-1) && j(0,0,1));
10355        is_neighbor = !is_neighbor;
10356        i && is_neighbor?(
10357          col0 = I(#0);
10358          kmin = -1;
10359          distmin = inf;
10360          repeat (nb_offs,k,
10361            p = offx[k];
10362            q = offy[k];
10363            r = offz[k];
10364            if (!j(p,q,r),
10365              col = J(#0,p,q,r);
10366              dist = norm(col-=col0);
10367              if (dist<distmin, distmin = dist; kmin = k);
10368            );
10369          );
10370          if (kmin>=0,
10371            I(#-1) = J(#0,offx[kmin],offy[kmin],offz[kmin]);
10372            0,
10373            1
10374          );
10375        ):i"
10376      rv[0,-1] rm.
10377    while iM
10378    rm.
10379  endl done
10380
10381#@cli rand : { value0[%] | [image0] },_{ value1[%] | [image1] } : [image] : (+)
10382#@cli : Fill selected images with random values uniformly distributed in the specified range.
10383#@cli : $ 400,400,1,3 rand -10,10 +blur 10 sign[-1]
10384
10385#@cli replace : source,target
10386#@cli : Replace pixel values in selected images.
10387#@cli : $ (1;2;3;4) +replace 2,3
10388replace :
10389  e[^-1] "Replace pixel values $1 with $2 in image$?."
10390  f "i==$1?$2:i"
10391
10392#@cli replace_inf : _expression
10393#@cli : Replace all infinite values in selected images by specified expression.
10394#@cli : $ (0;1;2) log +replace_inf 2
10395replace_inf :
10396  e[^-1] "Replace all infinite values in image$? by expression '$1'."
10397  f "isinf(i)?$1:i"
10398
10399#@cli replace_nan : _expression
10400#@cli : Replace all NaN values in selected images by specified expression.
10401#@cli : $ (-1;0;2) sqrt +replace_nan 2
10402replace_nan :
10403  e[^-1] "Replace all NaN values in images$? by expression '$1'."
10404  f "isnan(i)?$1:i"
10405
10406#@cli replace_naninf : _expression
10407#@cli : Replace all NaN and infinite values in selected images by specified expression.
10408replace_naninf :
10409  e[^-1] "Replace all NaN and infinite values in images$? by expression '$1'."
10410  f "isnan(i) || isinf(i)?$1:i"
10411
10412#@cli replace_seq : "search_seq","replace_seq"
10413#@cli : Search and replace a sequence of values in selected images.
10414#@cli : $ (1;2;3;4;5) +replace_seq "2,3,4","7,8"
10415replace_seq : skip "${2=''}"
10416  e[^-1] "Replace value sequence '$1' by value sequence '${2--1}' in image$?."
10417  y repeat $! l[$>] nm={n}
10418    1,100%
10419    eval "ref([ $1 ],str1);
10420          ref([ ${2--1} ],str2);
10421          copy_bloc(pd,src,len) = (
10422            l = len;
10423            pd + l>=h#1?resize(#1,1,h(#1) + 3*l,1,1,0);
10424            copy(i[pd],src,l);
10425            pd+=l;
10426          );
10427          for (ps = pd = 0, ps<h#0 && (qs = find(#0,str1,ps))>=0,
10428            qs>ps?copy_bloc(pd,i[#0,ps],qs - ps);
10429            copy_bloc(pd,str2,size(str2));
10430            ps = qs + size(str1);
10431          );
10432          ps<h#0?copy_bloc(pd,i[#0,ps],h#0 - ps);
10433          resize(#1,1,pd,1,1,0)"
10434    k. nm $nm
10435  endl done
10436
10437#@cli replace_str : "search_str","replace_str"
10438#@cli : Search and replace a string in selected images (viewed as strings, i.e. sequences of character codes).
10439#@cli : $ ('"Hello there, how are you ?"') +replace_str "Hello there","Hi David"
10440replace_str : skip "${2=}"
10441  e[^-1] "Replace string '$1' by string '${2--1}' in image$?."
10442  replace_seq {``{'"$1"'}},{'"${2--1}"'}
10443
10444#@cli round : rounding_value>=0,_rounding_type : (no arg) : (+)
10445#@cli : Round values of selected images.
10446#@cli : 'rounding_type' can be { -1=backward | 0=nearest | 1=forward }.
10447#@cli : Default value: 'rounding_type=0'.
10448#@cli : $ image.jpg +round 100
10449#@cli : $ image.jpg mul {pi/180} sin +round
10450
10451#@cli roundify : gamma>=0
10452#@cli : Apply roundify transformation on float-valued data, with specified gamma.
10453#@cli : Default value: 'gamma=0'.
10454#@cli : $ 1000 fill '4*x/w' repeat 5 +roundify[0] {$>*0.2} done append c display_graph 400,300
10455roundify : check $1>=0
10456  e[^-1] "Roundify image$?, with gamma $1."
10457  if $1==1 return fi
10458  repeat $! l[$>]
10459    +round 1 -.. . +*.. 2 abs. ^. $1 sign... *[-3,-1] *.. 0.5 +
10460  endl done
10461
10462#@cli = : eq. to 'set'. : (+)
10463
10464#@cli set : value,_x[%],_y[%],_z[%],_c[%] : (+)
10465#@cli : Set pixel value in selected images, at specified coordinates.
10466#@cli : (eq. to '=').\n
10467#@cli : If specified coordinates are outside the image bounds, no action is performed.
10468#@cli : Default values: 'x=y=z=c=0'.
10469#@cli : $ 2,2 set 1,0,0 set 2,1,0 set 3,0,1 set 4,1,1
10470#@cli : $ image.jpg repeat 10000 set 255,{u(100)}%,{u(100)}%,0,{u(100)}% done
10471
10472#@cli threshold : value[%],_is_soft={ 0 | 1 } :
10473#@cli : Threshold values of selected images.
10474#@cli : 'soft' can be { 0=hard-thresholding | 1=soft-thresholding }.
10475#@cli : Default value: 'is_soft=0'.
10476#@cli : $ image.jpg +threshold[0] 50% +threshold[0] 50%,1
10477#@cli : $$
10478threshold : check "isexpr($1) && isbool(${2=0})"
10479  e[^-1] ${"arg 1+!$2,Soft,Hard"}"-threshold image$? by $1."
10480  if $2  # Soft thresholding
10481    f "begin(
10482         str = ['$1'];
10483         value = str[size(str)-1]==_'%'?im + (iM-im)*$1:$1
10484       );
10485       i>=value?i - value:
10486       i<=-value?i + value:0"
10487  else ge $1
10488  fi
10489
10490#@cli vector2tensor
10491#@cli : Convert selected vector fields to corresponding tensor fields.
10492vector2tensor :
10493  e[^-1] "Convert vector field$? to tensor field$?."
10494  repeat $! l[$>]
10495    s c
10496    if $!==2 +sqr. *.. ... sqr...
10497    elif $!==3 +sqr.. +*... .. +sqr... *[-5,-4] [-6] sqr[-6]
10498    else error[0--4] "Command '$0': Invalid image ["{$!-$>-1}"] : Dimensions "{w}","{h}","{d}","{s}" does not
10499                      represent a field of 2D or 3D vectors."
10500    fi
10501    a c
10502  endl done
10503
10504#---------------------------------
10505#
10506#@cli :: Colors
10507#
10508#---------------------------------
10509
10510#@cli adjust_colors : -100<=_brightness<=100,-100<=_contrast<=100,-100<=_gamma<=100,-100<=_hue_shift<=100,\
10511# -100<=_saturation<=100,_value_min,_value_max
10512#@cli : Perform a global adjustment of colors on selected images.
10513#@cli : Range of correct image values are considered to be in [value_min,value_max] (e.g. [0,255]).
10514#@cli : If 'value_min==value_max==0', value range is estimated from min/max values of selected images.
10515#@cli : Processed images have pixel values constrained in [value_min,value_max].
10516#@cli : Default values: 'brightness=0', 'contrast=0', 'gamma=0', 'hue_shift=0', 'saturation=0', 'value_min=value_max=0'.
10517#@cli : $ image.jpg +adjust_colors 0,30,0,0,30
10518adjust_colors : check "${1=0}>=-100 && $1<=100 && ${2=0}>=-100 && $2<=100 && ${3=0}>=-100 && $3<=100 &&
10519                       ${4=0}>=-100 && $4<=100 && ${5=0}>=-100 && $5<=100" skip ${6=0},${7=0}
10520  e[^-1] "Adjust colors of image$?, with brightness $1, contrast $2, gamma $3, hue shift $4, saturation $5 and
10521          value range [$6,$7]."
10522  repeat $! l[$>] split_opacity l[0]
10523    range={"$6==$7 && $6==0?[im,iM]:[min($6,$7),max($6,$7)]"}
10524    m={arg(1,$range)} M={arg(2,$range)} fact={255/max(1e-5,$M-$m)}
10525    - $m * $fact
10526    if $4" || "$5 # Adjust Hue/Saturation
10527      to_rgb[0] rgb2hsv[0]
10528      sh[0] 0 +. {$4*1.8} rm.
10529      sh[0] 1 +. {($5%)^(1+($5>0))} c. 0,1 rm.
10530      hsv2rgb[0]
10531    fi
10532    if $3 # Adjust Gamma
10533      /[0] 255 ^[0] {10^-($3%)} *[0] 255
10534    fi
10535    if $2 # Adjust Contrast
10536      -[0] 128 *[0] {exp($2/64)} +[0] 128
10537    fi
10538    +[0] {$1*2} # Adjust Brightness
10539    /[0] $fact +[0] $m c[0] $range # Renormalize values.
10540    a c
10541  endl a c endl done
10542
10543#@cli ac : eq. to 'apply_channels'.
10544ac :
10545  _gmic_s="$?" v + _apply_channels $"*"
10546
10547#@cli apply_channels : "command",color_channels,_value_action={ 0=none | 1=cut | 2=normalize }
10548#@cli : Apply specified command on the chosen color channel(s) of each selected images.
10549#@cli : (eq. to 'ac').\n
10550#@cli : Argument 'color_channels' refers to a colorspace, and can be basically one of
10551#@cli : { all | rgba | [s]rgb | ryb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq }.
10552#@cli : You can also make the processing focus on a few particular channels of this colorspace,
10553#@cli : by setting 'color_channels' as 'colorspace_channel' (e.g. 'hsv_h' for the hue).
10554#@cli : All channel values are considered to be provided in the [0,255] range.
10555#@cli : Default value: 'value_action=0'.
10556#@cli : $ image.jpg +apply_channels "equalize blur 2",ycbcr_cbcr
10557apply_channels :
10558  _gmic_s="$?" v + _$0 $"*"
10559
10560_apply_channels : check "isint(${3=0}) && $3>=0 && $3<=2"
10561  channels=${"_ac_list \"$2\""}
10562  e[^-1] "Apply command '$1' on channels '"$channels"' of image"$_gmic_s"."
10563  ('$/') id={h=0;for(k=0,k<w,((h*=31)+=i[k++])%=1048576)} rm.
10564  _ac_$channels m _ac_precond$id:$_p m _ac_forward$id:$_f m _ac_backward$id:$_b
10565  repeat $! l[$>]
10566    _ac_precond$id is_alpha={s==2" || "s==4}
10567    if $is_alpha s c,{1-s} fi
10568    _ac_forward$id[0] a c
10569    sh $_s _ac. "$1"
10570    if $3==1 c. 0,255 elif $3==2 n. 0,255 fi
10571    rm.
10572    if $is_alpha s c,{1-s} fi
10573    _ac_backward$id[0] a c
10574  endl done
10575  um _ac_precond$id,_ac_forward$id,_ac_backward$id
10576
10577_ac_list :
10578  if isnum("$1")
10579    arg 1+round($1),all,rgba,rgb,rgb_r,rgb_g,rgb_b,rgba_a,\
10580                    lrgb,lrgb_r,lrgb_g,lrgb_b,\
10581                    ycbcr_y,ycbcr_cbcr,ycbcr_cb,ycbcr_cr,ycbcr_cg,\
10582                    lab_l,lab_ab,lab_a,lab_b,\
10583                    lch_ch,lch_c,lch_h,\
10584                    hsv_h,hsv_s,hsv_v,hsi_i,hsl_l,\
10585                    cmyk_c,cmyk_m,cmyk_y,cmyk_k,\
10586                    yiq_y,yiq_iq,ryb,ryb_r,ryb_y,ryb_b
10587  else u "$1" fi
10588
10589_ac :
10590  whds={w},{h},{d},{s} ${1--1} k[0] r $whds,0
10591
10592_ac_all : _p="" _f="" _b="" _s=0,100%
10593
10594_ac_rgba : _p="to_rgba" _f="" _b="" _s=0,3
10595_ac_rgba_r : _p="to_color" _f="" _b="" _s=0
10596_ac_rgba_g : _p="to_color" _f="" _b="" _s=1
10597_ac_rgba_b : _p="to_color" _f="" _b="" _s=2
10598_ac_rgba_a : _p="to_rgba" _f="" _b="" _s=3
10599
10600_ac_rgb : _p="to_color" _f="" _b="" _s=0,2
10601_ac_rgb_r : _ac_rgba_r
10602_ac_rgb_g : _ac_rgba_g
10603_ac_rgb_b : _ac_rgba_b
10604
10605_ac_srgb : _ac_rgb
10606_ac_srgb_r : _ac_rgb_r
10607_ac_srgb_g : _ac_rgb_g
10608_ac_srgb_b : _ac_rgb_b
10609
10610_ac_lrgb : _p="to_color" _f="srgb2rgb" _b="rgb2srgb" _s=0,2
10611_ac_lrgb_r : _p="to_color" _f="srgb2rgb" _b="rgb2srgb" _s=0
10612_ac_lrgb_g : _p="to_color" _f="srgb2rgb" _b="rgb2srgb" _s=1
10613_ac_lrgb_b : _p="to_color" _f="srgb2rgb" _b="rgb2srgb" _s=2
10614
10615_ac_ryb : _p="to_color" _f="rgb2ryb" _b="ryb2rgb" _s=0,2
10616_ac_ryb_r : _p="to_color" _f="rgb2ryb" _b="ryb2rgb" _s=0
10617_ac_ryb_y : _p="to_color" _f="rgb2ryb" _b="ryb2rgb" _s=1
10618_ac_ryb_b : _p="to_color" _f="rgb2ryb" _b="ryb2rgb" _s=2
10619
10620_ac_ycbcr : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=0,2
10621_ac_ycbcr_y : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=0
10622_ac_ycbcr_cbcr : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=1,2
10623_ac_ycbcr_cb : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=1
10624_ac_ycbcr_cr : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=2
10625_ac_ycbcr_cg : _p="to_color" _f="sh 0,1 mirror. c rm. rgb2ycbcr" _b="ycbcr2rgb sh 0,1 mirror. c rm." _s=2
10626
10627_ac_lab : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=0,2
10628_ac_lab_l : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=0
10629_ac_lab_ab : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=1,2
10630_ac_lab_a : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=1
10631_ac_lab_b : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=2
10632
10633_ac_lch : _p="to_color" _f="srgb2rgb rgb2lch8" _b="lch82rgb rgb2srgb" _s=0,2
10634_ac_lch_l : _ac_lab_l
10635_ac_lch_ch : _p="to_color" _f="srgb2rgb rgb2lch8" _b="lch82rgb rgb2srgb" _s=1,2
10636_ac_lch_c : _p="to_color" _f="srgb2rgb rgb2lch8" _b="lch82rgb rgb2srgb" _s=1
10637_ac_lch_h : _p="to_color" _f="srgb2rgb rgb2lch8" _b="lch82rgb rgb2srgb" _s=2
10638
10639_ac_hsv : _p="to_color" _f="rgb2hsv8" _b="hsv82rgb" _s=0,2
10640_ac_hsv_h : _p="to_color" _f="rgb2hsv8" _b="hsv82rgb" _s=0
10641_ac_hsv_s : _p="to_color" _f="rgb2hsv8" _b="hsv82rgb" _s=1
10642_ac_hsv_v : _p="to_color" _f="rgb2hsv8" _b="hsv82rgb" _s=2
10643
10644_ac_hsi : _p="to_color" _f="rgb2hsi8" _b="hsi82rgb" _s=0,2
10645_ac_hsi_h : _p="to_color" _f="rgb2hsi8" _b="hsi82rgb" _s=0
10646_ac_hsi_s : _p="to_color" _f="rgb2hsi8" _b="hsi82rgb" _s=1
10647_ac_hsi_i : _p="to_color" _f="rgb2hsi8" _b="hsi82rgb" _s=2
10648
10649_ac_hsl : _p="to_color" _f="rgb2hsl8" _b="hsl82rgb" _s=0,2
10650_ac_hsl_h : _p="to_color" _f="rgb2hsl8" _b="hsl82rgb" _s=0
10651_ac_hsl_s : _p="to_color" _f="rgb2hsl8" _b="hsl82rgb" _s=1
10652_ac_hsl_l : _p="to_color" _f="rgb2hsl8" _b="hsl82rgb" _s=2
10653
10654_ac_cmy : _p="to_color" _f="rgb2cmy" _b="cmy2rgb" _s=0,2
10655_ac_cmy_c : _p="to_color" _f="rgb2cmy" _b="cmy2rgb" _s=0
10656_ac_cmy_m : _p="to_color" _f="rgb2cmy" _b="cmy2rgb" _s=1
10657_ac_cmy_y : _p="to_color" _f="rgb2cmy" _b="cmy2rgb" _s=2
10658
10659_ac_cmyk : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=0,3
10660_ac_cmyk_c : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=0
10661_ac_cmyk_m : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=1
10662_ac_cmyk_y : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=2
10663_ac_cmyk_k : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=3
10664
10665_ac_yiq : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=0,2
10666_ac_yiq_y : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=0
10667_ac_yiq_iq : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=1,2
10668_ac_yiq_i : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=1
10669_ac_yiq_q : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=2
10670
10671#@cli autoindex : nb_colors>0,0<=_dithering<=1,_method={ 0=median-cut | 1=k-means }
10672#@cli : Index selected vector-valued images by adapted colormaps.
10673#@cli : Default values: 'dithering=0' and 'method=1'.
10674#@cli : $ image.jpg +autoindex[0] 4 +autoindex[0] 8 +autoindex[0] 16
10675autoindex : check "isint($1) && $1>0 && ${2=0}>=0" skip ${3=1}
10676  e[^-1] "Index colors in images$? by adapted colormap with $1 entries, dithering level $2 and "\
10677          ${arg\ 1+!$3,k-means,median-cut}" method."
10678  repeat $! l[$>]
10679    if w>h if w>256 +r2dx 256 else [0] fi
10680    else if h>256 +r2dy 256 else [0] fi
10681    fi
10682    colormap[1] $1,$3,0
10683    index[0] [1],$2,1 rm[1]
10684  endl done
10685
10686#@cli bayer2rgb : _GM_smoothness,_RB_smoothness1,_RB_smoothness2
10687#@cli : Transform selected RGB-Bayer sampled images to color images.
10688#@cli : Default values: 'GM_smoothness=RB_smoothness=1' and 'RB_smoothness2=0.5'.
10689#@cli : $ image.jpg rgb2bayer 0 +bayer2rgb 1,1,0.5
10690bayer2rgb : skip ${1=1},${2=1},${3=0.5}
10691  e[^-1] "Transform RGB-Bayer image$? to color images, with smoothness ($1,$2,$3)."
10692  channels 0 repeat $! l[$>]
10693
10694    # Expand image size to avoid problems with borders.
10695    expand_x {"2 + 4*$1"},0 expand_y {"2 + 4*$1"},0
10696
10697    # Compute green-magenta chromaticity.
10698    (-1,1;1,-1) r. ..,..,1,1,0,2
10699    +*.. .
10700
10701    (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm.
10702    b. $1
10703
10704    *.. .
10705    -[-3,-2]
10706
10707    # Compute red-blue chromaticity.
10708    (1,-1) r. ..,..,1,1,0,2  # Horizontal estimate
10709    *. ...
10710    (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm.
10711    blur_y. $2 blur_x. $3
10712
10713    (1;-1) r. ..,..,1,1,0,2  # Vertical estimate
10714    *. [-4]
10715    (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm.
10716    blur_x. $2 blur_y. $3
10717
10718    +[-2,-1] /. 2
10719
10720    # Luminance reconstruction.
10721    (2,0;0,-2) r. ..,..,1,1,0,2
10722    *. ..
10723    -[-4,-1]
10724
10725    # RGB reconstruction.
10726    a[-3--1] c
10727    mix_rgb. 1,-1,2,1,1,0,1,-1,-2
10728
10729    # Shrink to original image size.
10730    shrink_x {"2 + 4*$1"},0 shrink_y {"2 + 4*$1"},0
10731    c 0,255
10732
10733  endl done
10734
10735#@cli deltaE : [image],_metric={ 0=deltaE_1976 | 1=deltaE_2000 },"_to_Lab_command"
10736#@cli : Compute the CIE DeltaE color difference between selected images and specified [image].
10737#@cli : Argument 'to_Lab_command' is a command able to convert colors of [image] into a Lab representation.
10738#@cli : Default values: 'metric=1' and 'to_Lab_command="srgb2lab"'.
10739#@cli : $ image.jpg +blur 2 +deltaE[0] [1],1,srgb2lab
10740deltaE : check ${"is_image_arg $1"}" && isbool(${2=1})" skip "${3=srgb2lab}"
10741  e[^-1] "Compute the CIE DeltaE_"${"s0,s1=1976,2000 u $s$2"}" color difference between image$? and image$1, "\
10742         "with to_Lab command '$3'."
10743  pass$1 1
10744  needs_to_lab={"s = ['$3']; s!=0 && s!=' '"}
10745  if $needs_to_lab m "_deltaE_to_lab : $3" +_deltaE_to_lab. rm.. fi
10746  repeat $!-1 l[$>,-1] nm={0,n}
10747    if $needs_to_lab _deltaE_to_lab[0] fi
10748    if !$2 -.. . norm..                                                # DeltaE_1976
10749    else 100%,100%,100%,1,${-math_lib}"deltaE00(I#0,I#1)" rv[0,-1] rm. # DeltaE_2000
10750    fi
10751  nm[0] $nm endl done
10752  rm. um _deltaE_to_lab
10753
10754#@cli cmy2rgb
10755#@cli : Convert color representation of selected images from CMY to RGB.
10756cmy2rgb :
10757  e[^-1] "Convert color representation of image$? from CMY to RGB."
10758  rgb2cmy
10759
10760#@cli cmyk2rgb
10761#@cli : Convert color representation of selected images from CMYK to RGB.
10762cmyk2rgb :
10763  e[^-1] "Convert color representation of image$? from CMYK to RGB."
10764  repeat $! l[$>]
10765    s c +/. -255 +. 1 *[0-2] . rm. +[0-2] . rm.
10766    a c cmy2rgb
10767  endl done
10768
10769#@cli colorblind : type={ 0=protanopia | 1=protanomaly | 2=deuteranopia | 3=deuteranomaly | \
10770# 4=tritanopia | 5=tritanomaly | 6=achromatopsia | 7=achromatomaly }
10771#@cli : Simulate color blindness vision.
10772#@cli : $ image.jpg +colorblind 0
10773colorblind : check "isint($1) && $1>=0 && $1<=7"
10774  s0="protanopia" s1="protanomaly" s2="deuteranopia" s3="deuteranomaly" s4="tritanopia"
10775  s5="tritanomaly" s6="achromatopsia" s7="achromatomaly"
10776  e[^-1] "Simulate color blindness of type '"${s$1}"' on image$?."
10777  type0=(0.567,0.433,0;0.558,0.442,0;0,0.242,0.758)
10778  type1=(0.817,0.183,0;0.333,0.667,0;0,0.125,0.875)
10779  type2=(0.625,0.375,0;0.7,0.3,0;0,0.3,0.7)
10780  type3=(0.8,0.2,0;0.258,0.742,0;0,0.142,0.858)
10781  type4=(0.95,0.05,0;0,0.433,0.567;0,0.475,0.525)
10782  type5=(0.967,0.033,0;0,0.733,0.267;0,0.183,0.817)
10783  type6=(0.299,0.587,0.114;0.299,0.587,0.114;0.299,0.587,0.114)
10784  type7=(0.618,0.320,0.062;0.163,0.775,0.062;0.163,0.320,0.516)
10785  repeat $! l[$>] split_opacity l[0] to_rgb mix_channels ${type$1} endl a c endl done
10786
10787#@cli colormap : nb_levels>=0,_method={ 0=median-cut | 1=k-means },_sort_vectors
10788#@cli : Estimate best-fitting colormap with 'nb_colors' entries, to index selected images.
10789#@cli : Set 'nb_levels==0' to extract all existing colors of an image.
10790#@cli : 'sort_vectors' can be { 0=unsorted | 1=by increasing norm | 2=by decreasing occurrence }.
10791#@cli : Default value: 'method=1' and 'sort_vectors=1'.
10792#@cli : $ image.jpg +colormap[0] 4 +colormap[0] 8 +colormap[0] 16
10793#@cli : $$ https://gmic.eu/oldtutorial/_colormap
10794colormap : check "isint($1) && $1>=0" skip ${2=1},${3=1}
10795  if $1 e[0--3] "Estimate colormap with $1 entries for image$?, by "${arg\ 1+!$2,k-means,median-cut}" method."
10796  else e[0--3] "Estimate full colormap for image$?."
10797  fi
10798  repeat $! l[$>] nm={b} is_half=${-is_half}
10799
10800    if $1 # Estimate quantized colormap.
10801      r {whd},1,1,100%,-1
10802      if !$2 +_colormap $1 # Just run the median-cut algorithm
10803      else
10804        +_colormap $1
10805        max_diff={(iM-im+1)/8192}
10806        do
10807          +index.. . # Find nearest cluster for each color
10808          if $is_half
10809            ..,1,1,{1,s}
10810            eval "
10811              ref(vector(#w*s#0),csum);
10812              ref(vector(#w,0),cocc);
10813              repeat (w#2,k,
10814                val = i[#2,k];
10815                repeat (s#0,c,csum[val + c*w]+=i(#0,k,0,0,c));
10816                ++cocc[val];
10817              );
10818              off = 0;
10819              repeat (s#0,c,
10820                repeat (w,k,occ = cocc[k]; occ?(csum[off++]/=occ));
10821              );
10822              draw(#3,csum,0,0,0,0)"
10823            rm..
10824          else
10825            ..,1,1,{1,s+1}
10826            f.. ">I[#3,i]+=[ I[#0,x],1 ]" rm..
10827            f. "s = i(x,0,0,s-1); s?I/s:[ I[#1,x], 0 ]"
10828          fi
10829          +-.. . abs. diff={iM/w} rm. # Compute colormap difference
10830          j.. . rm.
10831        while $diff>$max_diff
10832      fi
10833      if $3 index.. .,0,0 histogram.. {[w,0,w-1]} a y sort -,x rows 1 # Sort by decreasing occurrence
10834      else rm..
10835      fi
10836
10837    else # Extract full colormap.
10838
10839      # Compute map of hashcodes.
10840      +n. 0,255 f. "ret = vectors(); H = 0; repeat (s,p,(H*=31)+=j(0,0,0,p)); ret[0] = H%2048; ret"
10841      channels. 0 equalize. 2048 n. 0,2047 round.
10842
10843      # Find single occurrence of each color/vector.
10844      1,1,1,.. .x2047
10845      eval[0] ">"${-math_lib}"
10846        begin( ret = vectors(); col_r = vectors() );
10847        col_s = I;
10848        H = 2 + i#1;
10849        sH = dar_size(#H);
10850        is_found = 0;
10851        repeat (sH,p,
10852          copy(col_r,i[#H,p],s#0,1,h(#H));
10853          col_r==col_s?(is_found = 1; break());
10854        );
10855        !is_found?dar_insert(#H,col_s);
10856        ret;
10857        end(repeat (l - 2,p, resize(#2 + p,1,dar_size(#2 + p),1,s#0,0)))"
10858      a[2--1] y transpose. k.
10859    fi
10860
10861    if $3==1 +norm rv a c sort +,x channels 1,100% fi # Sort colors by increasing norm.
10862    nm "[colormap of "$nm"]"
10863  endl done
10864
10865_colormap : # Implementation of the median-cut algorithm.
10866  m "__colormap : repeat s sh[$""1] $> =.. {iv},$""1,0,0,$> rm. done"
10867  1,1,1,{s} __colormap 0 # Initialize image of box variances
10868  repeat $1-1
10869    b,a={[xM,cM]} # b = box with highest variance, a = axis with highest variance
10870    shift[$b] 0,0,0,{-$a},2 sort[$b] +,x shift[$b] 0,0,0,$a,2
10871    if {$b,w>1} s[$b] x,2 else 1 fi # Split selected box along its median axis
10872    mv[$b] -1 r. {w+1},1,1,100%,0
10873    __colormap $b __colormap {$!-2} # Update box variances
10874  done
10875  rm. r 1,1,1,100%,2 a x # Average value in each box and append as final colormap
10876  um __colormap
10877
10878#@cli compose_channels
10879#@cli : Compose all channels of each selected image, using specified arithmetic operator (+,-,or,min,...).
10880#@cli : Default value: '1=+'.
10881#@cli : $ image.jpg +compose_channels and
10882#@cli : $$
10883compose_channels : skip ${1="+"}
10884  e[^-1] "Compose all channels of image$?, with operator '$1'."
10885  repeat $! l[$>]
10886    sh 0
10887    repeat s#-2-1 sh.. {$>+1} l[-2,-1] $1 endl done
10888    rm. r 100%,100%,100%,1,-1
10889  endl done
10890
10891#@cli direction2rgb
10892#@cli : Compute RGB representation of selected 2D direction fields.
10893#@cli : $ image.jpg luminance gradient append c blur 2 orientation +direction2rgb
10894direction2rgb :
10895  e[^-1] "Compute RGB representation of 2D direction field$?."
10896  channels 0,1 repeat $! l[$>] nm={0,n}
10897    s c complex2polar round.. 0.001
10898    *. {180/pi} %. 360 100%,100%,1,1,1 mv... $!
10899    if im!=iM n. 0,1 else f. 1 fi
10900    a c hsv2rgb
10901  nm $nm endl done
10902
10903#@cli ditheredbw
10904#@cli : Create dithered B&W version of selected images.
10905#@cli : $ image.jpg +equalize ditheredbw[-1]
10906ditheredbw :
10907  e[^-1] "Create dithered B&W version of image$?."
10908  repeat $! l[$>] split_opacity
10909    luminance[0] n[0] 0,255 (0,255) index[0] .,1,1 rm.
10910  a c endl done
10911
10912#@cli fc : eq. to 'fill_color'.
10913fc :
10914  _gmic_s="$?" v + _fill_color $*
10915
10916#@cli fill_color : col1,...,colN
10917#@cli : Fill selected images with specified color.
10918#@cli : (eq. to 'fc').
10919#@cli : $ image.jpg +fill_color 255,0,255
10920#@cli : $$ https://gmic.eu/oldtutorial/_fill_color
10921fill_color :
10922  _gmic_s="$?" v + _$0 $*
10923
10924_fill_color :
10925  e[0--3] "Fill image"$_gmic_s" with color (${^0})."
10926  repeat $! l[$>]
10927    repeat s sh[0] $> f. {arg(1+$>,${^0})} done k[0]
10928  nm {n} endl done
10929
10930#@cli gradient2rgb : _is_orientation={ 0 | 1 }
10931#@cli : Compute RGB representation of 2D gradient of selected images.
10932#@cli : Default value: 'is_orientation=0'.
10933#@cli : $ image.jpg +gradient2rgb 0 equalize[-1]
10934gradient2rgb : check "isbool(${1=0})"
10935  arg 1+!$1,"orientation ",""
10936  e[^-1] "Compute RGB representation of 2D gradient "${}"of image$?."
10937  norm repeat $! l[$>]
10938    if $1 gradient_orientation 2 else g xy fi
10939    a c direction2rgb
10940  endl done
10941
10942#@cli hcy2rgb
10943#@cli : Convert color representation of selected images from HCY to RGB.
10944hcy2rgb :
10945  e[^-1] "Convert color representation of image$? from HCY to RGB."
10946  to_color f "
10947    H = (R/60)%6;
10948    X = G*(1 - abs(H%2 - 1));
10949    RGB = arg(1 + int(H),[G,X,0],[X,G,0],[0,G,X],[0,X,G],[X,0,G],[G,0,X]);
10950    m = B - 0.3*RGB[0] - 0.59*RGB[1] - 0.11*RGB[2];
10951    cut((RGB+=m)*=255,0,255)"
10952
10953#@cli hsi2rgb
10954#@cli : Convert color representation of selected images from HSI to RGB.
10955hsi2rgb :
10956  e[^-1] "Convert color representation of image$? from HSI to RGB."
10957  to_color
10958  f "
10959    H = (R/60)%6;
10960    S = G;
10961    I = B;
10962    Z = 1 - abs((H%2) - 1);
10963    C = I*S/(1 + Z);
10964    X = C*Z;
10965    m = I*(1 - S)/3;
10966    RGB = arg(1 + int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]);
10967    (RGB+=m)*=3*255"
10968
10969#@cli hsi82rgb
10970#@cli : Convert color representation of selected images from HSI8 to RGB.
10971hsi82rgb :
10972  e[^-1] "Convert color representation of image$? from HSI8 to RGB."
10973  _hsx82rgb hsi2rgb
10974
10975#@cli hsl2rgb
10976#@cli : Convert color representation of selected images from HSL to RGB.
10977hsl2rgb :
10978  e[^-1] "Convert color representation of image$? from HSL to RGB."
10979  to_color
10980  f "
10981    H = (R/60)%6;
10982    S = G;
10983    L = B;
10984    C = (1 - abs(2*L - 1))*S;
10985    X = C*(1 - abs(H%2 - 1));
10986    m = L - C/2;
10987    RGB = arg(1 + int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]);
10988    (RGB+=m)*=255"
10989
10990#@cli hsl82rgb
10991#@cli : Convert color representation of selected images from HSL8 to RGB.
10992hsl82rgb :
10993  e[^-1] "Convert color representation of image$? from HSL8 to RGB."
10994  _hsx82rgb hsl2rgb
10995
10996#@cli hsv2rgb
10997#@cli : Convert color representation of selected images from HSV to RGB.
10998#@cli : $ (0,360;0,360^0,0;1,1^1,1;1,1) resize 400,400,1,3,3 hsv2rgb
10999hsv2rgb :
11000  e[^-1] "Convert color representation of image$? from HSV to RGB."
11001  to_color
11002  f "
11003    H = (R/60)%6;
11004    S = G;
11005    V = B;
11006    C = V*S;
11007    X = C*(1 - abs(H%2 - 1));
11008    m = V - C;
11009    RGB = arg(1 + int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]);
11010    (RGB+=m)*=255"
11011
11012#@cli hsv82rgb
11013#@cli : Convert color representation of selected images from HSV8 to RGB.
11014hsv82rgb :
11015  e[^-1] "Convert color representation of image$? from HSV8 to RGB."
11016  _hsx82rgb hsv2rgb
11017
11018_hsx82rgb :
11019 repeat $!
11020   sh[$>] 0 /. 0.708333 rm.
11021   sh[$>] 1,2 /. 255 rm.
11022 done
11023
11024#@cli int2rgb
11025#@cli : Convert color representation of selected images from INT24 to RGB.
11026int2rgb :
11027  e[^-1] "Convert color representation of image$? from INT24 scalars to RGB."
11028  round repeat $! l[$>]
11029    +>> 8 &[1] 255 +&[0] 255 >>[0] 16 a c
11030  endl done
11031
11032#@cli jzazbz2rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11033#@cli : Convert color representation of selected images from RGB to Jzazbz.
11034#@cli : Default value: 'illuminant=2'.
11035jzazbz2rgb : skip "${1=,}"
11036  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11037  e[^-1] "Convert color representation of image$? from Jzazbz to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11038  jzazbz2xyz xyz2rgb $illu
11039
11040#@cli jzazbz2xyz
11041#@cli : Convert color representation of selected images from RGB to XYZ.
11042jzazbz2xyz :
11043  e[^-1] "Convert color representation of image$? from Jzazbz to XYZ."
11044  repeat $! l[$>] split_opacity
11045    f[0] ${-_jzazbz_const}"
11046      tmp = i0 + Jzazbz_d0;
11047      Iz = tmp/(1 + Jzazbz_d - Jzazbz_d*tmp);
11048      azz = i1;
11049      bzz = i2;
11050      Lp = Iz + 0.138605043271539*azz + 0.0580473161561189*bzz;
11051      Mp = Iz - 0.138605043271539*azz - 0.0580473161561189*bzz;
11052      Sp = Iz - 0.0960192420263189*azz - 0.811891896056039*bzz;
11053      tmp = Lp^(1/Jzazbz_p);
11054      L = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n);
11055      tmp = Mp^(1/Jzazbz_p);
11056      M = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n);
11057      tmp = Sp^(1/Jzazbz_p);
11058      S = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n);
11059      Xp = 1.92422643578761*L - 1.00479231259537*M + 0.037651404030618*S;
11060      Yp = 0.350316762094999*L + 0.726481193931655*M - 0.065384422948085*S;
11061      Zp = -0.0909828109828476*L - 0.312728290523074*M + 1.52276656130526*S;
11062      X = (Xp + (Jzazbz_b - 1)*Zp)/Jzazbz_b;
11063      Y = (Yp + (Jzazbz_g - 1)*X)/Jzazbz_g;
11064      Z = Zp;
11065      [ X,Y,Z ]/255"
11066  a c endl done
11067
11068# The XYZ<->Jzazbz conversion code has been written by Alan Gibson,
11069# and published on this page: <http://im.snibgo.com/jzazbz.htm>
11070_jzazbz_const :
11071  u "const Jzazbz_b = 1.15;
11072     const Jzazbz_g = 0.66;
11073     const Jzazbz_c1 = 3424/4096;
11074     const Jzazbz_c2 = 2413/128;
11075     const Jzazbz_c3 = 2392/128;
11076     const Jzazbz_n = 2610/16384;
11077     const Jzazbz_p = 1.7*2523/32;
11078     const Jzazbz_d = -0.56;
11079     const Jzazbz_d0 = 1.6295499532821566e-11;
11080     const peakLum = 10000;"
11081
11082#@cli lab2lch
11083#@cli : Convert color representation of selected images from Lab to Lch.
11084lab2lch :
11085  e[^-1] "Convert color representation of image$? from Lab to Lch."
11086  repeat $! l[$>]
11087    to_color s c complex2polar[1,2] a c
11088  endl done
11089
11090#@cli lab2rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11091#@cli : Convert color representation of selected images from Lab to RGB.
11092#@cli : Default value: 'illuminant=2'.
11093#@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb
11094lab2rgb : skip "${1=,}"
11095  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11096  e[^-1] "Convert color representation of image$? from Lab to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11097  lab2xyz $illu xyz2rgb $illu
11098
11099#@cli lab2srgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11100#@cli : Convert color representation of selected images from Lab to sRGB.
11101#@cli : Default value: 'illuminant=2'.
11102#@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb
11103lab2srgb : skip "${1=,}"
11104  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11105  e[^-1] "Convert color representation of image$? from Lab to sRGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11106  lab2rgb $illu rgb2srgb
11107
11108#@cli lab82srgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11109#@cli : Convert color representation of selected images from Lab8 to sRGB.
11110#@cli : Default value: 'illuminant=2'.
11111#@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb
11112lab82srgb : skip "${1=,}"
11113  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11114  e[^-1] "Convert color representation of image$? from Lab8 to sRGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11115  lab82rgb $illu rgb2srgb
11116
11117#@cli lab2xyz : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11118#@cli : Convert color representation of selected images from Lab to XYZ.
11119#@cli : Default value: 'illuminant=2'.
11120lab2xyz : skip "${1=,}"
11121  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11122  e[^-1] "Convert color representation of image$? from Lab to XYZ, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11123  to_color
11124  f "
11125    begin(
11126      const epsilon = 216/24389;
11127      const kappa = 24389/27;
11128      D65 = [ 0.4124564, 0.3575761, 0.1804375,
11129              0.2126729, 0.7151522, 0.0721750,
11130              0.0193339, 0.1191920, 0.9503041 ];
11131      D50 = [ 0.43603516, 0.38511658, 0.14305115,
11132              0.22248840, 0.71690369, 0.06060791,
11133              0.01391602, 0.09706116, 0.71392822 ];
11134      E = [ 0.488718,0.3106803,0.2006017,
11135            0.1762044,0.8129847,0.0108109,
11136            0,0.0102048,0.9897952 ];
11137      white = ("$illu"==2?E:"$illu"==1?D65:D50)*[ 1,1,1 ];
11138    );
11139
11140    fy = (i0 + 16)/116;
11141    fz = fy - i2/200;
11142    fx = i1/500 + fy;
11143    fx3 = fx^3;
11144    fz3 = fz^3;
11145    XYZ = [ fx3>epsilon?fx3:(116*fx - 16)/kappa,
11146            i0>kappa*epsilon?((i0+16)/116)^3:i0/kappa,
11147            fz3>epsilon?fz3:(116*fz - 16)/kappa ];
11148    XYZ*=white"
11149
11150#@cli lab82rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11151#@cli : Convert color representation of selected images from Lab8 to RGB.
11152#@cli : Default value: 'illuminant=2'.
11153lab82rgb : skip "${1=,}"
11154  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11155  e[^-1] "Convert color representation of image$? from Lab8 to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11156  repeat $!
11157    sh[$>] 0 /. 2.55 rm.
11158    sh[$>] 1 /. 0.85 -. 127 rm.
11159    sh[$>] 2 /. 0.836 -. 149 rm.
11160  done lab2rgb $illu c 0,255
11161
11162#@cli lch2lab
11163#@cli : Convert color representation of selected images from Lch to Lab.
11164lch2lab :
11165  e[^-1] "Convert color representation of image$? from Lch to Lab."
11166  repeat $! l[$>]
11167    to_color s c polar2complex[1,2] a c
11168  endl done
11169
11170#@cli lch2rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11171#@cli : Convert color representation of selected images from Lch to RGB.
11172#@cli : Default value: 'illuminant=2'.
11173lch2rgb : skip "${1=,}"
11174  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11175  e[^-1] "Convert color representation of image$? from Lch to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11176  lch2lab lab2rgb $illu
11177
11178#@cli lch82rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11179#@cli : Convert color representation of selected images from Lch8 to RGB.
11180#@cli : Default value: 'illuminant=2'.
11181lch82rgb : skip "${1=,}"
11182  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11183  e[^-1] "Convert color representation of image$? from Lch8 to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11184  pi,facth={[pi,255/(2*pi)]}
11185  repeat $!
11186    sh[$>] 0 /. 2.55 rm.
11187    sh[$>] 1 /. 1.1086 rm.
11188    sh[$>] 2 /. $facth -. $pi rm.
11189  done lch2rgb $illu c 0,255
11190
11191#@cli luminance
11192#@cli : Compute luminance of selected sRGB images.
11193#@cli : $ image.jpg +luminance
11194#@cli : $$
11195luminance :
11196  e[^-1] "Compute luminance of image$?."
11197  remove_opacity srgb2rgb
11198  repeat $! l[$>]
11199  if s==3 sh 0 sh[0] 1 sh[0] 2 *[1] 0.22248840 *[2] 0.71690369 *[3] 0.06060791 +[1-3] rm[1]
11200  elif s!=1 norm n 0,255
11201  fi endl done
11202  channels 0 rgb2srgb
11203
11204#@cli lightness
11205#@cli : Compute lightness of selected sRGB images.
11206#@cli : $ image.jpg +lightness
11207lightness :
11208  e[^-1] "Compute lightness of image$?."
11209  remove_opacity srgb2rgb
11210  if s==3 srgb2lab channels 0 * {255/100} elif s!=1 norm n 0,255 rgb2srgb fi
11211
11212#@cli lut_contrast : _nb_colors>1,_min_rgb_value
11213#@cli : Generate a RGB colormap where consecutive colors have high contrast.
11214#@cli : This function performs a specific score maximization to generate the result, so
11215#@cli : it may take some time when 'nb_colors' is high.
11216#@cli : Default values: 'nb_colors=256' and 'min_rgb_value=64'.
11217lut_contrast : check "isint(${1=256}) && $1>=1 && isnum(${2=48})"
11218  e[^-1] "Generate high-contrast RGB colormap with $1 colors and min RGB value $2."
11219  l[]
11220
11221    # Initialization by farthest point sampling of the RGB cube.
11222    64,64,64,1 eval "repeat (8,k, x = !!(k&1); y = !!(k&2); z = !!(k&4); i([x,y,z]*(w-1)) = 1)"
11223    N={is}
11224    e[] ""
11225    do
11226      +neq. 0 distance. 1 xyzM={[xM,yM,zM]} rm.
11227      col={round([$xyzM]*255/(w-1))}
11228      if max($col)>=$2 =. 1,$xyzM N+=1 else =. -1,$xyzM fi
11229      e[] "\r  [ Init ] > Colors \#"$N
11230    while $N<$1
11231    >. 0 {is},1,1,3
11232    eval.. ">begin(k = 0); i>0?(I[#-1,k++] = round([ x,y,z ]*255/63))"
11233    k.
11234
11235    N0=5 # 5 first colors to be preserved.
11236    s x repeat $!
11237      if {$>,I==[0,0,0]} rv[$>,0] fi
11238      if {$>,I==[255,255,255]} rv[$>,1] fi
11239      if {$>,I==[255,0,0]} rv[$>,2] fi
11240      if {$>,I==[0,255,0]} rv[$>,3] fi
11241      if {$>,I==[0,0,255]} rv[$>,4] fi
11242    done a x
11243
11244    # Functional optimization.
11245    e[] ""
11246    +srgb2lab a c
11247    energy_max=${-_lut_contrast.}
11248    nb_attempts=1000
11249    do
11250      e[] "\r  [ Optim ] > Score = "{_$energy_max}", Attempts = "$nb_attempts"      "
11251      . eval "
11252        do(
11253          k0 = round(u("$N0",w-1));
11254          k1 = round(u("$N0",w-1)),
11255        k0==k1);
11256        tmp = I[k0]; I[k0] = I[k1]; I[k1] = tmp"
11257      energy=${-_lut_contrast.}
11258      if $energy>$energy_max energy_max=$energy k. nb_attempts=1000 else rm. nb_attempts-=1 fi
11259    while $nb_attempts>0
11260
11261    channels 0,2
11262  endl
11263
11264_lut_contrast :
11265  100%,1,1,1,">
11266    const N = 10;
11267    dist = 0; sumw = 0;
11268    RGB0 = (I[#0,x])[3,3];
11269    kmin = max(x-N,0);
11270    kmax = min(x+N,w-1);
11271    for (k = kmin, k<=kmax, ++k,
11272      RGB = (I[#0,k])[3,3];
11273      w = (1 + N - abs(k-x))^1.5;
11274      dist+= w*norm(RGB - RGB0);
11275      sumw+=w;
11276    );
11277    dist/=sumw"
11278  u {is} rm.
11279
11280#@cli map_clut : [clut] | "clut_name"
11281#@cli : Map specified RGB color LUT to selected images.
11282#@cli : $ image.jpg uniform_distribution {2^6},3 mirror[-1] x +map_clut[0] [1]
11283map_clut :
11284  e[^-1] "Map color LUT $1 on image$?."
11285  if !$! return fi
11286  to_color
11287  if ${"is_image_arg $1"} pass$1 0 to_rgb. else clut "$1" fi
11288  l={round((w*h*d)^(1/3))}
11289  if w*h*d!=$l^3 error "Command '$0': Specified CLUT $1 has invalid dimensions "({w},{h},{d},{s}). fi
11290  r. $l,$l,$l,3,-1
11291  repeat $!-1 l[$>,-1] nm={0,n} split_opacity[0] /[0] {256/$l}
11292    +warp. [0],0,1,1
11293    rm[0] mv. 0 a[^-1] c nm[0] $nm
11294  endl done rm.
11295
11296#@cli mix_rgb : a11,a12,a13,a21,a22,a23,a31,a32,a33
11297#@cli : Apply 3x3 specified matrix to RGB colors of selected images.
11298#@cli : Default values: 'a11=1', 'a12=a13=a21=0', 'a22=1', 'a23=a31=a32=0' and 'a33=1'.
11299#@cli : $ image.jpg +mix_rgb 0,1,0,1,0,0,0,0,1
11300#@cli : $$
11301mix_rgb : skip ${1=1},${2=0},${3=0},${4=0},${5=1},${6=0},${7=0},${8=0},${9=1}
11302  e[^-1] "Apply matrix [ $1 $2 $3 ; $4 $5 $6 ; $7 $8 $9 ] to RGB colors of image$?."
11303  to_color repeat $! sh[$>] 0,2 mix_channels. (${1-3};${4-6};${7-9}) rm. done
11304
11305#@cli oklab2rgb
11306#@cli : Convert color representation of selected images from OKlab to RGB.
11307#@cli : (see colorspace definition at: <https://bottosson.github.io/posts/oklab/> ).
11308#@cli : See also: ''rgb2oklab''.
11309oklab2rgb :
11310  e[^-1] "Convert color representation of image$? from Oklab to RGB."
11311  repeat $! l[$>] split_opacity to_rgb[0]
11312    f[0] "
11313     l = (i0 + 0.3963377774*i1 + 0.2158037573*i2)^3;
11314     m = (i0 - 0.1055613458*i1 - 0.0638541728*i2)^3;
11315     s = (i0 - 0.0894841775*i1 - 1.2914855480*i2)^3;
11316     [ 4.0767245293*l - 3.3072168827*m + 0.2307590544*s,
11317       -1.2681437731*l + 2.6093323231*m - 0.3411344290*s,
11318       -0.0041119885*l - 0.7034763098*m + 1.7068625689*s ]"
11319    * 255
11320  a c endl done
11321
11322#@cli palette : palette_name | palette_number
11323#@cli : Input specified color palette at the end of the image list.
11324#@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | \
11325# parula | spring | summer | autumn | winter | bone | copper | pink | vga | \
11326# algae | amp | balance | curl | deep | delta | dense | diff | gray | haline | ice | \
11327# matter | oxy | phase | rain | solar | speed | tarn | tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | \
11328# uzebox | amiga7800 | amiga7800mess | fornaxvoid1 }
11329#@cli : $ palette hsv
11330palette :
11331  names=${-_palette_names} N={narg($names)}
11332  l[] if isint("$1") name=${"arg 1+($1%"$N"),"$names} else name="$1" fi onfail name="$1" endl
11333  e[^-1] "Input color palette '"$name"'."
11334  _palette_$name
11335  nm. $name
11336
11337_palette_names :
11338  u default,hsv,lines,hot,cool,jet,flag,cube,rainbow,\
11339    parula,spring,summer,autumn,winter,bone,copper,pink,vga,\
11340    algae,amp,balance,curl,deep,delta,dense,diff,gray,haline,ice,\
11341    matter,oxy,phase,rain,solar,speed,tarn,tempo,thermal,topo,turbid,aurora,hocuspocus,srb2,uzebox,\
11342    amiga7800,amiga7800mess,fornaxvoid1
11343
11344_palette2code : # Convert a set of input palettes
11345  sort_list +,n
11346  repeat $! l[$>]
11347    r {whd},1,1,100%,-1
11348    img2base64 0,0
11349    e[] "_palette_"{n}" : "
11350    b64=\"${}\"
11351    l[] ('$b64') s x,-119 ('\\\\\n') a[0--3] .,x rm. a x b64={t} rm endl # No more than 120 char / lines
11352    e[] "  base642img \\\n"$b64"\n"
11353  endl done
11354
11355# The color palettes below comes from the CImg library 'http://cimg.eu/'.
11356_palette_default :
11357  256,1,1,3,[16+32*int(x>>5),16+32*int((x>>2)&7),32+64*int(x&3)]
11358
11359_palette_hsv :
11360  256,1,1,3,[x*359/(w-1),1,1] hsv2rgb. round.
11361
11362_palette_lines :
11363  base642img \
11364"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2MTcKeJwNkhGgu1AYxT8YDP4QDB4Eg+BKEATBYBAEQXDhQhAMHgweBBeuXB\
11365gMgmAwCIIgCIJBEAQPguBKEASDwWDwYDD4/tGx75zf+Q4gAuhCPFVzM0CgvUXFmr0plIpkB2KwW4bppr16aMgRzge16wbwTbxW0L6Y0IxsjiFyZyQvAISoD\
11366PFMYgWlnTUI/M4bsllO+I5M0GuoWm+LP6kImrtktjhu8sxjaJ9V6zwNDy0FijoRcloRzGvghbDTAiO+Z8/Hv9JoKRbRr79JjwXqpOfBGxRasEejCH2kHyhr\
11367uCHk2jXJ5xkgGFdjPgp/4IZ4HDTk4LTjXrAED56dzkIskRGG8Gwh9FdqTh8bkMgp0+JG9tb+xXpLf58ugHG/GoPG6ZmnSGh+Bn1OyCkX23dUAjon753DhyT\
11368JuhiCaxcy8acf79DuuwbQNo/NItIqXXWF491E8R0ECNOppKlesjlaK8wsIqpKwL3QJwnhCygGtbCWbgbvJ0W4GFMOSrgnpIQ9rZHDYMyVS261dE1U1B/hkU\
11369InqiU/44MDQ/m4BMtHV9Wq5bgqjtJ6laFWKUDtOwsRFvaMLvSAZbHj+u9o/u0itAN5xHX85KSu4GblHN0BJgEHfPGtksZjK4JkzFY1AvkIMTEJBX7tO4HCF\
113706Hov85VvUXaEWosOIqirYUDkWW/OH7FGP18B9h0jjs49tyukEYSKYiZTjCmPnkxI7neAWkOOTxrs4A3blzFEhvJsgMXqmsicxgRGMFil7o5wS3HuIZmAGcc\
11371g8wvlM8UfPWXpfJN5JTPj+YlnxB6QD6tvGmdD9oUxoBrWc3mf8h5fjM="
11372
11373_palette_flag :
11374  base642img \
11375"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICMyNAp4nPv/n4Hh/wjGDCMeD4JIGEAMAH3RPtA="
11376
11377_palette_cube :
11378  base642img \
11379"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjMxIDEgMSAzICMzMzgKeJxj5BCQkFfXN7Nz8wuLS80trWnpmTJn8erNuw6dunTr0cuPP39/e/f83v\
11380Xzxw/s2LBy4cyJnY1VRVnJ0cHezjYmOqqyoryszFxCUoqahhaOHgER8en55XVtfdPmLV27dc+RM1fuPH79+ffvL2+e3Ll69ui+beuWz5ve395QUZCRGBHo6\
11381WhlpKUsLczNwsIjIqOsbWzt7BUUlZhZWNnYMWHGguXrt+87fu7avadvv/759enVo1uXTx/es2XNkjlTe1vryvLS4sL83e0tDDQUJQQ5Wdj4xGRVdU1tXX1C\
11382YpKzi6ubuybNWrhy486DJy/cePD8/fd/DIQAE6eghKKGgYW9u39YXFpeWV1r79Q5S9Zs2XP49OVbj159+vWf9oCQN5kI+oIOgIVbWFpZy8jK0TMwIjGjoKK\
11383hvX/6vOXrtu07evbqnSdvvvymQ0ABAC/raR8=" r. 256,1,1,3,3 round.
11384
11385_palette_rainbow :
11386  base642img \
11387"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjI1IDEgMSAzICM1NjcKeJxj5ublE+DnF5KQUVBWU9fS0tHV0dXVAVL6RiZm5haWFpaWVpZWVtbW1i\
11388BsbWMDJq3BwMrK0sLMWF9LRYqLkYGRAQygFAYbG2DkVrH2iUjNq2zpnTp3+eadB09fvvXo+dvPP/7+//v375/fv37++PH9+7dvX799/fr1GwQAWV+/ACEUA\
11389Bmf3796+uDW1XMnD+3dum75wpkTOhvK81Ii/Z3NDbSUxNhZuPkEhISFhMWl5RRV1LV09AyMjE1MLWwcnJxd3T29vH18/fz9AwIDg4JDQCA0DAjCI0BkRGRk\
11390dExsQnJaZm5ReW1T54Tp85au3rBp8+ZNmzZt3Lhxw4YN69euXbd2zWpMABTfsHHTpq3bd+7ae+Dg4eMnTp+9cPnC2RNHDuzatnH10gWzJvW01JUXZCbHhvt\
113915OttbW5gYGepDgJ6enq6OjpaWpoa6mqqKorysnJy8goKCopKSopKigrycrLSkuIggHy+/kJCQAB8PFzOHiIyKnqm9R0BYbHJmXmlVY2tn38SpM2bPmTd/wc\
11392JFi5csWbZ82dJly5avWLFiJRCsAoPVYGL1GqBDN27eugPoyqMnz1+/++gxMCRPH9mzdd3KhbOn9rU3VBZmxEcEByGD4BAwPzgoMDAgwM/P19vT093d1dnJ3\
11393trK3NhIX0dDTVUJ5GCgi5VVVIFpSkdXT9/A0AAGQH7U1dXW1gJ5UA2E1NTUNdQ1NDTU1dXUVJQU5WQkxYT4efkFhIQE+Xm5mAHNkQH/"
11394  r. 256,1,1,3,3 round.
11395
11396# The color palettes below have been converted from
11397# 'https://stackoverflow.com/questions/33273340/matlab-set-color-map-color-range'.
11398_palette_parula :
11399  base642img \
11400"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgMSAxIDMgIzIwMwp4nAHAAD//NTY2NDAoHAwDAwUJDhETFBQSEAwJBwYGBggLEBYdJS44Qk1ZZX\
11401F7hpGbpK22v8fP2N/n7/b8//79+/j29fX2+C0zOT9FTVZeZGlucnZ6foKGio+UmZ2hpKeprK6xs7W3ubu8vb6/v7+/v76+vby8u7q5ubm7vsLHzNLX3OLp8\
11402PiMmKWyvsvX3uHh4N7c2tjW1NPS0tHPzcrGwr65tK+ppJ6Yko2HgXx4dHBsaGVhXlpXU09LRj85NDAsKCMfGhUQJK9mpg==" r. 256,1,1,3,3 round.
11403
11404_palette_jet :
11405  base642img \
11406"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNzYgMSAxIDMgIzEzMQp4nGNgwA24JFSN7H2i0ksa+2av3Hb4wr3XP/9jgt/vH145tnPN/ImtMH1MvN\
11407Iapk7+sVnlLRPnrdl57PLDd7+xaPz55t7Fw9tWzulrKkmP8rU3UpXgYoTbPWXh+j0nrz35+BeLxm8vb587sHnZzO76wpRwLxt9JVF2PL5gYAAAc+1twA=="
11408  r. 256,1,1,3,3 round.
11409
11410_palette_hot :
11411  base642img \
11412"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI0IDEgMSAzICMxNDEKeJzjFRKXVdLQNTK3dXb3DY6IS87IK66oa+nsmzJz3pKV67fu2n/k1IWrt+\
114134/ff3+66//1AUMpABGdl5BcRlFdZBTndx9g0BOzS2urGvuADl18cr1W3buP3LyPMipr4BO/UdNu0kEjFxC0kpaRtbO3kHRydnFNS3dk2cvXr1lz5EzV+88e\
11414fP1LwA796yq" r. 256,1,1,3,3 round.
11415
11416_palette_cool :
11417  base642img \
11418"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzIgMSAxIDMgIzc4CnicY+ERkVHRMbFx8QmJSckpqWnpmTJnyZote46cuXLnyZsvf/58efPkzpUzR/\
11419ZsWbNkzpSelpqSnJSYEB8XGxMdFRkRHpb/BAAAHuY/4Q==" r. 256,1,1,3,3 round.
11420
11421_palette_spring :
11422  base642img \
11423"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjggMSAxIDMgIzcxCnic+/8fN2DhFVPQNLJ28Q1LyCyqbumdtmDlpt1Hzl578PLT79+fXj64dvbI7k\
114240rF0zrbakuykwI83WxNtJUEONlAQDKiDfN" r. 256,1,1,3,3 round.
11425
11426_palette_summer :
11427  base642img \
11428"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjggMSAxIDMgIzcxCnicY+EVU9A0snbxDUvILKpu6Z22YOWm3UfOXnvw8tPvprbuCVNmzl20bPX6LT\
11429v2Hjx26vyVm3cfPXv94cvPv2l4AACR+i4M" r. 256,1,1,3,3 round.
11430
11431_palette_autumn :
11432  base642img \
11433"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjYgMSAxIDMgIzQyCnic+/8fF2Dhk1DSMXPwDI5NL6xu6ZuxaM22Aycv333+8TcDTgAAaHcm2w=="
11434  r. 256,1,1,3,3 round.
11435
11436_palette_winter :
11437  base642img \
11438"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzEgMSAxIDMgIzc5CnicY2DAC1h4RGRUdU1t3fzC4tLzyxs6JsxYuHLjzkOnLt16/Prz778/v358+/\
11439Lpwzs3rlw4ffzw/t3bN69bvXzxvFnTJvV1tTUBACEGJp4=" r. 256,1,1,3,3 round.
11440
11441_palette_bone :
11442  base642img \
11443"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KOTkgMSAxIDMgIzI1Mgp4nGNkZuXg4uETFBaVkJKRU1BWVdfU0TMwNjG3tLZzcHJx9/T28w8KCY+Mjk\
11444tISk3PzM4rKCwpq6iqbWhqaevs7p0wccq0GbPnzl+4ZNnKtRu3bN+9/9Cxk2cuXLl+696jpy/ffPj8/dc/RmKsCI2IiU9KzcjJLyqtqK5rbGnv6ps4Zfqsu\
11445QsWAw3esHnbzr37Dx09cfrM+YtXrt24fef+w8fPnr968+7j56/ff/35x8TKwcMvJCopLa+koq6lZ2hibmXnCDTZNyA4PDI2ITktE2hwSVlldV1DU2t7Z3cf\
114462N2zwO5esXrtho1btu3YvXf/wSPHcFoBAJTokrQ=" r. 256,1,1,3,3 round.
11447
11448_palette_copper :
11449  base642img \
11450"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTE1IDEgMSAzICMzMzkKeJxj4+TmExQWk5SWU1RW09TRNzKxsLK1d3b18PINCA6NiI5LSE7LyM4rKC\
114516rqK5rbGnv7O2fNGX6rLnzFy1dvmrthk1bd+zee+DQ0ROnz164fO3mnXsPnzx7+eb9xy/ff/359x87YGFl5+Dm5RMQFBYVk5CSlpVXVFZRU9fU1tEzMDIxN\
11452be0trG1d3R2dfP08vH1DwwKCY2Iio6NT0hKTk3PyMrJKygsLi2rqKyurW9sam1r7+zu7eufOGXa9Jmz5sybv3DxkmUrVq1Zu37Dpi1bt+/avXffgUOHjx5j\
11453YgZZysnFzcPLxy8oJCwiIiYuISklLSMrp6CopKyiqqauoakFdIG+gaGRsYmpmbmFpZWNrZ29g6OTs4ubu4ent4+vn39AYFBwaFh4RGRUdExsXHxCYnJKalp\
114546RmZWdm5uXn5BUXFJaVl5RWVVTW1dPQCk5ZAs" r. 256,1,1,3,3 round.
11455
11456_palette_pink :
11457  base642img \
11458"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTIxIDEgMSAzICMzMDUKeJxT1DG1cfbwC46ISUzNzC0sraiub2rr7O6bOGX6zDnzFy5ZtnL1ug2bt2\
114597fuXvvgUOHDx85euz4iZOnTp85e+7c+QsXL12+cuXqtes3bt66ffvO3Xv37j94+PDR4ydPnj57/uLFy1evX799++79hw8fP33+8uXr12/ff/z49ev37z9//\
11460/37zyoso6SurW9sZmVr7+Tq7uXjHxQSFh4ZHRuflJyanpGVnZtfWFRSWl5RWV1b39Ta0d0PddLiZSvXIJx05NiJU2fOXbh89fqNW3fu3X/0BGgzFS1uaGxq\
11461aW1r7+zu6e2bMHHS5CnTps+YOWv2nHnzFy5avGTpsuUrVq5avWbtuvUbNm4COmn3vgOHj504fRbonhu3795/+OTZi1dv33/68u3Hrz//AH2168E="
11462  r. 256,1,1,3,3 round.
11463
11464_palette_vga :
11465  base642img \
11466"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICMxNzgKeJz7/+fn10/vXz9/8uDOjasXzpw4sv/IiTMXrt648+DJ89fvP339+ec/Bv\
11467jx8dXj25dOHdi2ZsGU1tIEVwbCwDWhtHXKgjXbDpy6dPvxq48/MM2Q0zF3cPcLiYpPycgtLK2saSAMaipLC3MzUuKjQvzcHcx15IhwB4Yt/zH8v2vr+pWL5\
1146807r72goz00KdTPEdDthQEYIYbgMw3c0CSEi/E9G7JIRQoT9T0YYEgYAJsphWQ=="
11469
11470# The color palettes below have been converted from 'https://matplotlib.org/cmocean/'.
11471_palette_algae :
11472  base642img \
11473"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNTAgMSAxIDMgIzE2MQp4nAGWAGn/1M3Hwbq0raegmZKLhH11bWVcU0k+MygeFg8JBwYICg0PEhQVFx\
11474gZGRkZGRkYFxYVExL38u7p5ODc19PPy8fEwLy4tbGtqqainpqWko2JhH96dnFtaGRfW1ZRTUlEQDw3My4qJczFvbavqKGak42GgHp0bmhjX1pXVFJRUFBPT\
11475k1MS0lIRkRCPz07ODUyLywpJiMgHBkV1p88Lw==" r. 256,1,1,3,3 round.
11476
11477_palette_amp :
11478  base642img \
11479"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjEgMSAxIDMgIzE5NAp4nAG3AEj/8O3r6ejm5OLh397c29nY1tXU0tHPzszLycjGxMPBv728uri1s7\
11480CtqqainpqVkIuFgHp0b2lkXlhTTkhDPurm4dzX0s3Iw765tLCrpqGcmJOOiYSAe3ZxbGdiXVhTTklDPTgyLSgjHhkWEhAODg0ODg4ODg4NDQwLCgnq5N7Y0\
11481szGwLq0rqihnJWPiYN9eHJsZmBbVU9KRUA7NjIuKygmJCQkJCUmJicoKSkpKCcmJSMhHxwaGBUS64BWpQ==" r. 256,1,1,3,3 round.
11482
11483_palette_balance :
11484  base642img \
11485"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTIxIDEgMSAzICMzNzQKeJwBawGU/hgaHB4gIiMlJigoKSkoJyQgGhMNCgoMERYcIictMzg+Q0lOVV\
11486thaG51fIKJkJado6mwtrzDyc7V2+Dm7PDu7Oro5+Xj4t/e3dva2NfW1NLR0M7Ny8rIxsXDwcC+vLq4tbOwraqno56alZCLhYB6dG9pY15YU01IQj0dICImK\
11487CsuMDM2OTs/QUVJTFFVW19kaGxxdHh8gISHi46SlZmcoKOmqqyvs7a5vL/CxcnMz9PW2t3h5ens6OPe2dTPysXAu7axraijnpmVkIuGgXx3cm1pY15ZVE9K\
11488RD45My4oIx8aFhMQDw4NDg4ODg4ODQ0MCwoJREpRWF5lbHN7goqSmqGpsLa7vb69vby8u7u6urm5ubm5ubq6uru8vL2+v8HDxcbIys3P0dXX2t3f4ubp6+f\
11489h2tXPyMK9trCqpJ6YkYuGf3l0bWhiXFZQS0ZBPDczLisoJiQkJCQlJSYnKCkpKSgnJiQiIR4cGhcVEqaTs4c=" r. 256,1,1,3,3 round.
11490
11491_palette_curl :
11492  base642img \
11493"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI0IDEgMSAzICMzODEKeJwTFROXkJCUkpKWlpaRARJSkuJiosJCggKCwmLSCup6Znbu/uHx6fnldW\
1149429U2YtXLF+6+6DJ85duXn/6euP3/78+fn96+eP79++efXy+bOnjx89uH/3zu2b169duXT+7KkTRw/u271984Y1K5YsmDN9Ul9nS31VaUF2anxUqL+Xi72Vq\
11495ZySmpaekZmVraOrp29gSERMQnJGTkFxeXV9U3tX38SpM2bPW7h0xaq1GzZv27F738Ejx0+dvXDl+q27D5+8ePPhy+d3r57cv3X1wumjB/ds37hm+UKgHb0d\
11496TbUVxXmZKQnR4UF+Xq6OtlZmxvraGqpKCrJgn4kIg/wmwMfr4ubh7esfFBIWGR2bkJSSnpWTV1BUXFpeUVVdU1tXV9/Q2NTc2tbR1Tth8rRZcxcuXbVu846\
114979h0+cvQz2+cfXT+5eO3/i4O4ta5cvnD21v6u1obq8uCA3KyMtJTkpMSEhIT4+PgEG4uPjYqMjw0OD/L09nO2tzACwu7nF" r. 256,1,1,3,3 round.
11498
11499_palette_deep :
11500  base642img \
11501"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDkgMSAxIDMgIzE1OAp4nAGTAGz/+e7j2M7Dt6yhlYqAdm5mYVxYVVJPTUtKSEZEQ0FAPz49PT0+P0\
11502BBQT8+Ozk2MzAsKfv38/Ds6OXh3dnV0czIw764s66po56Yk46Ig396dG9qZF5ZU05IQz46NjIvKycjHxvJw724s6+rqKWko6Kjo6Ojo6OjoqKhoJ+dnJuam\
11503ZeWlZSTkY+Mh4B4b2ZeVU1FPTYvM2JJnA==" r. 256,1,1,3,3 round.
11504
11505_palette_delta :
11506  base642img \
11507"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM3NzAKeJwTEBIWEROXkJSWkZWTV1BUUlZRVVUDAlVlJUV5OVlZGWlpKRCQlp\
11508aWASmBqFFT19DU0tHVNzA0NjWzsLKxc3Bycff08Q8Ki4xNSMnIKSipqGlo6ejpnzx91rxFS1euWb952869Bw4fP3X2wuVrN+8+ePzs5Zv3n7/9/Pvv7+9fP\
1150979/+/r508f37968fvXi2dPHjx7cu3PrxrUrl86fPX3i6OEDe3dt37Jx3arlSxbMnTVtcn9PR2tjXVV5cUFORmpibFRYkL+3u4uDraWZkb62hqqSvIykuIiQ\
11510AB8vDzcXFzc3Dw8vH7+AINCbIqJiYuLiEhISkmAAZIiLi8srKCmrqqlramnr6ukbGBmbmJpbWFqDPeTm4e3rHxgcFhEVE5eQlJKWkZWTV1BUUlZRWV1b39j\
11511c2t7Z3ds3YdKUaTNmzZk7f+HipctXrFqzdv3GTVu2bt+xc/eeffsPHjpy9NiJk6fPnLtw8fKVa9dv3r5z78Gjx0+fv3z99t2HT1++/fj15+f3L58+vH398t\
11512mTR/fv3r55/eqlC+fOnDp5/Ojhgwf27dm9c8e2rZs3bVy/bu2aVStXLF+2ZPGihQvmzZ0ze+aM6dOmTp40sb+vt7urs72tpbmxoa62uqqirLS4sCAvJyszP\
11513TU5MT4uJioiLCQowM/H29Pd1dnR3tba0tzUxMhAT0dLQ01F2d7JFRJvMQnJ6dl5RWVVtY2tnb0TgL6aOWv27Dlz5gLBvHnz5s+fv2DBgoULFy5atHjxkiVL\
11514ly5dtmz58hUrVq5ctWr16jVr1q5dt27d+vUbNmzYuHHT5s1btm4DhsEuUCAcOHj4yNHjJ04BQ+H8xUtXrl6/cQsYDPcfPHz0+MnTp6ePH96/e9umdSuXLpw\
11515zY0p/d3tTXVVpYW5GSkJ0eDAwYp3trS1MDHS11JUVZKUlRIUF+Hi4ONjZWIGAjZ2Dk5uHj18QlIIlpYCpE5J+gYkTmDq1tLV14EBbW1tLS1NDQ11NVQWYsB\
11516Xk5WSkpSTERUWEAGwPYP8="
11517
11518_palette_dense :
11519  base642img \
11520"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNzQgMSAxIDMgIzIyNAp4nHt878alM8cO7N66YfXSBbOnT+rram9uqKmqKCstKQaBktLSsvLyioqKys\
11521pKIFleDpIoLMjLyc5MS0mKj4kMC/L38XBxsDF//+bV8ycP792+ce3ShTMnjx3ev3fXts3r16xYsmDOjCkTejpaGmoqivOz05Pjo8NDAnw8XB3trMyNDfW0N\
11522FSVFGSlJcXFRISFhQQF+Pnev3398sWzp0+ePH4EA48fP34CBo8fP3p4/+7tm9euXDh78tihfbu2bly7YvG8mVP6u1rqK0vyMpJjwwN93BxtzI30NFUBgy55\
11523hg==" r. 256,1,1,3,3 round.
11524
11525_palette_diff :
11526  base642img \
11527"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTQzIDEgMSAzICM0NDAKeJwBrQFS/gcJCw0ODxESFBYWGBseIiYqLjI2Oj5CRUlNUVRYXF9jZmtvcn\
11528Z5fYCEiYyQk5ebn6Onq66yt7q/wsfLz9PX3N/k5+vu8fP19fX08e/s6ebi39zZ1dLOy8jEwr67uLWyr6yppqOgnZqYlZKPjIqHhIF+fHl2c3Bua2hkYF1ZV\
11529lJPS0hFQT47NzQwLSomIyAdIyYpLC8zNTk7P0JFSEtNUFNVWFteYGNmaGtucHN2eXx+gYSHioyPkpWYm56hpKeqrbC0trq+wMTHy8/R1djc3+Pm6ezu8PHx\
115308O/s6ufj4NzZ1dLOy8fDwLy5trKvq6mlop+bmJWSj4yJhoOAfXp3dHFubGhmY2BeW1hWU1FOTEpHRUI/PTs4NTIwLSooJSJAQ0dKTlFUWFteYWRmaGpsbnB\
11531yc3V4enx9gIKEhoiKjY+Rk5aYmpyfoaSnqauusLO2uLu+wcTGyszP0tXY29/i5efq7O7v8PDv7Onm4t3Z1dDLxsK9ubSvq6einZqVkY2IhIB8d3Nva2djX1\
11532tXU09LR0RAOzg0MS0qJyQiISAeHRsbGRgWFRQSEA4ODAoIBjEs26k=" r. 256,1,1,3,3 round.
11533
11534_palette_gray :
11535  256,1,1,1,x r. 100%,1,1,3
11536
11537_palette_haline :
11538  base642img \
11539"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDYgMSAxIDMgIzE0OQp4nAGKAHX/KiwuLSgiGxQPDAwPEhcbICUpLTA0Nzo8QEJGSU5TWF9mb3mEkJ\
115406sucXR2+bw+RgaHB8mMDlBSE9VW19laW5zeHyBhouQlZqfpKmus7i9wsbLztLV2Nrd4OPm6e1xfoyZoaKfnJiVkpCOjIuKiYmIiIiHh4aFhIOBfnt4dHBrZ\
115412JeW1xgZW12gIqU7TdADQ==" r. 256,1,1,3,3 round.
11542
11543_palette_ice :
11544  base642img \
11545"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA5IDEgMSAzICMzMzgKeJwBRwG4/gQGBwoMDhETFRgaHB4gIiQmKCorLS8wMjQ1Nzg5Ojs8PD0+Pj\
115464+Pj4+Pj4+Pj4+Pj4+Pz9AQUJDREVGSEpLTE9QUlRWWFpcXmFjZWhqbXBzdnl8f4OGio6Slpqfo6essLS5vsLGy8/T2Nzg5OkGCAkMDhASFBYYGRwdHyEjJ\
11547CYoKSwtLzEzNDc4Ojw+QEJER0lLTlBSVVhaXWBiZWdqbW9ydXd6fX+ChIaJjI6Rk5WYmp2goqSnqayvsbS2uLu9v8LEx8rLzdDS1NfZ293f4eTm6evt8PL0\
115489/n7ExYaHSAkJyouMTU5PEBDR0pOUlZZXWFlaW1xdHh8gISIjI+Tlpmcn6KkpqiqrK6vsLKztLW2t7i5urq7vL2+v8DAwcLDxMXGx8jJysvMzc3P0NDS09P\
11549V1tfY2dvc3t/h4+Xn6evt7/L09fj6/ETzn2w=" r. 256,1,1,3,3 round.
11550
11551_palette_matter :
11552  base642img \
11553"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgMSAxIDMgIzIwMwp4nAHAAD///fz8+/v6+vn4+Pf29fTz8vHw7+3s6ujn5eLg3drX1NDNycXAvL\
11554ezrqmkn5qVkIqFgHt1cGplYFpVUEpFQDs2Mevl4NrUz8nEvrmzrqijnZiSjYiCfXdybGdiXVhTTkpGQj46NzMwLSsoJiQiIB8dHBsaGhkYGBcXFhYVFBMSE\
11555Q+uqKOemJOPioWBfHh0cGxpZWJfXFpYVlRTUlJSU1NUVVdYWVtcXV5fYGFiYmNjY2NiYWBfXVxaV1VST0xJRkM/PV5d6A==" r. 256,1,1,3,3 round.
11556
11557_palette_oxy :
11558  base642img \
11559"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2NTYKeJyzd3RydnP39PL18w8MDg2LiIqJjUtMSknNyMzOyS8oKi4rr6iqqa\
11560tvaG5pbevo7Oru6e0LDAgMDA4JCQ0Pj4iMigaqj09ISEpOTklLS8/IzMrOyc3LLygsKi4uLSsvr6yqrq6tq2tobGoCGgAzoX/CxEmTp0ydNn3mzFmz586bP\
115613/hosVLli5bvmLV6jVr163fsHHzlq3btu/YuXvP3n0HDh46dOToseMnT50+c+78hYuXr1y9duPmrdt3791/8Ojxk6fPX7x89ebt+w+fPn/5/u3L54/v3719\
11562/frVq1cvIeAFEDx//vzZs2dPnz558uTx40ePHj18+ODB/fv37t29e+cOKxCwQQE7DED5rGDAwgJSws7Bxc3HLygkKibh6x8QGBQcEhoWHhERFR0dEwcMgMS\
11563k5JTUtPT0zKys7Ny8vPxCYACUAgOgAhgANbV1DcAAaAGFIDAAenp7+4D+nwTy/wyg/+cAAwDo/8Vg/wMDAOT/TZu3QP2/dz8wAAj7/937T5+//vv75/fPH6\
11564BQ+PTh3ZvXL58/ffLowb27d25ev3b10sXz586cOnni6JFDB/fv3bN7x/ZtWzdt3LAO4n92dg4OTk4uLi5ubh4eHl4g4AMCfhAAMYB8Hm5uLk5OTg4OdnZvs\
11565P+DQ8LC4AkgPjEpKTk1FSUBFJWUlpVVVFZW1dTW1jc0NgJTUGt7R2cnSgIABsCcuXPnLYAmgJXAAAAlgE2bNoMSwK7de/bsAwbAYWAAnAAGwNlz585fvHT5\
11566yrXrN27evnP3HigAngED4DUwAD58fJSRlpIYExUe4uft4epgZ2NlYWZqYmRooK+ro6OtqamhrqaqqqKspKSoIC8vJysrIyMlJSkJADQjlG8="
11567
11568_palette_phase :
11569  base642img \
11570"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA2IDEgMSAzICMzMjkKeJwBPgHB/qissLO2uby/wsTHyczO0dPU1tjZ29zd3d7e3t7d3NvZ19XSz8\
11571zJxcG9ubWxrKeinpiTjYeBe3VuaGFbVE1HQTs1MCsnIyEeHBkXFRMRDw0MCwsOEhgeJS01PkdQWWJqcXh/hIqPlJidoqZ2dHJwbmtoZWNhXVtYVVJPTElFQ\
11572j47NzQwLSsoJiUlJicpLC8zNjo/Q0dLT1NXW19iZmltcHN2eXx+gYOGiIqLjY6PkJGSk5OUlZWWlpeXmJiZmZmampmZmZiXlpSSkI6MioiGhIF/fXp4DhIX\
11573Gx4iJiouMjY6PkNHTFFXXGJobnV8g4qSmaGor7e+xMvQ1tvf4+fq7O/w8vPz9PPz8vHv7ero5eHd2dTPysbBu7axrKejnpqVkIyHgnx3cWtlXlhRSUI5Mio\
11574jHRcTEA8ODQ0NDQ0NDPDwmdk=" r. 256,1,1,3,3 round.
11575
11576_palette_rain :
11577  base642img \
11578"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTE1IDEgMSAzICMzNDIKeJx7++bVy2dPHz96eP/e3Tu3b928cf3q5Yvnz5w8dmjf7u2b169evmTBnB\
11579lTJvZ2tjbWVpYU5makxEeHBfq4O9taGOtpqirIiIsI8nJxsLGyMDMzs7CwcXBy8woIiYhJSEnLyikoKCopq6ioqIIAkFZRVlZSUnzz6vnTRw/u3bl57cqlC\
115802dPnzh25NCBfXt37dyxfduWzZs2rl+3ds2qlSuWL12yeOGC+fPmzJ41Y/q0qZMnTZzQ19Pd1dHW0txYX1tdWVFaXJifm52ZnpqcGB8bFREWEhTg5+Pl7urs\
11581aG9rbWluYmSgr6OloaaiKC8r/fHdq+ePH9y9efXimZNHDgA9tm7V0oVzZ06bPLG/p6sTZGZTQ31dbU1VZUV5aWlJcVFhQX5+Xl4uDORBAYidk5OTnZWVmZm\
11582enpaanJSYEBcTHRkRGgy23RNovZMD0H4rSwDxo6Ac" r. 256,1,1,3,3 round.
11583
11584_palette_solar :
11585  base642img \
11586"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzUgMSAxIDMgIzExNgp4nAFpAJb/N0BJUltkbXZ/h46VnKGnrLG1ur7BxcjMztHU1tja3N3f3+AVGB\
11587odHyIlJysvNDpARk5VXGRrc3uDi5OcpK22v8jR2+Xu+RkcHyEjJCQkIiAeHBoYFhQTExITFBUXGh0gJCgsMDU6PkNIm9UqlA=="
11588  r. 256,1,1,3,3 round.
11589
11590_palette_speed :
11591  base642img \
11592"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDkgMSAxIDMgIzE1OAp4nAGTAGz//fn28+/r5+Pf2tXOyMG5samgl46FfHJpYFZNQzoxKCAZEg4LCg\
11593wOEBMVFxgZGRkYF/rz7efh29bQy8bCvbm1sq6rqKWinpuYlZKOi4eDf3t3c25pZWBbVlFMSEI9ODMvKSTIvbKnnJCFeW5jWE1COS8nHxgRDAcFBQgLDxQYG\
11594x8iJScpKywsLCwrKiknJCIfGxgUF7w7kA==" r. 256,1,1,3,3 round.
11595
11596_palette_tarn :
11597  base642img \
11598"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA1IDEgMSAzICMzMjYKeJwBOwHE/hcaHiEjJyktMDU8Q0lQV11kanF4f4aNk5uiqbG4v8XKzc/S1N\
11599bY2t3f4uPm6evu8PT2+vv8+/j08Ovn4tzWzsa+t6+ooJmSioR9dm5nX1dPRz84MSolIh8eHRwaGRcUExAOCwoICQsODyQoLTE1OT5CRklMT1JVWFpdYGNlZ\
116002psb3F0dnl7fYCEiY6Ump+lq7C2vMHHzdLY3uTq7/T39vPu6eTf2tXRzcrGwr66trOvq6ekoJyZlpKOi4eDf3t3cm5qZGFcV1NOSkVBPTgzLikkIA0ODw8Q\
11601Dw8ODQwNEBETFhgbHR8hIyUnKisuMTQ3O0FIUlpia3N8hI2Vnqavt8DJ0dri6/H18uzj29HJwLewq6ikop+dm5mXlJKPjYuJh4WEgoF/fnx7enh2c3JvbWt\
11602paGZkYmFfXVpVUfhNmgM=" r. 256,1,1,3,3 round.
11603
11604_palette_tempo :
11605  base642img \
11606"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTEyIDEgMSAzICMzNDcKeJwBUAGv/v369/Tx7uvn5OHe29fU0c3Kx8PAvLm1sq+rp6OgnJiVkI2JhY\
11607B9eHRwa2hjX1pVUUxIQz86NTEtKSUhHhsYFhMSEREQEBEREhMUFBUWFxcYGRkaGhsbGxscHBwcHBsbGxsbGhoaGRkZGBgXFxYWFRX08vDu7Oro5eTi4N7c2\
11608tjW1dPRz87MysnHxcPCwL+9u7q4t7W0srGvrqyqqaempKKhn52cmpiWlZKQj42KiYeEgoB+fHp4dXNxb21raWZkYmBeXFpXVVNRT01LSEZFQ0A+PDo4NjQx\
11609Ly4rKSclIiAe8+/s6eXi3tvY1dLOy8jFwr+9ure0sa+sqqelo6CenJqYlpSSkY+OjIuKiIeGhYSDg4KBgYGAgH9/fn5+fXx8e3t6eXh4d3Z1dHNycXBvbm1\
11610samloZmVkY2FgX11cW1pYV1ZUU1JQT05MS0pIR0ZFRA7PoM4=" r. 256,1,1,3,3 round.
11611
11612_palette_thermal :
11613  base642img \
11614"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA3IDEgMSAzICMzMjUKeJxjYWFhZWVj5+Ti4RcUkZBRUNUyMLWyd/HwCQiJiI5PSsvMyS8qq6ypb2\
11615rr7J0wefqseQuXrlizYfP2XfsOHj155vzla7fuPnj07MXrt+8/fv7y7fuPn79+/YaAX79+/vzx/dvXL58/fXz/7s2rF8qq6hpa2jp6+gaGRkbGYACkTMzML\
11616aysbe0dnJxd3T28vH39/AODgkNCwyMio6JjY+MTEpOSU1LTMjKzcnLzC4pKyiqraxua2zp7JkyeNmveoqUr127cumPPgSMnzly4cuPOgycv3nz8+tPE0tbJ\
116173ScwPCYpI6+0urGjb8qM2fPmz58/b+6c2bNmzpg2dcrkSRMn9Pf39fb2dHd1drS3tbY0NzXU11RXlpcWFeRlZ6QmJcREhgUH+nl7uLk6Odrb2dqAgK2tHdS\
11618lQIcGBoeGRwIArFub1Q==" r. 256,1,1,3,3 round.
11619
11620_palette_topo :
11621  base642img \
11622"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2OTEKeJxT19TS1tXTNzQyNjE1s7C0sraxtbWzt3dwcAQBBwcHe3s7OztbCA\
11623Cy7MFyTk7Ozi6urm7u7h6enl7e3j6+fn7+AYFBwSGhYRGR0TFxCUkp6Zk5+UVllbWNrZ29E6fOmrd4+eoNW3bsPXj05LlL127de/Ts9fvP33/z8vELCAoJC\
116244uIiolLSEpKScvIysopKCqpqGvpGhibWVrbOTq7eXr7BQaHRUbHJSSnZWTnFRSXVVTXNTS3dXT3TZg8bebseQuXLF+5Zv2mrTt27zt46PDRY8dPnDx1+szZ\
11625c+cvXLp85eq16zdu3rp95979Bw8fPX767PmLV6/fvHv/8dPnr99/SEmDrFNWUVMHB4SBobGJmbmllY2tvaOzq5uHt69/YHBoRBTYR2mZ2bn5hcWl5ZXVtfW\
11626NTS1tHV09fRMmTZk2Y9ac+QsXL12+cvXa9Rs3b92+c/fe/QcPHz1+8tSZcxcuXr567cZNkPVg21+8fP3m7fsPQMu//fj5+4+qmoaWDjD8jYGhD7HVxc3DC2\
11627htQCAwMMMjIqNiYuPiE5OSU1LTMzKzcnLz8guLikvKyisqq2tq6xoam5pb29o7urp7+vonTJw0ZSrIMfMWLFoCdcy2Hbv27AO5BeSUy1ev37pz78Hjp89fv\
11628Xn/8fO3H7/+6ugZmphb2tg7uXp4+wWFRkTHJ6UCI664rLKmvqm1vbO7F2zupMmTp0yZOnXatGnTp8+YMXPmzFmzZs+eM2fu3Lnz5s2fv2DBgoULFwHBYgwA\
11629FVyyZOmy5StWrlq9dt0GRAgdO3EKGPHAmAdHvZS0tLQMMPqBCQAM5MHxoqEJS59m5uagBGptYwNLiSjADgIcXDx8/IPDomITUzKywQmwoaW9u2/S1JlzFix\
11630evmrdxq079xw4fPzUuUtXb9558AQAVgJv5Q=="
11631
11632_palette_turbid :
11633  base642img \
11634"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI2IDEgMSAzICMzNzAKeJx78fzpk8ePHj64f+/undu3bt64fu3qlcuXLl28cP7c2TOnT506cfzY0S\
11635OHDx08sH/f3j27d+7Yvm3L5k0b1q9ds2rl8mVLFi2YN2fWzOlTJ0/s7+3p6mhraWqoq6mqKC0uzM/NzkxPTU6Ii4kMDw0O9Pf19nR3dXa0t7W2NDc1NtTX1\
11636dZUV1X+8unD29cvnz15dP/u7RvXrlw6f/b0yWNHDh3Yu3vn9q2bNqxbvXI5xI4Z06ZMmtAHsaKxvra6sqKsBGRJTlZmelpKcmJCXGxMVGREWGhIcGCAv5+P\
11637t5enh7uri5Ojg72djbWVpYW5qYmxkaG+nq6OlqaGuqqKsqKC/KrlSxbOmz1j6qT+3s72lsa6msryksL8nCyguxPjY6IiQkOCAvx8vb083N1cXZyBZgENs7O\
116381tbWxsbEGASsYsIQACyAwNzc3MzMzNTUxMQbaZ2hgoK8HtFNHW0sTaK2aqoqKshLQZjlZGWkAeF+paA=="
11639  r. 256,1,1,3,3 round.
11640
11641# The color palettes below comes from the LoSpec palette list 'https://lospec.com/palette-list'.
11642_palette_aurora :
11643  base642img \
11644"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2ODYKeJxt0kFo01AYB/B3qYwwaDbCaEG6aS4T3ME4qJrgVpCiLUqolEgYbI\
11645QirIce9kQLknXbZSplWaGMSaeUYWU0g21QD0XbFLaasimMQqmUOiyBHiyOemgZZfpMql7Ew/v43vtOv+//QN/5yze89x89eZ58++HzMQJjQJnXizmktH4gh\
11646JSQN6SgbqO/bgHgfnfwejYwG88d1hqtdivEwSVZ1U60PQnSDiitv1Er31R5Cd5zWkmapTnok9ZrKXmRNdtI2ifJalbekDiONg/Stzj4UJIPNVmCHOOckWS5\
11647gbRaWS3suqnhcSFRKaEWaqNyJRWfvCuEN3fqCBViK9DPDA/1EhghuoHpbH//GTDC+CNesQc3DZgBBSzktGuZzwZdAoMDspedWlzLpMvlkzqqZDJh3uegvPE\
11648CQp3TemY1Av7xG9iuXz/zOaMjxkLGo/KnilvJeHQhMB18FkumCzVAO7xQkovqS8iN/PVralYSWRvL+fRRTv10cqylxGl9rG/qe1FNG2Cr4Q/o/opmrIOkxj\
11649kOblbVdFwS+eEBzGIhhjwML2QSsQDvplhhLrpTKn1FzWphIzDqnFgp11EznwpfWd07avxEjXr5aI2/aHf5PP7gi1fb+XSAdzBncfwcjlMOIZLYfQ89Qxdww\
11650kIAzGoFrD9+zYaPEv/zG0xWzx8ddP3GJhS9GPnrAeSeLkcXpyZnHojh2FYR0Yzhr8iQowdJmvPqMaqy5OWuU2Y7zeoRL0jrnf380qSTdHb9Ta2qqhIc0b+G\
11651lNzXvw4yrr/zr7W0YjYdj9hJWx/AcBdGCZcYMwZMBAZ4gPG4y+G0mkyEqafXhGGQJ9g71FzEv5H4iFCzdDThEddW2pnqrk7oNFG7VclEb24fFg1S5xShLyi\
11652ffewTnPaFoJJPh4NXb9t/Aejmgpc="
11653
11654_palette_hocuspocus :
11655  base642img \
11656"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2MzQKeJxjk3x5buu8lopLu5f0lsV5mHk7WxlpKkrINhWlhnla6iotmtpakR\
11657Ht7b5oUlNRUpCT+anN87qK4jzteqtz4/wdjVRfXNy1pK880UdUiJ+Xi52VyUJTXpSPg1ns57Vt08sjLGTeHp5bFW2vovrr3qFVfWVRLk9Ob53TmhMW8OT0p\
11658tlNOUF2DASAqIiwkKAAPx8vDzcXFycH+9PrZw5uW71g+uJpnQ1lWQkRMGW/Hp3aNKspK1wUAjR/3Tu2blpdWsDzFy9fv3n3/uOHG4fXz2orjGfklTN0DEwq\
11659bZ26bOfJm6/+MIg83t2fZq1amRUb6GiqIQu0go2NlUVgamtpWpibuUZ+Upifq6WBfn1xerSfk5n29LaytDB3c8OOyowoL0sdhRktxUlBrqYWx3fPbczwMZD\
11660ePK0ywdtcWevjkRnFweYy3Dc29GT7mcrIf9ndGW+rxM0Mda/AgcWdBVHuRqpk+n9qX3NFYWZ8eKCosCAfDxcHO5Dm5+XmZP9258jKSeUJEcBwnt9ZHOt5cP\
11661Oymf1NpSncvHwCgkIiouj+F5adluOmIchflwfyhqbslb3LJlQm+bismNJSkOBjo1eQHO4P9L/pjiVTmvPj/ewXT2goiPGxNN+zfEp9TpiLcU9lZoyPvZGqi\
11662CAo+pkZfz29sHvZxJriO1vA/uZ+cnhZZ7afoQjMP0yioiIiIsLCwkJCQoKCggLK3z69evbo3q1rl86dOnZo365tm9YuWzRv1rRJfV1tTXVVZc115cX52enJ\
116638dHhwf7e7s6/P715/vDOtQtH9+3avnn9mhVPz+9cPLE61R/mf3h6gMY/AyMzCxs7hxyu+AcA2aYUlw=="
11664
11665_palette_srb2 :
11666  base642img \
11667"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM0OTcKeJz7//398/vXzx/fv3398vnT+9vry/PT48P93e3N9dXlxfnZGcAS0/\
11668ra6kpzU2NDfJz3b169eDZIXXZ8sLe9sfZ/GIAYdBrG/fz0xul9G5dMh8ufX9+f76/P///19cPrZ7cXw4Tf3z+/f/38/vr8eH97fXn+/5+f3z69f/NyqIWh5\
11669QWZiVFB3s42prpqckD33Du+dmpNipcRAxT8/39///z6ePuXR+aWhzjqSV/dv2pqQ1Yo2Pkg+efH1vYUBxlKMKCB89vnt+eH26v/J+T/5QtmTOhsrCzKSooK\
11670BjrDuLo4Pzs5PjzY390ZqEZb+fXj25eB2neDgmVyd3P9uzund66Y3gZ2vK+rrRmG/2/vnl0d7awNcbY8uru2L+6vzo72t9dTV5AU4mZl+P/hwYUDGxZMaCh\
11671ICHAwUOD//+TE2knlUfZQb8vnQ/XB/P/x9eO7186f2D+3vzx+P1ABDv//h4c3If/3dzRUFILiwMvJ2lhbRdob6G9rc2N9bXVleWlxYf77l4/v3rx89uTm6u\
11672Ls5Ohg/zs7p5eGmknCbXoP9TeMXxzurC8vzM5AwP8wvr+3m6ONuZGuhrKcpAg/F8uKKQ3ZES4Gsujx//nhud3Lm2NMYKENEgX5Hx78z89tndOSHeRvb64uz\
116738/+Hxb/ADJVV38="
11674
11675_palette_uzebox :
11676  base642img \
11677"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1Mwp4nGNQ8ciduO3Wf4aRSkOBChR4QEEuFEyEgm1QcAsK/kPBcNFPLgilEK\
11678yiEPynEAAA16V90A=="
11679
11680_palette_amiga7800 :
11681  base642img \
11682"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1MzMKeJxjEFQydglNK++YuWr3mbvv/v////3Z1SObF01syI/ztdGWMnQKTiltm7\
116835i56nbb/7+h4CfL64f27pkclNhgr+dfUBiUfOUpduO33j5Cyr9/93dM7tXzewoTwt1MZbVcwhMKm6Zumz7iZsvfwMlP9w/t3fN7K7KjHA3UxVhJgYGDkkta\
116845/YvPoJCzcdvvIUTRIM2MQ1LL2ic2r75m84eGD9vN6a7ChPC3UxVqg0g4CikXNIaln7jJW7Tj+8sH/d3J7qrEgPczVRFqAkn7yBY1BySeu05TtO3nr95///\
11685b0+vHN60cEJ9XqyPtZYkA5r/v399cvnQxgX9dbkx3laaEuwoll96/AVFMwfYen4FpEC6cXzb0inNRYkB9nqyPJjOu4MUdLoy3EBZLmkdW7/4gsZJi7ccvfb\
116868x/8fz68d3bJ4UmNBvJ+tjjSXlLaNb1x+w8RFm49cffYdHLwoIahu4RmVXdM7b/2Bi48+Q4L//b2ze1bP6qxID3M1UUZ1PVAW3X9o/p/eDIl3TgZkAHVk65\
11687xNt46sm9FcnBTooC/HC5Jh5peGu/DEvfdfPr97+QRug4+jaUxGCSTyX71+/eb1+y8/voPBJ0g81RZm1EDi/dt/ZAB1ZGWyr5pNUAay9/5+fApPnhZKAtw8g\
11688mLS8BDcvP/04undVZkR7maqIsJAIMDFzgEGvHL6oHTY1DetCdV/ADmMewY="
11689
11690_palette_amiga7800mess :
11691  base642img \
11692"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM3MTQKeJxj1LONLG9ZsP3sjbsPXn349v//PyAEgxu7Z5aHOLoXLdh84smf/2jg7+\
11693fnV/ZvmFsV712/+cT9t7//QDVBNP9+/+T6qd2rpjame3nGpFZNmrts454j567euvfw2dkDG+bPmNBQmBrl76LKycbKo2DmFp6YW905ce7yTbsPX75weP3sy\
11694b0FYb6exrKycrKyTEyMTEDMKG2XWtY1e+PxCye2LWqrLQ7yM9eQ5GdhYBCTUdbQs3IPjC1s7Z2+dMfOTy9unzu0Y/W87sqcBA9XSx03n9DcCWsOXbz//N33\
11695vyD3AcGfn28fXti/dXVvYYQjI7r////88uzGhaOrJzZk+JtrKEjKqVoGZjX1zFx35Oixa0+e37p4aNOSGV2VeUl+ZvraKvLiUrKKqjZBCQX1E+at2nni1I7\
116961S+f21hWlR/na6kowMzIys/ApW7v6R6XmljVPmLNq2+XT+zatWjCpuTQjysfWQE2OQ8bQKTg2s7y1d8aS9TsOXnz78sn9OzeObVs5q60wxtNC0ye2oGXm6j\
116973n7zx8/urth88gB/5/8/jm2V1LJteXZod4OASnVfTMWLp+9/nHr7+CJP+8f3jtzP5N83vrC8OCXEyt7N0iynoWbth37OytZ59+/rxy+tjB7WsXTG4tTwm0U\
11698pWXQvf/mpkVYYG+NsCwF5cQYGNkYBGUU1RSkFfVtQiJzO1YtPrikc3zumszQ73ttVX4BXj5WHjFJWUsQxOT6/rnr9137t6bVy+ePbp17jDQA50lCd7muR0T\
11699l+07eenuiw8/QYGPlITe3rt6amlzZe/izUfO33rx/R807YAiCMh6c/P88TWTy2Oi8lvmbDt19ws0Xf6Dxb+vuTo4/hkYGBkZeCSV9Z3D4zOnTZ++ujInJNj\
11700TRl+GixEkycDJxcnJwa2gb+vqm1q75OiVGc2FCTGRfi5WBurywhyszABUd6ys"
11701
11702_palette_fornaxvoid1 :
11703  base642img \
11704"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1MjkKeJyrKi3ISU+OiwwN8HF3srM01Te3d/MLi03NLalp6Z48exEDO6+IlIK6ro\
11705mVg7tvSFTChwcXDmxYMKGhICHAwUBBgOH/p2e3zh3aunLuxNaKnIQQj8/Pb184snPtwmld9cXpMQGu/6HgwYEFDQlAHTeObJjbWZrkZ6UuzAAC/9/dPbN71\
11706cyO8rRQF2MlQQYGVk5eITEpOWV1bX0TCxsHRkYGFIChXkBez9YnOquqc8bybUevPP7/6enNM0AXNoDcp6EgsGzT3hOX77388v/Wtom5HioMjy8f2bpsekdl\
11707ZpS3ja4cP4Oqlr6xhY2jm5d/cERsYlrWvWvnju3btm7ZvGl9bXVleWlnDu/auGLB9L7WmpLspMgAjw8vH925eu7EwV1b1i5bMHNSD7r/+zqaasoKslLiIoJ\
1170883BysTG8cWjMN6HG4+9H9r62hpqKkICcjKS4qLMjPy43u//8fHl46vGXptI7KrGgfWz2FBXNnz5w+dfLEvp6ujraWpgYGTiFpFV0ze4/AqOSc0rq2A2tmth\
11709UnGDDgAuj+/48GXn/6icI/c+Hy9Vt3Hz558frdp68///zH8D+RABb//79/ePHw5qVTh3dvWTuxLNrh/7d3T+9eOXN496ZVC2dO6GhgEFQydglNK++YuWr3m\
11710bvv/qP7n6B6KHhx4QA4Cfy/v39+fby9PAOXsIyqnjlGeAIAmOWLbw=="
11711
11712# Command that tries to compress a smooth-enough palette.
11713compress_palette :
11714  N={w}
11715  do
11716    N-=1
11717    +r. $N,1,1,3,2 round.
11718    ri. ..,3 round.
11719    deltaE. ..
11720    dEavg,dEmax={[ia,iM]} rm.
11721    e[] "N = "$N", dEavg = "$dEavg", dEmax = "$dEmax
11722  while $dEavg<0.75" && "$dEmax<1.25
11723  N+=1
11724  r $N,1,1,3,2 round.
11725  e[] ""
11726  img2base64 0,0 rm
11727  str=\"${}\"
11728  ('$str') s. x,-119 ('\\\n') if $!>2 a[0--3] .,x fi rm.
11729  i[0] ('"  base642img \\\n"')
11730  ('"\n  r. 256,1,1,3,3 round.\n"')
11731  a x
11732  ot pal.txt
11733
11734#@cli pseudogray : _max_increment>=0,_JND_threshold>=0,_bits_depth>0
11735#@cli : Generate pseudogray colormap with specified increment and perceptual threshold.
11736#@cli : If 'JND_threshold' is 0, no perceptual constraints are applied.
11737#@cli : Default values: 'max_increment=5', 'JND_threshold=2.3' and 'bits_depth=8'.
11738#@cli : $ pseudogray 5
11739pseudogray : check "isint(${1=5}) && $1>=0 && ${2=2.3}>=0 && isint(${3=8}) && $3>0"
11740  e[^-1] "Generate pseudogray colormap with increment $1, JND threshold $2 and $3 bits depth."
11741
11742  # Generate all possible sRGB colors with given increments.
11743  {round(2^$3)},1,1,3,'x'
11744  if !$1 n. 0,255 return fi
11745  {$1+1},{$1+1},{$1+1},1,'x' +f. 'y' +f. 'z' a[-3--1] c r. {w*h*d},1,1,3,-1
11746  f. 'R=i(x,0,0,0);G=i(x,0,0,1);B=i(x,0,0,2);if(min(R,G,B),-1,i)'
11747  permute. cxyz discard. -1 r. 3,{h/3},1,1,-1 permute. yzcx
11748  r.. {w*100}% ri. ..,0,2 +[-2,-1]
11749  f. 'R=i(x,0,0,0);G=i(x,0,0,1);B=i(x,0,0,2);if(max(R,G,B)>2^$3-1,-1,i)'
11750  permute. cxyz discard. -1 r. 3,{h/3},1,1,-1 permute. yzcx
11751  n. 0,255
11752  +srgb2lab. rv[-2,-1] a[-2,-1] y sort. +,x  # Sort by increasing lightness.
11753  if !$2 rows. 1
11754  else # Add perceptual constraint if requested.
11755
11756    # Constraint 1 : keep colors close enough to equivalent 'pure' grays.
11757    s. y rv[-2,-1] . sh. 1,2 f. 0 rm. -[-2,-1] norm.
11758    <=. $2 *. 'x+1' discard. 0 -. 1 map. .. rm..
11759
11760    # Constraint 2 : remove neighboring colors that are above the JND.
11761    repeat 10000
11762      +srgb2lab. +shift. 0,{1-2*($>%2)},0,0,1 -[-2,-1] norm.
11763      <=. $2
11764      if im rm. break fi
11765      *. 'y+1' discard. 0 -. 1 map. .. rm..
11766    done
11767    transpose.
11768  fi
11769  nm. pseudogray$1
11770
11771#@cli replace_color : tolerance[%]>=0,smoothness[%]>=0,src1,src2,...,dest1,dest2,...
11772#@cli : Replace pixels from/to specified colors in selected images.
11773#@cli : $ image.jpg +replace_color 40,3,204,153,110,255,0,0
11774replace_color : check "$1>=0 && $2>=0"
11775  l[] (${3--1}) y c s c,2 col1={0,^} col2={1,^} rm endl
11776  e[^-1] "Replace color ("$col1") by color ("$col2") in image$?, with tolerance $1 and smoothness $2."
11777  repeat $! l[$>]
11778    1,1,1,100%,$col1 ri[1] [0]
11779    if $1 -[1] [0] norm[1] <=[1] $1 else ==[1] [0] l[1] s c & endl fi
11780    b[1] $2
11781    1,1,1,{0,s},$col2 ri[2] [0] j[0] [2],0,0,0,0,1,[1] k[0]
11782  endl done
11783
11784#@cli retinex : _value_offset>0,_colorspace={ hsi | hsv | lab | lrgb | rgb | ycbcr },\
11785# 0<=_min_cut<=100,0<=_max_cut<=100,_sigma_low>0,_sigma_mid>0,_sigma_high>0
11786#@cli : Apply multi-scale retinex algorithm on selected images to improve color consistency.
11787#@cli : (as described in the page <http://www.ipol.im/pub/art/2014/107/>).
11788#@cli : Default values: 'offset=1', 'colorspace=hsv', 'min_cut=1', 'max_cut=1', 'sigma_low=15','sigma_mid=80' \
11789# and 'sigma_high=250'.
11790retinex : check "${1=5}>0 && ${3=1}>=0 && $3<=100 && ${4=1}>=0 && $4<=100 && ${5=15}>0 && ${6=80}>0 && ${7=250}>0"
11791  skip "${2=hsv}"
11792  e[^-1] "Apply Retinex color consistency algorithm on image$?, with value offset $1, colorspace '$2', cuts ($3,$4)
11793          and sigmas (${5-7})."
11794  if '$2'=='hsi' mode=hsi_i
11795  elif '$2'=='hsv' mode=hsv_v
11796  elif '$2'=='lab' mode=lab_l
11797  elif '$2'=='rgb' mode=rgb
11798  elif '$2'=='lrgb' mode=lrgb
11799  elif '$2'=='ycbcr' mode=ycbcr_y
11800  else error[0--2] "Command '$0': Invalid colorspace argument '$2'."
11801  fi
11802  ac "_retinex $1,${3--1}",$mode
11803
11804_retinex :
11805  - {im-$1} {[w,h,d,s]},1
11806  repeat 3 +b[0] {arg(1+$>,${4-6})} +/[0,-1] rm.. *[1,-1] done
11807  rm[0] log c $2%,{100-$3}% n 0,255
11808
11809#@cli rgb2bayer : _start_pattern=0,_color_grid=0
11810#@cli : Transform selected color images to RGB-Bayer sampled images.
11811#@cli : Default values: 'start_pattern=0' and 'color_grid=0'.
11812#@cli : $ image.jpg +rgb2bayer 0
11813rgb2bayer : skip ${1=0},${2=0}
11814  e[^-1] "Transform image$? to a RGB-Bayer "${arg\ 1+!$2,color,monochrome}" grid, starting from pattern '$1'."
11815  to_rgb repeat $! l[$>]
11816    _rgb2bayer$1 ri[1] [0],0,2 * if !$2 s c + fi
11817  endl done
11818
11819_rgb2bayer0 : (1,0;0,0^0,1;1,0^0,0;0,1)
11820_rgb2bayer1 : (0,0;0,1^0,1;1,0^1,0;0,0)
11821_rgb2bayer2 : (0,1;0,0^1,0;0,1^0,0;1,0)
11822_rgb2bayer3 : (0,0;1,0^1,0;0,1^0,1;0,0)
11823
11824#@cli rgb2cmy
11825#@cli : Convert color representation of selected images from RGB to CMY.
11826#@cli : $ image.jpg rgb2cmy split c
11827rgb2cmy :
11828  e[^-1] "Convert color representation of image$? from RGB to CMY."
11829  to_rgb * -1 + 255 c 0,255
11830
11831#@cli rgb2cmyk
11832#@cli : Convert color representation of selected images from RGB to CMYK.
11833#@cli : $ image.jpg rgb2cmyk split c
11834#@cli : $ image.jpg rgb2cmyk split c fill[3] 0 append c cmyk2rgb
11835rgb2cmyk :
11836  e[^-1] "Convert color representation of image$? from RGB to CMYK."
11837  rgb2cmy repeat $! l[$>]
11838    s c +min -[0-2] . +/. 255 -. 1 *. -1 +==. 0 +[-2,-1]
11839    /[0-2] . rm. a c
11840  endl done
11841
11842#@cli rgb2hcy
11843#@cli : Convert color representation of selected images from RGB to HCY.
11844#@cli : $ image.jpg rgb2hcy split c
11845rgb2hcy :
11846  e[^-1] "Convert color representation of image$? from RGB to HCY."
11847  to_color f "
11848    M = max(R,G,B);
11849    C = M - min(R,G,B);
11850    H = 60*(C==0?0:M==R?((G-B)/C)%6:M==G?(B-R)/C+2:(R-G)/C+4);
11851    Y = 0.299*R + 0.587*G + 0.114*B;
11852    [ H,C/255,Y/255 ]"
11853
11854#@cli rgb2hsi
11855#@cli : Convert color representation of selected images from RGB to HSI.
11856#@cli : $ image.jpg rgb2hsi split c
11857rgb2hsi :
11858  e[^-1] "Convert color representation of image$? from RGB to HSI."
11859  to_color
11860  f "
11861    m = min(R,G,B);
11862    M = max(R,G,B);
11863    C = M - m;
11864    sum = R + G + B;
11865    H = 60*(C==0?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4);
11866    S = sum<=0?0:1 - 3*m/sum;
11867    I = sum/(3*255);
11868    [ H, S, I ]"
11869
11870#@cli rgb2hsi8
11871#@cli : Convert color representation of selected images from RGB to HSI8.
11872#@cli : $ image.jpg rgb2hsi8 split c
11873rgb2hsi8 :
11874  e[^-1] "Convert color representation of image$? from RGB to HSI8."
11875  rgb2hsi _rgb2hsx8
11876
11877#@cli rgb2hsl
11878#@cli : Convert color representation of selected images from RGB to HSL.
11879#@cli : $ image.jpg rgb2hsl split c
11880#@cli : $ image.jpg rgb2hsl +split c add[-3] 100 mod[-3] 360 append[-3--1] c hsl2rgb
11881rgb2hsl :
11882  e[^-1] "Convert color representation of image$? from RGB to HSL."
11883  to_color
11884  f "
11885    m = min(R,G,B);
11886    M = max(R,G,B);
11887    C = M - m;
11888    H = 60*(C==0?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4);
11889    L = 0.5*(m + M)/255;
11890    S = L==1 || L==0?0:C/(1 - abs(2*L - 1))/255;
11891    [ H, S, L ]"
11892
11893#@cli rgb2hsl8
11894#@cli : Convert color representation of selected images from RGB to HSL8.
11895#@cli : $ image.jpg rgb2hsl8 split c
11896rgb2hsl8 :
11897  e[^-1] "Convert color representation of image$? from RGB to HSL8."
11898  rgb2hsl _rgb2hsx8
11899
11900#@cli rgb2hsv
11901#@cli : Convert color representation of selected images from RGB to HSV.
11902#@cli : $ image.jpg rgb2hsv split c
11903#@cli : $ image.jpg rgb2hsv +split c add[-2] 0.3 cut[-2] 0,1 append[-3--1] c hsv2rgb
11904rgb2hsv :
11905  e[^-1] "Convert color representation of image$? from RGB to HSV."
11906  to_color
11907  f "
11908    M = max(R,G,B);
11909    C = M - min(R,G,B);
11910    H = 60*(C==0?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4);
11911    S = M<=0?0:C/M;
11912    [ H, S, M/255 ]"
11913
11914#@cli rgb2hsv8
11915#@cli : Convert color representation of selected images from RGB to HSV8.
11916#@cli : $ image.jpg rgb2hsv8 split c
11917rgb2hsv8 :
11918  e[^-1] "Convert color representation of image$? from RGB to HSV8."
11919  rgb2hsv _rgb2hsx8
11920
11921_rgb2hsx8 :
11922  repeat $!
11923    sh[$>] 0 *. 0.708333 rm.
11924    sh[$>] 1,2 *. 255 rm.
11925  done
11926
11927#@cli rgb2int
11928#@cli : Convert color representation of selected images from RGB to INT24 scalars.
11929#@cli : $ image.jpg rgb2int
11930rgb2int :
11931  e[^-1] "Convert color representation of image$? from RGB to INT24 scalars."
11932  to_rgb round repeat $! l[$>]
11933    s c <<[0] 16 <<[1] 8 +
11934  endl done
11935
11936#@cli rgb2jzazbz : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11937#@cli : Convert color representation of selected images from RGB to Jzazbz.
11938#@cli : Default value: 'illuminant=2'.
11939rgb2jzazbz : skip "${1=,}"
11940  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11941  e[^-1] "Convert color representation of image$? from RGB to Jzazbz, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11942  rgb2xyz $illu xyz2jzazbz
11943
11944#@cli rgb2lab : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11945#@cli : Convert color representation of selected images from RGB to Lab.
11946#@cli : Default value: 'illuminant=2'.
11947rgb2lab : skip "${1=,}"
11948  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11949  e[^-1] "Convert color representation of image$? from RGB to Lab, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11950  rgb2xyz $illu xyz2lab $illu
11951
11952#@cli rgb2lab8 : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11953#@cli : Convert color representation of selected images from RGB to Lab8.
11954#@cli : Default value: 'illuminant=2'.
11955#@cli : $ image.jpg rgb2lab8 split c
11956rgb2lab8 : skip "${1=,}"
11957  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11958  e[^-1] "Convert color representation of image$? from RGB to Lab8, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11959  rgb2lab $illu repeat $!
11960    sh[$>] 0 *. 2.55 rm.
11961    sh[$>] 1 +. 127 *. 0.85 rm.
11962    sh[$>] 2 +. 149 *. 0.836 rm.
11963  done c 0,255
11964
11965#@cli rgb2lch : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11966#@cli : Convert color representation of selected images from RGB to Lch.
11967#@cli : Default value: 'illuminant=2'.
11968#@cli : $ image.jpg rgb2lch split c
11969rgb2lch : skip "${1=,}"
11970  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11971  e[^-1] "Convert color representation of image$? from RGB to Lch, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11972  rgb2lab $illu lab2lch
11973
11974#@cli rgb2lch8 : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11975#@cli : Convert color representation of selected images from RGB to Lch8.
11976#@cli : Default value: 'illuminant=2'.
11977#@cli : $ image.jpg rgb2lch8 split c
11978rgb2lch8 : skip "${1=,}"
11979  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
11980  e[^-1] "Convert color representation of image$? from RGB to Lch8, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11981  pi,facth={[pi,255/(2*pi)]}
11982  rgb2lch $illu repeat $!
11983    sh[$>] 0 *. 2.55 rm.
11984    sh[$>] 1 *. 1.1086 rm.
11985    sh[$>] 2 +. $pi *. $facth rm.
11986  done c 0,255
11987
11988#@cli rgb2luv
11989#@cli : Convert color representation of selected images from RGB to LUV.
11990#@cli : $ image.jpg rgb2luv split c
11991rgb2luv :
11992  e[^-1] "Convert color representation of image$? from RGB to LUV."
11993  repeat $! l[$>]
11994    +rgb2xyz rgb2lab.. channels.. 0 s. c
11995    *. 3 +*.. 15 +[-2,-1] +. ... +. 1e-8  # Z <- X+15Y+3Z
11996    *... 4 *.. 9 /[-3,-2] . rm.
11997    -.. 0.2009 -. 0.4610
11998    +*... 13 *... . *[-2,-1] a c
11999  endl done
12000
12001#@cli rgb2oklab
12002#@cli : Convert color representation of selected images from RGB to Oklab.
12003#@cli : (see colorspace definition at: <https://bottosson.github.io/posts/oklab/> ).
12004#@cli : See also: ''oklab2rgb''.
12005rgb2oklab :
12006  e[^-1] "Convert color representation of image$? from RGB to Oklab."
12007  repeat $! l[$>] split_opacity to_rgb[0] /[0] 255
12008    f[0] "
12009      l = cbrt(0.4121656120*R + 0.5362752080*G + 0.0514575653*B);
12010      m = cbrt(0.2118591070*R + 0.6807189584*G + 0.1074065790*B);
12011      s = cbrt(0.0883097947*R + 0.2818474174*G + 0.6302613616*B);
12012      [ 0.2104542553*l + 0.7936177850*m - 0.0040720468*s,
12013        1.9779984951*l - 2.4285922050*m + 0.4505937099*s,
12014        0.0259040371*l + 0.7827717662*m - 0.8086757660*s ]"
12015  a c endl done
12016
12017#@cli rgb2ryb
12018#@cli : Convert color representation of selected images from RGB to RYB.
12019#@cli : $ image.jpg rgb2ryb split c
12020rgb2ryb :
12021  e[^-1] "Convert color representation of image$? from RGB to RYB."
12022  to_color
12023  f "red = R;
12024     green = G;
12025     blue = B;
12026     white = min(red,green,blue);
12027     red-=white;
12028     green-=white;
12029     blue-=white;
12030     maxgreen = max(red,green,blue);
12031     yellow = min(red,green);
12032     red-=yellow;
12033     green-=yellow;
12034     blue>0 && green>0?(blue/=2; green/=2);
12035     yellow+=green;
12036     blue+=green;
12037     maxyellow = max(red,yellow,blue);
12038     maxyellow>0?(
12039       N = maxgreen/maxyellow;
12040       red*=N;
12041       yellow*=N;
12042       blue*=N;
12043     );
12044     red+=white;
12045     yellow+=white;
12046     blue+=white;
12047     [ red,yellow,blue ]"
12048
12049#@cli rgb2srgb
12050#@cli : Convert color representation of selected images from linear RGB to sRGB.
12051rgb2srgb :
12052  e[^-1] "Convert color representation of image$? from linear RGB to sRGB."
12053  f "val = i/255; sval = val<=0.0031308?val*12.92:1.055*val^0.416667 - 0.055; cut(255*sval,0,255)"
12054
12055#@cli rgb2xyz : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12056#@cli : Convert color representation of selected images from RGB to XYZ.
12057#@cli : Default value: 'illuminant=2'.
12058#@cli : $ image.jpg rgb2xyz split c
12059rgb2xyz : skip "${1=,}"
12060  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
12061  e[^-1] "Convert color representation of image$? from RGB to XYZ, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12062  if $illu==2 # E
12063     mix_rgb {[0.488718,0.3106803,0.2006017,\
12064               0.1762044,0.8129847,0.0108109,\
12065               0,0.0102048,0.9897952]/255}
12066  elif $illu==1 # D65
12067    mix_rgb {[0.4124564,0.3575761,0.1804375,\
12068              0.2126729,0.7151522,0.0721750,\
12069              0.0193339,0.1191920,0.9503041]/255}
12070  else # D50
12071    mix_rgb {[0.43603516,0.38511658,0.14305115,\
12072              0.22248840,0.71690369,0.06060791,\
12073              0.01391602,0.09706116,0.71392822]/255}
12074  fi
12075
12076#@cli rgb2xyz8 : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12077#@cli : Convert color representation of selected images from RGB to XYZ8.
12078#@cli : Default value: 'illuminant=2'.
12079#@cli : $ image.jpg rgb2xyz8 split c
12080rgb2xyz8 : skip "${1=,}"
12081  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
12082  e[^-1] "Convert color representation of image$? from RGB to XYZ8, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12083  rgb2xyz $illu repeat $!
12084    sh[$>] 0 *. 255 rm.
12085    sh[$>] 1 *. 255 rm.
12086    sh[$>] 2 *. 231.8182 rm.
12087  done
12088
12089#@cli rgb2yiq
12090#@cli : Convert color representation of selected images from RGB to YIQ.
12091#@cli : $ image.jpg rgb2yiq split c
12092rgb2yiq :
12093  e[^-1] "Convert color representation of image$? from RGB to YIQ."
12094  mix_rgb 0.299,0.587,0.114,\
12095          0.595716,-0.274453,-0.321263,\
12096          0.211456,-0.522591,0.311135
12097
12098#@cli rgb2yiq8
12099#@cli : Convert color representation of selected images from RGB to YIQ8.
12100#@cli : $ image.jpg rgb2yiq8 split c
12101rgb2yiq8 :
12102  e[^-1] "Convert color representation of image$? from RGB to YIQ."
12103  rgb2yiq
12104  repeat $!
12105    sh[$>] 1 +. 151.908 *. 0.8393238012481239 rm.
12106    sh[$>] 2 +. 133.261 *. 0.9567690472081104 rm.
12107  done
12108
12109#@cli rgb2ycbcr
12110#@cli : Convert color representation of selected images from RGB to YCbCr.
12111#@cli : $ image.jpg rgb2ycbcr split c
12112rgb2ycbcr :
12113  e[^-1] "Convert color representation of image$? from RGB to YCbCr."
12114  mix_rgb 66,129,25,-38,-74,112,112,-94,-18
12115  repeat $!
12116    sh[$>] 0,2 +. 128 /. 256 rm.
12117    sh[$>] 0 +. 16 rm.
12118    sh[$>] 1,2 +. 128 rm.
12119  done
12120
12121#@cli rgb2yuv
12122#@cli : Convert color representation of selected images from RGB to YUV.
12123#@cli : $ image.jpg rgb2yuv split c
12124rgb2yuv :
12125  e[^-1] "Convert color representation of image$? from RGB to YUV."
12126  mix_rgb {[0.299,0.587,0.114,\
12127            -0.14713,-0.28886,0.436,\
12128            0.615,-0.51498,-0.10001]/255}
12129
12130#@cli rgb2yuv8
12131#@cli : Convert color representation of selected images from RGB to YUV8.
12132#@cli : $ image.jpg rgb2yuv8 split c
12133rgb2yuv8 :
12134  e[^-1] "Convert color representation of image$? from RGB to YUV8."
12135  rgb2yuv repeat $!
12136    sh[$>] 0 *. 255 rm.
12137    sh[$>] 1 +. 0.44 *. 289.773 rm.
12138    sh[$>] 2 +. 0.62 *. 205.645 rm.
12139  done
12140
12141#@cli remove_opacity
12142#@cli : Remove opacity channel of selected images.
12143remove_opacity :
12144  e[^-1] "Remove opacity channel of image$?."
12145  repeat $! l[$>]
12146    if s==2 channels 0
12147    elif s==4 channels 0,2
12148    fi
12149  nm {n} endl done
12150
12151#@cli ryb2rgb
12152#@cli : Convert color representation of selected images from RYB to RGB.
12153ryb2rgb :
12154  e[^-1] "Convert color representation of image$? from RYB to RGB."
12155  to_color
12156  f "red = R;
12157     yellow = G;
12158     blue = B;
12159     white = min(red,yellow,blue);
12160     red-=white;
12161     yellow-=white;
12162     blue-=white;
12163     maxyellow = max(red,yellow,blue);
12164     green = min(yellow,blue);
12165     yellow-=green;
12166     blue-=green;
12167     blue>0 && green>0?(blue*=2; green*=2);
12168     red+=yellow;
12169     green+=yellow;
12170     maxgreen = max(red,green,blue);
12171     maxgreen>0?(
12172       N = maxyellow/maxgreen;
12173       red*=N;
12174       green*=N;
12175       blue*=N;
12176     );
12177     red+=white;
12178     green+=white;
12179     blue+=white;
12180     [ red,green,blue ]"
12181
12182#@cli select_color : tolerance[%]>=0,col1,...,colN
12183#@cli : Select pixels with specified color in selected images.
12184#@cli : $ image.jpg +select_color 40,204,153,110
12185#@cli : $$ https://gmic.eu/oldtutorial/_select_color
12186select_color : skip ${1=0}
12187  e[^-1] "Select color (${2--1}) in image$?, with tolerance $1."
12188  repeat $! l[$>]
12189    +fc ${2--1} - norm <= $1
12190  endl done
12191
12192#@cli sepia
12193#@cli : Apply sepia tones effect on selected images.
12194#@cli : $ image.jpg sepia
12195sepia :
12196  e[^-1] "Apply sepia tones effect on image$?."
12197  (0,44,115,143,196,244^0,20,84,119,184,235^0,5,44,73,144,200) r. 256,1,1,3,3
12198  repeat $!-1 l[$>,-1] split_opacity luminance[0] map[0] . a[^-1] c endl done
12199  rm.
12200
12201#@cli solarize
12202#@cli : Solarize selected images.
12203#@cli : $ image.jpg solarize
12204solarize :
12205  e[^-1] "Solarize image$?."
12206  luminance n 0,128 map 1
12207
12208#@cli split_colors : _tolerance>=0,_max_nb_outputs>0,_min_area>0
12209#@cli : Split selected images as several image containing a single color.
12210#@cli : One selected image can be split as at most 'max_nb_outputs' images.
12211#@cli : Output images are sorted by decreasing area of extracted color regions and have an additional alpha-channel.
12212#@cli : Default values: 'tolerance=0', 'max_nb_outputs=256' and 'min_area=8'.
12213#@cli : $ image.jpg quantize 5 +split_colors , display_rgba
12214split_colors : check "${1=0}>=0 && isint(${2=256}) && $2>0 && ${3=8}>=1"
12215  e[^-1] "Split image$? as single color outputs, with tolerance $1, $2 maximal outputs and minimal color area $3."
12216  repeat $! l[$>]
12217    +label 0,1 norm. area. 0,1
12218
12219    # Loop over biggest color regions.
12220    repeat $2-1
12221      coordsM={1,[xM,yM,zM,cM]}
12222      if {1,i($coordsM)<$3} break fi
12223      color={0,I($coordsM)}
12224      +select_color[0] $1,$color
12225      ==. 0 *[1] . ==. 0
12226    done
12227    if iM#1 !=[1] 0 mv[1] $! # Residual mask, if any.
12228    else rm[1] fi
12229    r[^0] [0],[0],[0],{0,s+1}
12230    N={$!-1}
12231    sh[^0] 0,{s-2} *[-$N--1] [0] rm[-$N--1]
12232    sh[^0] 100% *[-$N--1] 255 rm[0,-$N--1]
12233
12234  endl done
12235
12236#@cli split_opacity
12237#@cli : Split color and opacity parts of selected images.
12238split_opacity :
12239  e[^-1] "Split color and opacity parts of image$?."
12240  repeat $! l[$<] s c,{if(s==4,-3,if(s==2,-1,-s))} endl done
12241
12242#@cli srgb2lab : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12243#@cli : Convert color representation of selected images from sRGB to Lab.
12244#@cli : Default value: 'illuminant=2'.
12245#@cli : $ image.jpg srgb2lab split c
12246#@cli : $ image.jpg srgb2lab +split c mul[-2,-1] 2.5 append[-3--1] c lab2srgb
12247srgb2lab : skip "${1=,}"
12248  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
12249  e[^-1] "Convert color representation of image$? from sRGB to Lab, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12250  srgb2rgb rgb2lab $illu
12251
12252#@cli srgb2lab8 : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12253#@cli : Convert color representation of selected images from sRGB to Lab8.
12254#@cli : Default value: 'illuminant=2'.
12255srgb2lab8 : skip "${1=,}"
12256  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
12257  e[^-1] "Convert color representation of image$? from sRGB to Lab8, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12258  srgb2rgb rgb2lab8 $illu
12259
12260#@cli srgb2rgb
12261#@cli : Convert color representation of selected images from sRGB to linear RGB.
12262srgb2rgb :
12263  e[^-1] "Convert color representation of image$? from sRGB to linear RGB."
12264  f "sval = i/255; val = sval<=0.04045?sval/12.92:((sval + 0.055)/(1.055))^2.4; cut(255*val,0,255)"
12265
12266#@cli to_a
12267#@cli : Force selected images to have an alpha channel.
12268to_a :
12269  e[^-1] "Force image$? to have an alpha channel."
12270  repeat $! l[$>]
12271    if s==1||s==3 channels 0,{s} sh. {s-1} f. 255 rm. fi
12272  endl done
12273
12274#@cli to_color
12275#@cli : Force selected images to be in color mode (RGB or RGBA).
12276to_color :
12277  e[^-1] "Force image$? to be in color mode."
12278  repeat $! l[$>]
12279    if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
12280    elif s==2 r 100%,100%,1,4,0,1,0,0,0,1
12281    elif s==1 r 100%,100%,1,3,1
12282    fi
12283  endl done
12284
12285#@cli to_colormode : mode={ 0=adaptive | 1=G | 2=GA | 3=RGB | 4=RGBA }
12286#@cli : Force selected images to be in a given color mode.
12287#@cli : Default value: 'mode=0'.
12288to_colormode : skip ${1=0}
12289  if $1==1 to_gray
12290  elif $1==2 to_graya
12291  elif $1==3 to_rgb
12292  elif $1==4 to_rgba
12293  else
12294    is_rgb=0 is_alpha=0
12295    repeat $!
12296      if {$>,!s||s>4} error "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
12297      elif {$>,s==2} is_alpha=1
12298      elif {$>,s==3} is_rgb=1
12299      elif {$>,s==4} is_rgb=1 is_alpha=1
12300      fi
12301    done
12302    to_colormode {if($is_rgb,3,1)+$is_alpha}
12303  fi
12304
12305#@cli to_gray
12306#@cli : Force selected images to be in GRAY mode.
12307#@cli : $ image.jpg +to_gray
12308to_gray :
12309  e[^-1] "Force image$? to be in GRAY mode."
12310  repeat $! l[$>]
12311    if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
12312    elif s>=3 channels 0,2 luminance
12313    elif s==2 r 100%,100%,100%,1,0
12314    fi
12315  endl done
12316
12317#@cli to_graya
12318#@cli : Force selected images to be in GRAYA mode.
12319to_graya :
12320  e[^-1] "Force image$? to be in GRAYA mode."
12321  repeat $! l[$>]
12322    if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
12323    elif s==4 +channels 3 channels.. 0,2 luminance.. a c
12324    elif s==3 luminance channels 0,1 sh. 1 f. 255 rm.
12325    elif s==1 channels 0,1 sh. 1 f. 255 rm.
12326    fi
12327  endl done
12328
12329#@cli to_pseudogray : _max_step>=0,_is_perceptual_constraint={ 0 | 1 },_bits_depth>0
12330#@cli : Convert selected scalar images ([0-255]-valued) to pseudo-gray color images.
12331#@cli : Default values: 'max_step=5', 'is_perceptual_constraint=1' and 'bits_depth=8'.
12332#@cli : The original pseudo-gray technique has been introduced by Rich Franzen <http://r0k.us/graphics/pseudoGrey.html>.
12333#@cli : Extension of this technique to arbitrary increments for more tones, has been done by David Tschumperlé.
12334to_pseudogray : check "isint(${1=5}) && $1>=0 && isint(${3=8}) && $3>0" skip ${2=1}
12335  e[^-1] "Convert scalar image$? to pseudo-gray color images, with steps $1."
12336  channels 0 srgb2rgb pseudogray $1,{2.3*$2},$3
12337
12338  # Compute colormap with 65336 entries, to have match corresponding lightness.
12339  +srgb2lab. channels. 0 *. {65535/100} round. rows. 0,2
12340  rv[-2,-1] permute. xcyz +. 1 a[-2,-1] y pointcloud. 0
12341  +norm. !=. 0 distance. 1 *. -1 watershed.. . rm. -. 1
12342
12343  # Map colormap to images, with lightness preservation.
12344  repeat $!-1
12345    to_rgb[$>] rgb2lab[$>] channels[$>] 0 *[$>] {65535/100} round[$>] c[$>] 0,65535
12346    map[$>] .
12347  done rm.
12348
12349#@cli to_rgb
12350#@cli : Force selected images to be in RGB mode.
12351to_rgb :
12352   e[^-1] "Force image$? to be in RGB mode."
12353   repeat $! l[$>]
12354     if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
12355     elif s==4 channels 0,2
12356     elif s==2 channels 0,0 r 100%,100%,100%,3
12357     elif s==1 r 100%,100%,100%,3
12358     fi
12359   endl done
12360
12361#@cli to_rgba
12362#@cli : Force selected images to be in RGBA mode.
12363to_rgba :
12364   e[^-1] "Force image$? to be in RGBA mode."
12365   repeat $! l[$>]
12366     if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
12367     elif s==3 channels 0,3 sh. 3 f. 255 rm.
12368     elif s==2 r 100%,100%,100%,4 sh. 2 f. .. rm.
12369     elif s==1 r 100%,100%,100%,4 sh. 3 f. 255 rm.
12370     fi
12371   endl done
12372
12373#@cli transfer_histogram : [reference_image],_nb_levels>0,_color_channels
12374#@cli : Transfer histogram of the specified reference image to selected images.
12375#@cli : Argument 'color channels' is the same as with command 'apply_channels'.
12376#@cli : Default value: 'nb_levels=256' and 'color_channels=all'.
12377#@cli : $ image.jpg 100,100,1,3,"u([256,200,100])" +transfer_histogram[0] [1]
12378transfer_histogram : check ${"is_image_arg $1"}" && ${2=1024}>0" skip "${3=0}"
12379  channels=${"_ac_list \"$3\""}
12380  e[^-1] "Transfer histogram from image ["${"pass$1 -1"}"] to image$?, "\
12381         "with $2 levels, for channels '"$channels"'."
12382  pass$1 1 sref={s} rm.
12383  to_colormode $sref
12384  if ['$channels']!='all'
12385    pass$1 {$sref==3?2:0}
12386    to_color
12387    ac. "+store _transfer_histogram_reference",$channels rm.
12388    ac "_transfer_histogram $2",$channels,1
12389  else
12390    pass$1
12391    store. _transfer_histogram_reference
12392    repeat $! _transfer_histogram[$>] $2 done
12393  fi
12394  _transfer_histogram_reference=
12395
12396_transfer_histogram :
12397  $_transfer_histogram_reference
12398  repeat min(s#0,s#1)
12399    sh $>
12400    +histogram[-2,-1] $1 cumulate[-2,-1] /.. {-2,i[-1,2]} /. {i[-1,2]}
12401    f.. "*
12402      const w1 = w -1;
12403      val = i; X = x;
12404      val<i[#-1,X]?(
12405        step = int(X/2);
12406        while (X && step>=1, nX = max(0,X - step); val<i[#-1,nX]?(X = nX):(step = int(step/2)));
12407        X?(vc = i[#-1,X]; vp = i[#-1,X - 1]; (vc - val)>(val - vp)?--X); # Rounding
12408      ):(
12409        step = int((w1 - X)/2);
12410        while (X<w1 && step>=1, nX = min(w1,X + step); val>i[#-1,nX]?(X = nX):(step = int(step/2)));
12411        X<w1?(vc = i[#-1,X]; vn = i[#-1,X + 1]; (val - vc)>(vn - val)?++X); # Rounding
12412      );
12413      im#-3 + (iM#-3 - im#-3)*X/w1"
12414    n[-4] 0,{w-1} round[-4] map[-4] ..
12415    k[0,1]
12416  done
12417  rm.
12418
12419#@cli transfer_pca : [reference_image],_color_channels
12420#@cli : Transfer mean and covariance matrix of specified vector-valued reference image to selected images.
12421#@cli : Argument 'color channels' is the same as with command 'apply_channels'.
12422#@cli : Default value: 'color_channels=all'.
12423#@cli : $ sample lena,earth +transfer_pca[0] [1]
12424transfer_pca : check ${"is_image_arg $1"} skip "${2=all}"
12425  channels=${"_ac_list \"$2\""}
12426  e[^-1] "Transfer mean vector and covariance matrix from image ["${"pass$1 -1"}"] to image$?, "\
12427         "for channels '"$channels"'."
12428  pass$1 1 sref={s} rm.
12429  to_colormode[^-1] $sref
12430  if $sref==1 # Scalar case can to be solved more easily
12431    pass$1
12432    var_ref,avg_ref={[iv,ia]} rm.
12433    repeat $! l[$>] - {ia} * {sqrt($var_ref/iv)} + $avg_ref endl done
12434  elif ['$channels']!='all'
12435    pass$1 {$sref==3?2:0}
12436    to_color
12437    ac. "+store _transfer_pca_reference",$channels rm.
12438    ac "_transfer_pca",$channels,1
12439  else
12440    pass$1
12441    store. _transfer_pca_reference
12442    repeat $! _transfer_pca[$>] done
12443  fi
12444  _transfer_pca_reference=
12445
12446_transfer_pca :
12447  $_transfer_pca_reference
12448  f.. "*begin(
12449         cov = [ "${"covariance_colors[0] _avg"}" ];
12450         avg = [ "$_avg" ];
12451         eig = eig(cov);
12452         lambda = 1/sqrt(1e-6 + eig[0,s]);
12453         rot = mul(diag(lambda),eig[s,s*s],s);
12454
12455         cov_ref = [ "${"covariance_colors[1] _avg_ref"}" ];
12456         avg_ref = [ "$_avg_ref" ];
12457         eig_ref = eig(cov_ref);
12458         lambda_ref = sqrt(eig_ref[0,s]);
12459         rot_ref = mul(transpose(eig_ref[s,s*s],s),diag(lambda_ref),s);
12460
12461         M = mul(rot_ref,rot,s);
12462       );
12463       avg_ref + M*(I - avg)"
12464  rm.
12465
12466#@cli transfer_rgb : [target],_gamma>=0,_regularization>=0,_luminosity_constraints>=0,_rgb_resolution>=0,\
12467# _is_constraints={ 0 | 1 }
12468#@cli : Transfer colors from selected source images to selected reference image (given as argument).
12469#@cli : 'gamma' determines the importance of color occurrences in the matching process (0=none to 1=huge).
12470#@cli : 'regularization' determines the number of guided filter iterations to remove quantization effects.
12471#@cli : 'luminosity_constraints' tells if luminosity constraints must be applied on non-confident matched colors.
12472#@cli : 'is_constraints' tells if additional hard color constraints must be set (opens an interactive window).
12473#@cli : Default values: 'gamma=0.3','regularization=8', 'luminosity_constraints=0.1', 'rgb_resolution=64' and \
12474# 'is_constraints=0'.
12475#@cli : $ sample pencils,wall +transfer_rgb[0] [1],0,0.01
12476transfer_rgb : check "${2=0.3}>=0 && ${3=8}>=0 && ${4=0.15}>=0 && ${5=64}>=0 && isint(${6=0})"
12477  e[^-1] "Transfer colors of image $1 to image$?."
12478  sigma=1.5
12479  repeat $! pass$1 0 l[$>,-1]
12480    nm_source={0,b} nm_target={1,b}
12481    nm[0] source nm[1] target
12482
12483    # Generate matching functions from 3D RGB features.
12484    +_transfer_rgb[source,target] $2,$sigma,$5
12485    nm.. fsource nm. ftarget
12486    n[fsource,ftarget] 0,255
12487
12488    # Manage color constraints.
12489    if $6
12490      h0={2*{*,v}/3} ws0={source,max(1,w*$h0/h)} wt0={target,max(1,w*$h0/h)}
12491      w1={2*{*,u}/3} hs1={source,max(1,h*$w1/w)} ht1={target,max(1,h*$w1/w)}
12492      if abs($ws0+$wt0-$w1)<abs($hs1+$ht1-$h0) # Append along X.
12493        +r2dy[source,target] $h0 +b[-2,-1] 0.5% a[-4,-3] x a[-2,-1] x
12494      else # Append along Y.
12495        +r2dx[source,target] $w1 +b[-2,-1] 0.5% a[-4,-3] y a[-2,-1] y
12496      fi
12497      if w>$w1 r2dx[-2,-1] $w1 fi
12498      if h>$h0 r2dy[-2,-1] $h0 fi
12499      nm.. visu nm. both
12500
12501      w[visu] -1,-1
12502      N=0 do
12503        w[] -1,-1,"[G'MIC] Add Color Guide (Constraint ""#"{1+$N}")"
12504        +select[$visu] 1 if i==-1 rm. break fi
12505        line[$visu] {i[0]},{i[1]},{i[3]},{i[4]},1,0xF0F0F0F0,0
12506        line[$visu] {i[0]},{i[1]},{i[3]},{i[4]},1,0x0F0F0F0F,255
12507        circle[$visu] {i[0]},{i[1]},5,1,0,0,0 circle[$visu] {i[0]},{i[1]},3,1,255,0,0
12508        circle[$visu] {i[3]},{i[4]},5,1,0,0,0 circle[$visu] {i[3]},{i[4]},3,1,0,255,0
12509        s. y,2 rows[-2,-1] 0,1 a[-2,-1] x permute. xczy
12510        +warp[$both] .,0,0 rm..
12511        *. {($5-1)/255} s. x,2
12512        -. .. *. -1 a[-2,-1] c
12513        N+=1
12514      while {*}
12515      if $N a[-$N--1] x permute. xczy nm. constraints fi
12516      rm[$visu,$both] w 0
12517    fi
12518
12519    # Estimate warping field.
12520    if $constraints
12521      +pointcloud. 0 r. ...,...,...,3,0 +compose_channels. + a[-2,-1] c
12522      displacement[fsource] [ftarget],0.001,5,0,10000,1,. rm[ftarget,constraints,-1]
12523    else
12524      displacement[fsource] [ftarget],0.005 rm[ftarget]
12525    fi
12526    nm[fsource] displacement
12527
12528    # Generate video of matching.
12529    # if videos
12530    #   N=$!
12531    #   [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1
12532    #   +_transfer_rgb[source] 0,$sigma,{w} *.. . distance. 1 *. -1 watershed.. . rm.
12533    #   +_transfer_rgb[source] 0,$sigma,{w} a[-2,-1] c
12534
12535    #   [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1
12536    #   +_transfer_rgb[target] 0,$sigma,{w} *.. . distance. 1 *. -1 watershed.. . rm.
12537    #   +_transfer_rgb[target] 0,$sigma,{w} a[-2,-1] c
12538    #   warp. [displacement],1,1,1,48
12539
12540    #   l[$N--1]
12541    #     f3d 1200
12542    #     ap "s. c,-3 view_func. 6,.. rm.."
12543    #     ap "makefig"
12544    #     append[^0] [0],x rm[0]
12545    #     r2dx 1024
12546    #     [-1]x20
12547    #     o videos/video_${nm_source}_to_${nm_target}.mp4,10
12548    #     rm
12549    #   endl
12550    # fi
12551
12552    # Estimate confidence of the color mapping.
12553    +_transfer_rgb[target] 0,$sigma,{displacement,w}
12554    warp. [displacement],1,1,1 c. 0,100% nm. fconfidence
12555    +map_clut[source] . nm. confidence
12556
12557    # Generate transfer CLUT.
12558    [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1
12559    +_transfer_rgb[target] 0,0,{w} *.. .
12560    warp[-2,-1] [displacement],1,0,1
12561    distance. 1 *. -1 watershed.. . rm. -. 1
12562    nm. clut
12563    b[clut] $sigma%
12564
12565    # Enforce luminosity constraint for non-confident colors.
12566    if $4>0
12567      ^[fconfidence] {$4/10} *[fconfidence] -1 +[fconfidence] 1
12568      +f[fconfidence] x +f. y +f. z a[-3--1] c *. {255/(w-1)}
12569      rgb2hsv[clut,-1] channels. 100%
12570      j[clut] .,0,0,0,2,1,[fconfidence] rm.
12571      hsv2rgb[clut]
12572    fi
12573
12574    # Map CLUT to get result.
12575    +map_clut[source] [clut] nm. res_noregul
12576
12577    # Apply guided smoothing to remove quantization artifacts.
12578    if !$3
12579      nm[res_noregul] res
12580    else
12581      l[source,res_noregul]
12582        rgb2ycbcr  # Perform regularization in YCbCr to avoid creation of false colors.
12583        +-[1] [0] repeat $3 guided. [0],5,5 done +. [0] c. 0,255
12584        ycbcr2rgb
12585      endl
12586      nm. res
12587      j[res] [res_noregul],0,0,0,0,1,[confidence]
12588      rm[res_noregul]
12589    fi
12590
12591    k[res]
12592  endl done
12593
12594# _transfer_rgb : _gamma>=0,_smoothness>=0,_resolution>0
12595# Convert selected images into 3D volumetric scalar function for color matching.
12596_transfer_rgb : l[] check "${1=0}>=0 && ${2=1.5}>=0 && ${3=128}>0" gamma=$1 smoothness=$2 res=$3
12597  onfail noarg gamma=0 smoothness=1.5 res=128 endl
12598  e[^-1] "Convert image$? as 3D volumetric scalar functions for color matching, with gamma "$gamma",
12599          smoothness "$smoothness" and resolution "$res"."
12600  to_rgb repeat $! l[$>]
12601    b 0.3%
12602    r {w*h},3,1,1,-1 * {($res-1)/255}
12603    pointcloud 1,$res,$res,$res f 'if(i,i^$gamma,0)' b $smoothness% n 0,1
12604  endl done
12605
12606#@cli xyz2jzazbz
12607#@cli : Convert color representation of selected images from XYZ to RGB.
12608xyz2jzazbz :
12609  e[^-1] "Convert color representation of image$? from XYZ to Jzazbz."
12610  repeat $! l[$>] split_opacity
12611    f[0] ${-_jzazbz_const}"
12612      X = i0*255;
12613      Y = i1*255;
12614      Z = i2*255;
12615      Xp = Jzazbz_b*X - (Jzazbz_b - 1)*Z;
12616      Yp = Jzazbz_g*Y - (Jzazbz_g - 1)*X;
12617      Zp = Z;
12618      L = 0.41478972*Xp + 0.579999*Yp + 0.0146480*Zp;
12619      M = -0.2015100*Xp + 1.120649*Yp + 0.0531008*Zp;
12620      S = -0.0166008*Xp + 0.264800*Yp + 0.6684799*Zp;
12621      tmp = (L/peakLum)^Jzazbz_n;
12622      Lp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p;
12623      tmp = (M/peakLum)^Jzazbz_n;
12624      Mp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p;
12625      tmp = (S/peakLum)^Jzazbz_n;
12626      Sp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p;
12627      Iz  = 0.5*Lp + 0.5*Mp;
12628      az = 3.52400*Lp - 4.066708*Mp + 0.542708*Sp;
12629      bz = 0.199076*Lp + 1.096799*Mp - 1.295875*Sp;
12630      Jz = (1 + Jzazbz_d)*Iz/(1 + Jzazbz_d*Iz) - Jzazbz_d0;
12631      [ Jz,az,bz ]"
12632  a c endl done
12633
12634#@cli xyz2lab : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12635#@cli : Convert color representation of selected images from XYZ to Lab.
12636#@cli : Default value: 'illuminant=2'.
12637xyz2lab : skip "${1=,}"
12638  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
12639  e[^-1] "Convert color representation of image$? from XYZ to Lab, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12640  to_color
12641  f "
12642    begin(
12643      const epsilon = 216/24389;
12644      const kappa = 24389/27;
12645      lab(x) = (x>epsilon?cbrt(x):(x*kappa + 16)/116);
12646      D65 = [ 0.4124564, 0.3575761, 0.1804375,
12647              0.2126729, 0.7151522, 0.0721750,
12648              0.0193339, 0.1191920, 0.9503041 ];
12649      D50 = [ 0.43603516, 0.38511658, 0.14305115,
12650              0.22248840, 0.71690369, 0.06060791,
12651              0.01391602, 0.09706116, 0.71392822 ];
12652      E = [ 0.488718,0.3106803,0.2006017,
12653            0.1762044,0.8129847,0.0108109,
12654            0,0.0102048,0.9897952 ];
12655      white = ("$illu"==2?E:"$illu"==1?D65:D50)*[ 1,1,1 ];
12656    );
12657    xr = i0/white[0];
12658    yr = i1/white[1];
12659    zr = i2/white[2];
12660    fx = lab(xr);
12661    fy = lab(yr);
12662    fz = lab(zr);
12663    [ cut(116*fy - 16,0,100), 500*(fx - fy), 200*(fy - fz) ]"
12664
12665#@cli xyz2rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12666#@cli : Convert color representation of selected images from XYZ to RGB.
12667#@cli : Default value: 'illuminant=2'.
12668xyz2rgb : skip "${1=,}"
12669  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
12670  e[^-1] "Convert color representation of image$? from XYZ to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12671  if $illu==2 # E
12672    mix_rgb {[2.3706743,-0.9000405,-0.4706338,\
12673              -0.513885,1.4253036,0.0885814,\
12674              0.0052982,-0.0146949,1.0093968]*255}
12675  elif $illu # D65
12676    mix_rgb {[3.2404542,-1.5371385,-0.4985314,\
12677              -0.9692660,1.8760108,0.0415560,\
12678              0.0556434,-0.2040259,1.0572252]*255}
12679  else # D50
12680    mix_rgb {[3.134274799724,-1.617275708956,-0.490724283042,\
12681              -0.978795575994,1.916161689117,0.033453331711,\
12682              0.071976988401,-0.228984974402,1.405718224383]*255}
12683  fi
12684  c 0,255
12685
12686#@cli xyz82rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12687#@cli : Convert color representation of selected images from XYZ8 to RGB.
12688#@cli : Default value: 'illuminant=2'.
12689xyz82rgb : skip "${1=,}"
12690  l[] if isnum("$1") illu={"$1>1?2:$1>0?1:0"} else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 endl
12691  e[^-1] "Convert color representation of image$? from XYZ8 to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12692  repeat $!
12693    sh[$>] 0 /. 255 rm.
12694    sh[$>] 1 /. 255 rm.
12695    sh[$>] 2 /. 231.8182 rm.
12696  done xyz2rgb $illu
12697
12698#@cli ycbcr2rgb
12699#@cli : Convert color representation of selected images from YCbCr to RGB.
12700ycbcr2rgb :
12701  e[^-1] "Convert color representation of image$? from YCbCr to RGB."
12702  repeat $!
12703    sh[$>] 0 -. 16 rm.
12704    sh[$>] 1,2 -. 128 rm.
12705    sh[$>] 0,2 mix_rgb. 298,0,409,\
12706                        298,-100,-208,\
12707                        298,516,0
12708    +. 128 /. 256 c. 0,255 rm.
12709  done
12710
12711#@cli yiq2rgb
12712#@cli : Convert color representation of selected images from YIQ to RGB.
12713yiq2rgb :
12714  e[^-1] "Convert color representation of image$? from YIQ to RGB."
12715  mix_rgb 1,0.9563,0.6210,\
12716          1,-0.2721,-0.6474,\
12717          1,-1.1070,1.7046
12718  c 0,255
12719
12720#@cli yiq82rgb
12721#@cli : Convert color representation of selected images from YIQ8 to RGB.
12722yiq82rgb :
12723  e[^-1] "Convert color representation of image$? from YIQ8 to RGB."
12724  repeat $!
12725    sh[$>] 1 /. 0.8393238012481239 -. 151.908 rm.
12726    sh[$>] 2 /. 0.9567690472081104 -. 133.261 rm.
12727  done
12728  mix_rgb 1,0.9563,0.6210,\
12729          1,-0.2721,-0.6474,\
12730          1,-1.1070,1.7046
12731  c 0,255
12732
12733#@cli yuv2rgb
12734#@cli : Convert color representation of selected images from YUV to RGB.
12735yuv2rgb :
12736  e[^-1] "Convert color representation of image$? from YUV to RGB."
12737  mix_rgb {[1,0,1.13983,\
12738            1,-0.39465,-0.5806,\
12739            1,2.03211,0]*255}
12740  c 0,255
12741
12742#@cli yuv82rgb
12743#@cli : Convert selected images from YUV8 to RGB color bases.
12744yuv82rgb :
12745  e[^-1] "Convert color representation of image$? from YUV8 to RGB."
12746  repeat $!
12747    sh[$>] 0 /. 255 rm.
12748    sh[$>] 1 /. 289.773 -. 0.44 rm.
12749    sh[$>] 2 /. 205.645 -. 0.62 rm.
12750  done yuv2rgb
12751
12752#---------------------------------
12753#
12754#@cli :: Geometry Manipulation
12755#
12756#---------------------------------
12757
12758#@cli a : eq. to 'append' : (+)
12759
12760#@cli append : [image],axis,_centering : axis,_centering : (+)
12761#@cli : Append specified image to selected images, or all selected images together, along specified axis.
12762#@cli : (eq. to 'a').\n
12763#@cli : 'axis' can be { x | y | z | c }.
12764#@cli : Usual 'centering' values are { 0=left-justified | 0.5=centered | 1=right-justified }.
12765#@cli : Default value: 'centering=0'.
12766#@cli : $ image.jpg split y,10 reverse append y
12767#@cli : $ image.jpg repeat 5 +rows[0] 0,{10+18*$>}% done remove[0] append x,0.5
12768#@cli : $ image.jpg append[0] [0],y
12769
12770#@cli append_tiles : _M>=0,_N>=0,0<=_centering_x<=1,0<=_centering_y<=1
12771#@cli : Append MxN selected tiles as new images.
12772#@cli : If 'N' is set to 0, number of rows is estimated automatically.
12773#@cli : If 'M' is set to 0, number of columns is estimated automatically.
12774#@cli : If 'M' and 'N' are both set to '0', auto-mode is used.
12775#@cli : If 'M' or 'N' is set to 0, only a single image is produced.
12776#@cli : 'centering_x' and 'centering_y' tells about the centering of tiles when they have different sizes.
12777#@cli : Default values: 'M=0', 'N=0', 'centering_x=centering_y=0.5'.
12778#@cli : $ image.jpg split xy,4 append_tiles ,
12779append_tiles : check "isint(${1=0}) && isint(${2=0}) && ${3=0}>=0 && $3<=1 && ${4=$3}>=0 && $4<=1"
12780  if !$!
12781    e[0--3] "Append image$? as a 0x0-tiled image."
12782    return
12783  elif !$1" && "!$2 # Auto-mode
12784    N={int(sqrt($!))} M={ceil($!/$N)}
12785    e[0--3] "Append image$? as a "${M}x${N}"-tiled image (auto-mode)."
12786  elif !$2 # Auto-rows
12787    M=$1 N={round($!/$1,1,1)}
12788    e[0--3] "Append image$? as a "${M}x${N}"-tiled image."
12789  elif !$1 # Auto-columns
12790    M={round($!/$2,1,1)} N=$2
12791    e[0--3] "Append image$? as a "${M}x${N}"-tiled image."
12792  else
12793    e[0--3] "Append image$?, as $1x$2-tiled images."
12794    M=$1 N=$2
12795  fi
12796  W,H=${-max_wh} rr2d $W,$H,2,1
12797  MN={$M*$N} if $!%$MN 0x{$MN-($!%$MN)} fi
12798  repeat $!/$MN l[$>-{$>+$MN-1}]
12799    repeat $!/$M a[$>-{$>+$M-1}] x,$3 done a y,$4
12800  endl done
12801
12802#@cli apply_scales : "command",number_of_scales>0,_min_scale[%]>=0,_max_scale[%]>=0,_scale_gamma>0,_interpolation
12803#@cli : Apply specified command on different scales of selected images.
12804#@cli : 'interpolation' can be { 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos }.
12805#@cli : Default value: 'min_scale=25%', 'max_scale=100%' and 'interpolation=3'.
12806#@cli : $ image.jpg apply_scales "blur 5 sharpen 1000",4
12807apply_scales : check "isint($2) && $2>0 && ${3=25%}>=0 && ${4=100%}>=0 && ${5=1}>0 && isint(${6=3}) && $6>=0"
12808               skip "${1=}"
12809  s0="no" s1="nearest-neighbor" s2="average" s3="linear" s4="grid" s5="bicubic" s6="lanczos"
12810  e[^-1] "Apply command '$1' on image$? for $2 scales ($3 -> $4) and "${s{min(6,$6)}}" interpolation."
12811  repeat $! l[$<] nm={0,n}
12812    scale0={if(${"is_percent $3"},$3*max(w,h,d),$3)}
12813    scale1={if(${"is_percent $4"},$4*max(w,h,d),$4)}
12814    repeat $2
12815      scale={$scale0+($scale1-$scale0)*($>/max(1,$2-1))^$5}
12816      w={0,w==1?1:max(1,round($scale*w/max(w,h,d)))}
12817      h={0,h==1?1:max(1,round($scale*h/max(w,h,d)))}
12818      d={0,d==1?1:max(1,round($scale*d/max(w,h,d)))}
12819      +r[0] $w,$h,$d,100%,$6
12820      if narg("$1") l. $1 endl fi
12821    done
12822    rm[0] nm $nm
12823  endl done
12824
12825#@cli autocrop : value1,value2,... : (no arg) : (+)
12826#@cli : Autocrop selected images by specified vector-valued intensity.
12827#@cli : If no arguments are provided, cropping value is guessed.
12828#@cli : $ 400,400,1,3 fill_color 64,128,255 ellipse 50%,50%,120,120,0,1,255 +autocrop
12829
12830#@cli autocrop_components : _threshold[%],_min_area[%]>=0,_is_high_connectivity={ 0 | 1 },\
12831# _output_type={ 0=crop | 1=segmentation | 2=coordinates }
12832#@cli : Autocrop and extract connected components in selected images, according to a mask given as the last channel of
12833#@cli : each of the selected image (e.g. alpha-channel).
12834#@cli : Default values: 'threshold=0%', 'min_area=0.1%', 'is_high_connectivity=0' and 'output_type=1'.
12835#@cli : $ 256,256 noise 0.1,2 eq 1 dilate_circ 20 label_fg 0,1 normalize 0,255 +neq 0 *[-1] 255 append c \
12836# +autocrop_components ,
12837autocrop_components : skip ${1=0%} check "${2=0.1%}>=0 && isbool(${3=0}) && isint(${4=1}) && $4>=0 && $4<=2"
12838  e[^-1] "Autocrop connected components from image$?, with threshold $1, minimal area $2, "\
12839         ${arg\ 1+$3,low,high}" connectivity "\
12840         "and output type set to '"${arg\ 1+$4,crop,segmentation,coordinates}"'.\n"
12841  repeat $! l[$>]
12842    min_area={max(1,round(if(${is_percent\ $2},$2*w*h,$2)))}
12843    +channels 100% >. $1 area_fg. 0,$3 >=. $min_area  # Discard background and small objects.
12844    +area. 0,1 <. $min_area -|[-2,-1] label_fg. 0,1    # Fill small holes in objects.
12845
12846    # Extract detected objects.
12847    N={iM} repeat iM
12848      n={1+$>}
12849      e[] "\r  > "$n/$N
12850      rprogress {100*$n/$N}
12851      +==[1] $n +*[0,-1] rm..
12852      if $4==0 coords=${autocrop_coords.\ auto} rm. +z[0] $coords
12853      elif $4==1 autocrop.
12854      else coords=${autocrop_coords.\ auto} rm. ($coords) y.
12855      fi
12856    done
12857    rm[0,1]
12858    if !$! 0 fi
12859    if $4==2 a x fi
12860  endl done
12861
12862#@cli autocrop_seq : value1,value2,... | auto
12863#@cli : Autocrop selected images using the crop geometry of the last one by specified vector-valued intensity,
12864#@cli : or by automatic guessing the cropping value.
12865#@cli : Default value: auto mode.
12866#@cli : $ image.jpg +fill[-1] 0 ellipse[-1] 50%,50%,30%,20%,0,1,1 autocrop_seq 0
12867autocrop_seq : skip ${1=auto}
12868  e[^-1] "Auto-crop image$? using crop geometry of last image by vector '$*'."
12869  if !$! return fi
12870  is_auto={['"$1"']=='auto'}
12871  if $!==1 _autocrop$is_auto ${1--1} return fi
12872  coords=${autocrop_coords.\ ${1--1}}
12873  x0={arg(1,$coords)} y0={arg(2,$coords)} z0={arg(3,$coords)}
12874  x1={arg(4,$coords)} y1={arg(5,$coords)} z1={arg(6,$coords)}
12875  if $x0>$x1" || "$y0>$y1" || "$z0>$z1 i[0--2] 0 rm[1--1:2]
12876  else z $x0,$y0,$z0,$x1,$y1,$z1
12877  fi
12878
12879#@cli channels : { [image0] | c0[%] },_{ [image1] | c1[%] } : (+)
12880#@cli : Keep only specified channels of selected images.
12881#@cli : Dirichlet boundary is used when specified channels are out of range.
12882#@cli : $ image.jpg channels 0,1
12883#@cli : $ image.jpg luminance channels 0,2
12884
12885#@cli columns : { [image0] | x0[%] },_{ [image1] | x1[%] } : (+)
12886#@cli : Keep only specified columns of selected images.
12887#@cli : Dirichlet boundary is used when specified columns are out of range.
12888#@cli : $ image.jpg columns -25%,50%
12889
12890#@cli z : eq. to 'crop'. : (+)
12891
12892#@cli crop : x0[%],x1[%],_boundary_conditions : x0[%],y0[%],x1[%],y1[%],_boundary_conditions : \
12893# x0[%],y0[%],z0[%],x1[%],y1[%],z1[%],_boundary_conditions : \
12894# x0[%],y0[%],z0[%],c0[%],x1[%],y1[%],z1[%],c1[%],_boundary_conditions : (+)
12895#@cli : Crop selected images with specified region coordinates.
12896#@cli : (eq. to 'z').\n
12897#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
12898#@cli : Default value: 'boundary_conditions=0'.
12899#@cli : $ image.jpg +crop -230,-230,280,280,1 crop[0] -230,-230,280,280,0
12900#@cli : $ image.jpg crop 25%,25%,75%,75%
12901
12902#@cli diagonal
12903#@cli : Transform selected vectors as diagonal matrices.
12904#@cli : $ 1,10,1,1,'y' +diagonal
12905diagonal :
12906  e[^-1] "Transform vector$? as diagonal matrix."
12907  y repeat $! r[$>] {$>,h+1},100%,1,1,0 r[$>] {$>,h},100%,1,1,-1 done
12908
12909# downsize_aliased : 100<=factor<=0
12910# Downsize selected images with a specific algorithm to better render anti-aliased rendering
12911# over transparent background (from aliased transparent images, manage transparent border pixels with more care).
12912downsize_aliased : check "${1=50}>=0 && $1<=100"
12913  if $1==100 return elif !$1 r 1,1,1,100%,2 return fi
12914  N={ceil(1+100/$1)}
12915  repeat $! l[$>]
12916    split_opacity
12917    if $!==1 continue fi
12918    +dilate.. $N +==.. 0
12919    j[0] ..,0,0,0,0,1,. rm[-2,-1]
12920    a c
12921    r $1%,$1%,$1%,100%,2
12922  endl done
12923
12924#@cli elevate : _depth,_is_plain={ 0 | 1 },_is_colored={ 0 | 1 }
12925#@cli : Elevate selected 2D images into 3D volumes.
12926#@cli : Default values: 'depth=64', 'is_plain=1' and 'is_colored=1'.
12927elevate : check "${1=64}>0" skip ${2=1},${3=1}
12928  e[^-1] "Elevate 2D image$? into $1-slices volume(s)."
12929  r 100%,100%,1,100%
12930  repeat $! l[$>] nm={0,n}
12931    +norm 100%,100%,$1,{if($3,{0,s},1)}
12932    m={-2,im} d={-2,iM-$m}
12933    repeat $1
12934      if $2 +>=[1] {$m+$d*($>+1)/$1}
12935      else +ir[1] {$m+$d*$>/$1},{$m+$d*($>+1)/$1}
12936      fi
12937      r. 100%,100%,1,.. if $3 *. [0] fi
12938      j.. .,0,0,$> rm.
12939    done
12940  rm[0,1] nm $nm endl done
12941
12942#@cli expand_x : size_x>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
12943#@cli : Expand selected images along the x-axis.
12944#@cli : Default value: 'boundary_conditions=1'.
12945#@cli : $ image.jpg expand_x 30,0
12946expand_x : check "$1>=0 && ${2=1}>=0 && $2<=3"
12947  e[^-1] "Expand image$? along the x-axis with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
12948          boundary conditions."
12949  repeat $! r[$>] {$>,w+2*$1},100%,100%,100%,0,$2,0.5,0.5,0.5 done
12950
12951#@cli expand_xy : size>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
12952#@cli : Expand selected images along the xy-axes.
12953#@cli : Default value: 'boundary_conditions=1'.
12954#@cli : $ image.jpg expand_xy 30,0
12955expand_xy : check "$1>=0 && ${2=1}>=0 && $2<=3"
12956  e[^-1] "Expand image$? along the xy-axes with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
12957          boundary conditions."
12958  repeat $! r[$>] {$>,w+2*$1},{$>,h+2*$1},100%,100%,0,$2,0.5,0.5,0.5 done
12959
12960#@cli expand_xyz : size>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
12961#@cli : Expand selected images along the xyz-axes.
12962#@cli : Default value: 'boundary_conditions=1'.
12963expand_xyz : check "$1>=0 && ${2=1}>=0 && $2<=3"
12964  e[^-1] "Expand image$? along the xyz-axes with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
12965          boundary conditions."
12966  repeat $! r[$>] {$>,w+2*$1},{$>,h+2*$1},{$>,d+2*$1},100%,0,$2,0.5,0.5,0.5 done
12967
12968#@cli expand_y : size_y>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
12969#@cli : Expand selected images along the y-axis.
12970#@cli : Default value: 'boundary_conditions=1'.
12971#@cli : $ image.jpg expand_y 30,0
12972expand_y : check "$1>=0 && ${2=1}>=0 && $2<=3"
12973  e[^-1] "Expand image$? along the y-axis with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
12974          boundary conditions."
12975  repeat $! r[$>] 100%,{$>,h+2*$1},100%,100%,0,$2,0.5,0.5,0.5 done
12976
12977#@cli expand_z : size_z>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
12978#@cli : Expand selected images along the z-axis.
12979#@cli : Default value: 'boundary_conditions=1'.
12980expand_z : check "$1>=0 && ${2=1}>=0 && $2<=3"
12981  e[^-1] "Expand image$? along the z-axis with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
12982          boundary conditions."
12983  repeat $! r[$>] 100%,100%,{$>,d+2*$1},100%,0,$2,0.5,0.5,0.5 done
12984
12985#@cli extract : "condition",_output_type={ 0=xyzc-coordinates | 1=xyz-coordinates | 2=scalar-values | 3=vector-values }
12986#@cli : Extract a list of coordinates or values from selected image, where
12987#@cli : specified mathematical condition holds.
12988#@cli : For N coordinates matching, result is a 1xNx1x4 image.
12989#@cli : Default values: 'output_type=0'.
12990#@cli : $ sp lena +extract "norm(I)>128",3
12991extract : check "isin(${2=0},0,1,2,3)"
12992  s0,s1,s2,s3=xyzc-coordinates,xyz-coordinates,scalar-values,vector-values
12993  e[^-1] "Extract "$s$2" from image$? verifying condition '$1'."
12994  str=">"${-math_lib}"begin(run('1,32,1,',arg(1+$2,4,3,1,s)));($1)?("
12995  if $2==0   str.="dar_insert(#-1,[x,y,z,c]));i"
12996  elif $2==1 str.="dar_insert(#-1,[x,y,z]));I"
12997  elif $2==2 str.="dar_insert(#-1,i));i"
12998  else       str.="dar_insert(#-1,I));I"
12999  fi
13000  str.=";end(resize(#-1,1,dar_size(#-1),1,s(#-1),0))"
13001  repeat $! nm={$>,n} eval[$>] $str nm. $nm rv[$>,-1] rm. done
13002
13003#@cli extract_region : [label_image],_extract_xyz_coordinates={ 0 | 1 },_label_1,...,_label_M
13004#@cli : Extract all pixels of selected images whose corresponding label in '[label_image]' is equal to 'label_m',
13005#@cli : and output them as M column images.
13006#@cli : Default value: 'extract_xyz_coordinates=0'.
13007#@cli : $ image.jpg +blur 3 quantize. 4,0 +extract_region[0] [1],0,1,3
13008extract_region : check ${"is_image_arg $1"}" && isnum(${2=0})"
13009  if $#<3 e[0--3] "Extract pixels of image$? for labels [] in image $1, with"${"arg 1+!!$2,out"}" coordinates
13010                   -> no labels provided, ignoring." return fi
13011  e[^-1] "Extract pixels of image$? for labels {${3--1}} in image $1, with"${"arg 1+!!$2,out"}" coordinates."
13012  pass$1 mv. 0 repeat $!-1 l[0,{1+$<}] nm={n}
13013    1,16,1,{s+($2?3:0)} if $#>3 .x{$#-3} fi
13014    f[0] ">"${-math_lib}"
13015      begin(
13016        const N = iM + 1;  # Number of labels
13017        R = [ ${3--1} ];   # Requested labels
13018        hash = vectorN(0); # Correspondence table
13019        repeat (size(R),k, hash[R[k]] = k + 2);
13020      );
13021      (ind = hash[i])>0?(
13022        $2?dar_insert(#ind,[ I(#1),x,y,z ]):dar_insert(#ind,I(#1));
13023      ); i;
13024      end(
13025        repeat (l - 2,k, resize(#k + 2,1,dar_size(#k + 2),1,-100,0))
13026      )"
13027    rm[1] nm $nm
13028  endl done rm[0]
13029
13030#@cli montage : "_layout_code",_montage_mode={ 0<=centering<=1 | 2<=scale+2<=3 },\
13031# _output_mode={ 0=single layer | 1=multiple layers },"_processing_command"
13032#@cli : Create a single image montage from selected images, according to specified layout code :
13033#@cli : - 'X' to assemble all images using an automatically estimated layout.
13034#@cli : - 'H' to assemble all images horizontally.
13035#@cli : - 'V' to assemble all images vertically.
13036#@cli : - 'A' to assemble all images as an horizontal array.
13037#@cli : - 'B' to assemble all images as a vertical array.
13038#@cli : - 'Ha:b' to assemble two blocks 'a' and 'b' horizontally.
13039#@cli : - 'Va:b' to assemble two blocks 'a' and 'b' vertically.
13040#@cli : - 'Ra' to rotate a block 'a' by 90 deg. ('RRa' for 180 deg. and 'RRRa' for 270 deg.).
13041#@cli : - 'Ma' to mirror a block 'a' along the X-axis ('MRRa' for the Y-axis).
13042#@cli : A block 'a' can be an image index (treated periodically) or a nested layout expression 'Hb:c','Vb:c','Rb' or
13043#@cli : 'Mb' itself.
13044#@cli : For example, layout code 'H0:V1:2' creates an image where image [0] is on the left, and images [1] and [2]
13045#@cli : vertically packed on the right.
13046#@cli : Default values: 'layout_code=X', 'montage_mode=2', output_mode='0' and 'processing_command=""'.
13047#@cli : $ image.jpg sample ? +plasma[0] shape_cupid 256 normalize 0,255 frame 3,3,0 frame 10,10,255 to_rgb \
13048# +montage A +montage[^-1] H1:V0:VH2:1H0:3
13049montage : check "isnum(${2=2}) && $2>=0 && $2<=3" skip "${1=X}",${3=0},"${4=}"
13050  if $2<=1 e[0--3] "Create aligned montage from image$?, with layout code '$1' and centering $2."
13051  else e[0--3] "Create scaled montage from image$?, with layout code '$1' and scale "{$2-2}"."
13052  fi
13053  to_colormode 0
13054
13055  # Manage automatic layout.
13056  if lowercase('"$1"')=='x' +l
13057    repeat $! nm[$>] $> done
13058    repeat $!-1
13059      if {-2,w>h}" && "w>h mode=V      # Both landscape.
13060      elif {-2,h>w}" && "h>w mode=H    # Both portrait.
13061      elif {-2,w>h}" && "h>w # Landscape - portrait.
13062        if {-2,h/w}<(w/h) mode=V else mode=H fi
13063      else # Portrait - landscape.
13064        if {-2,w/h}<(h/w) mode=H else mode=V fi
13065      fi
13066      name=$mode{-2,n}:{n}
13067      montage[-2,-1] $mode,$2
13068      mv. 0 nm[0] $name
13069    done
13070    layout={0,n}
13071    rm endl montage $layout,$2,$3,"$4"
13072
13073  else # Non-automatic layout.
13074
13075    # Format and check validity of layout code.
13076    N=$!
13077    l[] _scode="$1" _mode=$2
13078      if lowercase('"$1"')=='h' if $N>1 {$N-1},1,1,1,-1 $N,1,1,1,x a x y else return fi # Simple horizontal montage.
13079      elif lowercase('"$1"')=='v' if $N>1 {$N-1},1,1,1,-2 $N,1,1,1,x a x y else return fi # Simple vertical montage.
13080      elif s=lowercase('"$1"');s=='a'||s=='b' # Montage as an array.
13081        if $N<2 return fi
13082        nr={round(sqrt($N))} nc={round($N/$nr,1,1)} # Horizontal array.
13083        if lowercase('"$1"')=='b' n=$nr nr=$nc nc=$n fi # Vertical array.
13084        $N,1,1,1,x s x,-{round(w/$nr,1,1)} repeat $! l[$>] if w>1 i[0] {w-1},1,1,1,-1 a x fi endl done a x
13085        if $nr>1 i[0] {$nr-1},1,1,1,-2 a x fi y
13086      else # Other complex montage.
13087        ('"$1"') f "if(i==72 || i==104,-1,
13088                    if(i==86 || i==118,-2,
13089                    if(i==82 || i==114,-3,
13090                    if(i==77 || i==109,-4,
13091                    if(i>=48 && i<=57,i-48,-5)))))"
13092        s +,-1 s +,-2 s +,-3 s +,-4 s +,-5
13093        repeat $! l[$>] if im>=0 ++. 48 =.. {t} rm. rows 0 fi endl done a y discard -5
13094      fi
13095      f 'if(i<0,i,i%$N)'
13096    endl
13097    if $!==$N rm return fi  # Empty layout code.
13098    nm[^-1] 0 repeat h c={i[$>]} # Duplicate multiple references.
13099      if $c>=0 if {$c,n} i.. [$c] i={$!-2} =. $i,0,$> ref$i=$c else nm[$c] 1 ref$c=$c fi fi
13100    done
13101    _code={^} _lcode={narg($_code)} rm.
13102
13103    # Determine image positions and sizes.
13104    N=$! repeat $N ($>,0,0,{$>,w},{$>,h},0,0,0) done
13105    l[$N--1] _p=1 k[${-_montage}] w={i[3]} h={i[4]} f 'if(i(0,y)<0,-1,i)' discard -1 y r 8,{h/8},1,1,-1
13106    onfail error[0--3] "Too many input images."
13107    endl
13108
13109    # Render montage.
13110    if narg("$4") m "__montage : $4 k[0]"
13111    else m "__montage : if $""7%2 mirror x fi if $""8%2 mirror y fi rotate {90*$""6}
13112                        r {max(1,round($""4,1,1))},{max(1,round($""5,1,1))},1,100%,3"
13113    fi
13114
13115    s=${max_s[^-1]}
13116    repeat h
13117      i={i(0,$>)} xi={i(1,$>)} yi={i(2,$>)} wi={i(3,$>)} hi={i(4,$>)} ai={i(5,$>)} mxi={i(6,$>)} myi={i(7,$>)}
13118      if $3||!$> i.. $w,$h,1,$s fi
13119      __montage[$i] ${ref$i},$xi,$yi,{max(1,$wi)},{max(1,$hi)},$ai,$mxi,$myi
13120      j.. [$i],$xi,$yi
13121    done
13122    um __montage
13123    rm[0-{$N-1},-1]
13124
13125  fi
13126  nm "[Montage '$1']"
13127
13128_montage :
13129  if $_p>$_lcode error "Command 'montage': Incomplete layout code '"$_scode"'." fi
13130  c={arg($_p,$_code)}
13131  if $c>=0 _p+=1 u $c # Single index.
13132  elif $c==-4 # Mirror.
13133    _p+=1 l=${-_montage} f[$l] 'a=i(5,y)%2;if((x==7&&a)||(x==6&&!a),!i,if(x==1,i(3,0)-i(3,y)-i,i))' u $l
13134  elif $c==-3 # Rotation.
13135    _p+=1 l=${-_montage} l[$l] s x +[2] [4] rv[1,2] *[1] -1 +[1] {4,@0} rv[3,4] +[5] 1 a x endl u $l
13136  else # Merge.
13137    _p+=1
13138    l=${-_montage} lw={$l,@3} lh={$l,@4}
13139    r=${-_montage} rw={$r,@3} rh={$r,@4}
13140
13141    if $c==-1 # Horizontal merge.
13142      if $_mode<2
13143        h={max($lh,$rh)}
13144        +[$l] '0,0,{($h-$lh)*min(1,$_mode)},0,0,0,0,0'
13145        +[$r] '0,$lw,{($h-$rh)*min(1,$_mode)},0,0,0,0,0'
13146      else
13147        h={($_mode-2)*max($lh,$rh)+(3-$_mode)*min($lh,$rh)}
13148        lf={$h/$lh} rf={$h/$rh} lw={$lw*$lf} rw={$rw*$rf}
13149        *[$l] '1,$lf,$lf,$lf,$lf,1,1,1' *[$r] '1,$rf,$rf,$rf,$rf,1,1,1' +[$r] '0,$lw,0,0,0,0,0,0'
13150      fi
13151      i[$l] (-1,0,0,{$lw+$rw},$h,0,0,0) a[$l,{$l+1}] y a[$l] [$r],y r[$r] 1,1,1,1,0
13152
13153    else # Vertical merge.
13154      if $_mode<2
13155        w={max($lw,$rw)}
13156        +[$l] '0,{($w-$lw)*min(1,$_mode)},0,0,0,0,0,0'
13157        +[$r] '0,{($w-$rw)*min(1,$_mode)},$lh,0,0,0,0,0'
13158      else
13159        w={($_mode-2)*max($lw,$rw)+(3-$_mode)*min($lw,$rw)}
13160        lf={$w/$lw} rf={$w/$rw} lh={$lh*$lf} rh={$rh*$rf}
13161        *[$l] '1,$lf,$lf,$lf,$lf,1,1,1' *[$r] '1,$rf,$rf,$rf,$rf,1,1,1' +[$r] '0,0,$lh,0,0,0,0,0'
13162      fi
13163      i[$l] (-1,0,0,$w,{$lh+$rh},0,0,0) a[$l,{$l+1}] y a[$l] [$r],y r[$r] 1,1,1,1,0
13164    fi
13165    u $l
13166  fi
13167
13168#@cli mirror : { x | y | z }...{ x | y | z } : (+)
13169#@cli : Mirror selected images along specified axes.
13170#@cli : $ image.jpg +mirror y +mirror[0] c
13171#@cli : $ image.jpg +mirror x +mirror y append_tiles 2,2
13172
13173#@cli permute : permutation_string : (+)
13174#@cli : Permute selected image axes by specified permutation.
13175#@cli : 'permutation' is a combination of the character set {x|y|z|c},
13176#@cli : e.g. 'xycz', 'cxyz', ...
13177#@cli : $ image.jpg permute yxzc
13178
13179#@cli r : eq. to 'resize'. : (+)
13180
13181#@cli resize : {[image_w] | width>0[%]},_{[image_h] | height>0[%]},_{[image_d] | depth>0[%]},\
13182# _{[image_s] | spectrum>0[%]},_interpolation,_boundary_conditions,_ax,_ay,_az,_ac : (+)
13183#@cli : Resize selected images with specified geometry.
13184#@cli : (eq. to 'r').\n
13185#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
13186# 4=grid | 5=bicubic | 6=lanczos }.
13187#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
13188#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
13189#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13190#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
13191#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0 or 4'
13192#@cli : (set to '0' by default, must be defined in range [0,1]).
13193#@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
13194#@cli : $ image.jpg +resize[-1] 256,128,1,3,2 +resize[-1] 120%,120%,1,3,0,1,0.5,0.5 \
13195# +resize[-1] 120%,120%,1,3,0,0,0.2,0.2 +resize[-1] [0],[0],1,3,4
13196
13197#@cli ri : eq. to 'resize_as_image'.
13198ri : skip "${2=1},${3=0},${4=0},${5=0},${6=0},${7=0}" # Faster than the non-shortcut version of the command
13199  pass$1 r[^-1] .,.,.,.,${2--1} rm.
13200
13201#@cli resize_as_image : [reference],_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
13202#@cli : Resize selected images to the geometry of specified [reference] image.
13203#@cli : (eq. to 'ri').
13204#@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
13205#@cli : $ image.jpg sample duck +resize_as_image[-1] [-2]
13206resize_as_image : check ${"is_image_arg $1"}" && isint(${2=1}) && $2>=-1 && $2<=6 && "\
13207   "isint(${3=0}) && $3>=0 && $3<=3 && isnum(${4=0}) && isnum(${5=0}) && isnum(${6=0}) && isnum(${7=0})"
13208  pass$1 r[^-1] .,.,.,.,${2--1} rm.
13209
13210#@cli resize_mn : width[%]>=0,_height[%]>=0,_depth[%]>=0,_B_value,_C_value
13211#@cli : Resize selected images with Mitchell-Netravali filter (cubic).
13212#@cli : For details about the method, see: <https://de.wikipedia.org/wiki/Mitchell-Netravali-Filter>.
13213#@cli : Default values: 'height=100%', 'depth=100%', 'B=0.3333' and 'C=0.3333'.
13214#@cli : $ image.jpg resize2dx 32 resize_mn 800%,800%
13215resize_mn : check "${2=100%}>=0 && ${3=100%}>=0" skip "${4=0.333},${5=0.333}"
13216  e[^-1] "Resize image$? to $1x$2x$3 using Mitchell-Netravali filter (B=$4, C=$5)."
13217  lib="const B = $4; const C = $5; const boundary = 1; const interp = 0;
13218       mn(P0,P1,P2,P3,d) = ( ( (-B/6-C)*P0 + (-3*B/2-C+2)*P1 + (3*B/2+C-2)*P2 + (B/6+C)*P3 )*d^3
13219                           + ( (B/2+2*C)*P0 + (2*B+C-3)*P1 + (-5*B/2-2*C+3)*P2 -C*P3)*d^2
13220                           + ( (-B/2-C)*P0 + (B/2+C)*P2)*d
13221                           + B/6*P0 + (-B/3+1)*P1 + B/6*P2);"
13222  repeat $! l[$>] nm={n}
13223    nw={${"is_percent $1"}?max(1,round($1*w)):round($1)}
13224    nh={${"is_percent $2"}?max(1,round($2*h)):round($2)}
13225    nd={${"is_percent $3"}?max(1,round($3*d)):round($3)}
13226    if !$nw" || "!$nh" || "!$nd rm 0
13227    elif !w rm $nw,$nh,$nd,1
13228    else
13229      if w==1||$nw<w r $nw,100%,100%,100%,{w==1?1:2}
13230      elif $nw>w
13231        $nw,100%,100%,100%,${lib}"X = x*(w#-1-1)/(w-1); d = X - int(X); P0 = I(#-1,X-1); P1 = I(#-1,X);
13232                                  P2 = I(#-1,X+1); P3 = I(#-1,X+2); mn(P0,P1,P2,P3,d);" k.
13233      fi
13234      if h==1||$nh<h
13235        r 100%,$nh,100%,100%,{h==1?1:2}
13236      elif $nh!=h
13237        100%,$nh,100%,100%,${lib}"Y = y*(h#-1-1)/(h-1); d = Y - int(Y); P0 = I(#-1,x,Y-1); P1 = I(#-1,x,Y);
13238                                  P2 = I(#-1,x,Y+1); P3 = I(#-1,x,Y+2); mn(P0,P1,P2,P3,d);" k.
13239      fi
13240      if d==1" || "$nd<d
13241        r 100%,100%,$nd,100%,{d==1?1:2}
13242      elif $nd!=d
13243        100%,100%,$nd,100%,${lib}"Z = z*(d#-1-1)/(d-1); d = Z - int(Z); P0 = I(#-1,x,y,Z-1); P1 = I(#-1,x,y,Z);
13244                                  P2 = I(#-1,x,y,Z+1); P3 = I(#-1,x,y,Z+2); mn(P0,P1,P2,P3,d);" k.
13245      fi
13246    fi
13247    nm $nm
13248  endl done
13249
13250#@cli resize_pow2 : _interpolation,_boundary_conditions,_ax,_ay,_az,_ac
13251#@cli : Resize selected images so that each dimension is a power of 2.
13252#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
13253# 4=grid | 5=bicubic | 6=lanczos }.
13254#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
13255#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
13256#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13257#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
13258#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
13259#@cli : (set to '0' by default, must be defined in range [0,1]).
13260#@cli : Default values: 'interpolation=0', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
13261#@cli : $ image.jpg +resize_pow2[-1] 0
13262resize_pow2 : check "isint(${1=0}) && $1>=-1 && $1<=6" skip ${2=0},${3=0},${4=0},${5=0},${6=0}
13263  e[^-1] "Resize image$? so that each dimension is a power of 2."
13264  repeat $!
13265    r[$>] {$>,2^(round(log2(w),1,1))},{$>,2^(round(log2(h),1,1))},{$>,2^(round(log2(d),1,1))},100%,${1-6}
13266  done
13267
13268#@cli rr2d : eq. to 'resize_ratio2d'.
13269rr2d :
13270  _gmic_s="$?" v + _resize_ratio2d $*
13271
13272#@cli resize_ratio2d : width>0,height>0,_mode={ 0=inside | 1=outside | 2=padded },0=<_interpolation<=6
13273#@cli : Resize selected images while preserving their aspect ratio.
13274#@cli : (eq. to 'rr2d').
13275#@cli : Default values: 'mode=0' and 'interpolation=6'.
13276resize_ratio2d :
13277  _gmic_s="$?" v + _$0 $*
13278
13279_resize_ratio2d : check "$1>0 && $2>0 && ${3=0}>=0 && $3<=2 && ${4=6}>=0 && $4<=6"
13280  e[0--3] "Resize 2D image"$_gmic_s" to $1x$2 with ratio-"${arg\ 1+$3,inside,outside,padded}\
13281           " mode and interpolation type $4."
13282  repeat $!
13283    ratio={$>,if($3==1,max($1/w,$2/h),min($1/w,$2/h))}
13284    r[$>] {$>,max(1,w*$ratio)},{$>,max(1,h*$ratio)},100%,100%,$4
13285  done
13286  if $3==2 r $1,$2,100%,100%,0,0,0.5,0.5 fi
13287
13288
13289#@cli r2dx : eq. to 'resize2dx'.
13290r2dx :
13291  _gmic_s="$?" v + _resize2dx $*
13292
13293#@cli resize2dx : width[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
13294#@cli : Resize selected images along the x-axis, preserving 2D ratio.
13295#@cli : (eq. to 'r2dx').\n
13296#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
13297# 4=grid | 5=bicubic | 6=lanczos }.
13298#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
13299#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
13300#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13301#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
13302#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
13303#@cli : (set to '0' by default, must be defined in range [0,1]).
13304#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
13305#@cli : $ image.jpg +resize2dx 100,2 append x
13306resize2dx :
13307  _gmic_s="$?" v + _$0 $*
13308
13309_resize2dx : check "$1>0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
13310                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
13311  e[0--3] "Resize 2D image"$_gmic_s" to $1 pixels along the x-axis, preserving 2D ratio."
13312  repeat $! l[$>]
13313    size={if(${is_percent\ $1},$1*w,$1)}
13314    r {max(1,$size)},{max(1,h*$size/w)},100%,100%,${2-7}
13315  endl done
13316
13317#@cli r2dy : eq. to 'resize2dy'.
13318r2dy :
13319  _gmic_s="$?" v + _resize2dy $*
13320
13321#@cli resize2dy : height[%]>=0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
13322#@cli : Resize selected images along the y-axis, preserving 2D ratio.
13323#@cli : (eq. to 'r2dy').\n
13324#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
13325# 4=grid | 5=bicubic | 6=lanczos }.
13326#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
13327#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
13328#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13329#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
13330#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
13331#@cli : (set to '0' by default, must be defined in range [0,1]).
13332#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
13333#@cli : $ image.jpg +resize2dy 100,2 append x
13334resize2dy :
13335  _gmic_s="$?" v + _$0 $*
13336
13337_resize2dy : check "$1>=0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
13338                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
13339  e[0--3] "Resize 2D image"$_gmic_s" to $1 pixels along the y-axis, preserving 2D ratio."
13340  repeat $! l[$>]
13341    size={if(${is_percent\ $1},$1*h,$1)}
13342    r {max(1,w*$size/h)},{max(1,$size)},100%,100%,${2-7}
13343  endl done
13344
13345#@cli r3dx : eq. to 'resize3dx'.
13346r3dx :
13347  _gmic_s="$?" v + _resize3dx $*
13348
13349#@cli resize3dx : width[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
13350#@cli : Resize selected images along the x-axis, preserving 3D ratio.
13351#@cli : (eq. to 'r3dx').\n
13352#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
13353# 4=grid | 5=bicubic | 6=lanczos }.
13354#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
13355#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
13356#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13357#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
13358#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
13359#@cli : (set to '0' by default, must be defined in range [0,1]).
13360#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
13361resize3dx :
13362  _gmic_s="$?" v + _$0 $*
13363
13364_resize3dx : check "$1>0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
13365                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
13366  e[0--3] "Resize 3D image"$_gmic_s" to $1 pixels along the x-axis, preserving 3D ratio."
13367  repeat $! l[$>]
13368    size={if(${is_percent\ $1},$1*w,$1)}
13369    r {max(1,$size)},{max(1,h*$size/w)},{max(1,d*$size/w)},100%,${2-7}
13370  endl done
13371
13372#@cli r3dy : eq. to 'resize3dy'.
13373r3dy :
13374  _gmic_s="$?" v + _resize3dy $*
13375
13376#@cli resize3dy : height[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
13377#@cli : Resize selected images along the y-axis, preserving 3D ratio.
13378#@cli : (eq. to 'r3dy').\n
13379#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
13380# 4=grid | 5=bicubic | 6=lanczos }.
13381#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
13382#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
13383#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13384#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
13385#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
13386#@cli : (set to '0' by default, must be defined in range [0,1]).
13387#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
13388resize3dy :
13389  _gmic_s="$?" v + _$0 $*
13390
13391_resize3dy : check "$1>0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
13392                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
13393  e[0--3] "Resize 3D image"$_gmic_s" to $1 pixels along the y-axis, preserving 3D ratio."
13394  repeat $! l[$>]
13395    size={if(${is_percent\ $1},$1*h,$1)}
13396    r {max(1,w*$size/h)},{max(1,$size)},{max(1,d*$size/h)},100%,${2-7}
13397  endl done
13398
13399#@cli r3dz : eq. to 'resize3dz'.
13400r3dz :
13401  _gmic_s="$?" v + _resize3dz $*
13402
13403#@cli resize3dz : depth[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
13404#@cli : Resize selected images along the z-axis, preserving 3D ratio.
13405#@cli : (eq. to 'r3dz').\n
13406#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
13407# 4=grid | 5=bicubic | 6=lanczos }.
13408#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
13409#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
13410#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13411#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
13412#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
13413#@cli : (set to '0' by default, must be defined in range [0,1]).
13414#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
13415resize3dz :
13416  _gmic_s="$?" v + _$0 $*
13417
13418_resize3dz : check "$1>0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
13419                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
13420  e[0--3] "Resize 3D image"$_gmic_s" to $1 pixels along the z-axis, preserving 3D ratio."
13421  repeat $! l[$>]
13422    size={if(${is_percent\ $1},$1*d,$1)}
13423    r[$>] {max(1,w*$size/d)},{max(1,h*$size/d)},{max(1,$size)},100%,${2-7}
13424  endl done
13425
13426#@cli rotate : angle,_interpolation,_boundary_conditions,_center_x[%],_center_y[%] : \
13427# u,v,w,angle,interpolation,boundary_conditions,_center_x[%],_center_y[%],_center_z[%] : (+)
13428#@cli : Rotate selected images with specified angle (in deg.), and optionally 3D axis (u,v,w).
13429#@cli : 'interpolation' can be { 0=none | 1=linear | 2=bicubic }.
13430#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13431#@cli : When a rotation center (cx,cy,_cz) is specified, the size of the image is preserved.
13432#@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'center_x=center_y=(undefined)'.
13433#@cli : $ image.jpg +rotate -25,1,2,50%,50% rotate[0] 25
13434
13435#@cli rotate_tileable : angle,_max_size_factor>=0
13436#@cli : Rotate selected images by specified angle and make them tileable.
13437#@cli : If resulting size of an image is too big, the image is replaced by a 1x1 image.
13438#@cli : Default values: 'max_size_factor=8'.
13439rotate_tileable : check ${2=8}>=0
13440  e[^-1] "Rotate image$? with angle $1 deg. and make them tileable."
13441
13442  # Reduce angle to known fraction.
13443  angle={$1%360}
13444  if $angle>=270 rotate 270 angle-=270
13445  elif $angle>=180 rotate 180 angle-=180
13446  elif $angle>=90 rotate 90 angle-=90
13447  fi
13448  # List of known fractions.
13449  (0,1;1,8;1,7;1,6;1,5;1,4;1,5;1,3;2,5;1,2;2,5;3,5;2,3;3,4;4,5;1,1;5,4;7,5;3,2;8,5;9,5;2,1;3,1;4,1;5,1;6,1;7,1;8,1)
13450  s. x,2 +/[-2,-1] atan. *. {180/pi}   # Compute corresponding angles.
13451  ($angle) index. .. rm..
13452  p={-3,@{^}} q={-2,@{^}} rm[-3--1]       # Find nearest fraction p/q to atan(angle).
13453  if !$p||!$q return fi
13454
13455  repeat $! l[$>]
13456    # Compute width and height of tile.
13457    theta={atan2($p,$q)}
13458    gcd=${gcd" "{h*$q},{w*$p}}
13459    pw={h*$q/$gcd}
13460    nw={round($pw*w/cos($theta))}
13461    gcd=${gcd" "{h*$p},{w*$q}}
13462    qh={w*$q/$gcd}
13463    nh={round($qh*h/cos($theta))}
13464
13465    # Rotate and make tileable (may result in very large images!).
13466    if !$2" || "($nw<$2*w" && "$nh<$2*h)
13467      r {1.5*$nw},{1.5*$nh},1,100%,0,2
13468      rotate {$theta*180/pi},1,2,50%,50%
13469      r $nw,$nh,1,100%,0,2,0.5,0.5
13470    else error[0--4] "Command '$0': Invalid image dimension "({w},{h},{d},{s}).
13471    fi
13472  endl done
13473
13474#@cli rows : { [image0] | y0[%] },_{ [image1] | y1[%] } : (+)
13475#@cli : Keep only specified rows of selected images.
13476#@cli : Dirichlet boundary conditions are used when specified rows are out of range.
13477#@cli : $ image.jpg rows -25%,50%
13478
13479#@cli scale2x
13480#@cli : Resize selected images using the Scale2x algorithm.
13481#@cli : $ image.jpg threshold 50% resize 50%,50% +scale2x
13482scale2x :
13483  e[^-1] "Double xy-dimensions of image$?, using Scale2x algorithm."
13484  repeat $! l[$>]
13485    r 200%,200%
13486    f "dx=x&1;dy=y&1;A=j(0,-2,0,0,0,1);B=j(2,0,0,0,0,1);C=j(-2,0,0,0,0,1);D=j(0,2,0,0,0,1);
13487       !dy*(!dx*if(C==A&&C!=D&&A!=B,A,i) + dx*if(A==B&&A!=C&&B!=D,B,i)) +
13488       dy*(dx*if(B==D&&B!=A&&D!=C,D,i) + !dx*if(D==C&&D!=B&&C!=A,C,i))"
13489  endl done
13490
13491#@cli scale3x
13492#@cli : Resize selected images using the Scale3x algorithm.
13493#@cli : $ image.jpg threshold 50% resize 33%,33% +scale3x
13494scale3x :
13495  e[^-1] "Triple xy-dimensions of image$?, using Scale3x algorithm."
13496  repeat $! l[$>]
13497    r 300%,300%
13498    f "dx=x%3;dy=y%3;c0=!dx;c1=(dx==1);c2=(dx==2);
13499       A=j(-3,-3,0,0,0,1);B=j(0,-3,0,0,0,1);C=j(3,-3,0,0,0,1);
13500       D=j(-3,0,0,0,0,1);F=j(3,0,0,0,0,1);
13501       G=j(-3,3,0,0,0,1);H=j(0,3,0,0,0,1);I=j(3,3,0,0,0,1);
13502       !dy*(c0*if(D==B&&D!=H&&B!=F,D,i) + c1*if((D==B&&D!=H&&B!=F&&i!=C)||(B==F&&B!=D&&F!=H&&i!=A),B,i) +
13503            c2*if(B==F&&B!=D&&F!=H,F,i)) +
13504       (dy==1)*(c0*if((H==D&&H!=F&&D!=B&&i!=A)||(D==B&&D!=H&&B!=F&&i!=G),D,i) + c1*i +
13505            c2*if((B==F&&B!=D&&F!=H&&i!=I)||(F==H&&F!=B&&H!=D&&i!=C),F,i)) +
13506       (dy==2)*(c0*if(H==D&&H!=F&&D!=B,D,i) + c1*if((F==H&&F!=B&&H!=D&&i!=G)||(H==D&&H!=F&&D!=B&&i!=I),H,i) +
13507            c2*if(F==H&&F!=B&&H!=D,F,i))"
13508  endl done
13509
13510#@cli scale_dcci2x : _edge_threshold>=0,_exponent>0,_extend_1px={ 0=false | 1=true }
13511#@cli : Double image size using directional cubic convolution interpolation,
13512#@cli : as described in <https://en.wikipedia.org/wiki/Directional_Cubic_Convolution_Interpolation>.
13513#@cli : Default values: 'edge_threshold=1.15', 'exponent=5' and 'extend_1px=0'.
13514#@cli : $ image.jpg +scale_dcci2x ,
13515scale_dcci2x : check "${1=1.15}>=0 && ${2=5}>=0" skip ${3=0}
13516  e[^-1] "Double xy-dimensions of image$?, using DCCI2x algorithm."
13517  repeat $! l[$>]
13518    r {2*w-(!$3)},{2*h-(!$3)},1,100%,4
13519
13520    # Estimate diagonal values.
13521    f "begin(
13522         const threshold = $1;
13523         const exponent = $2;
13524         interpolation = 0;
13525         boundary = 1;
13526         j1(x,y) = P[7*y + x + 24]; # eq. to 'j(x,y)', but faster
13527         j2(x,y) = P[7*x - y + 24]; # eq. to 'j(-y,x)', but faster
13528         interp(k) = -k#(-3,-3) + 9*k#(-1,-1) + 9*k#(1,1) - k#(3,3);
13529         d(k) = sum(abs([
13530                    k#(-1,-3) - k#(-3,-1), k#(1,-3) - k#(-1,-1), k#(3,-3) - k#(1,-1),
13531                    k#(-1,-1) - k#(-3,1), k#(1,-1) - k#(-1,1), k#(3,-1) - k#(1,1),
13532                    k#(-1,1) - k#(-3,3), k#(1,1) - k#(-1,3), k#(3,1) - k#(1,3)
13533                ]));
13534       );
13535       if (!((x*y)%2),i,
13536       ref(crop(x - 3,y - 3,0,c,7,7,1,1),P);
13537       d1 = d(j1);
13538       d2 = d(j2);
13539       ratio = (1 + d1)/(1 + d2);
13540       value = ratio>threshold ? interp(j1): # Up-right edge
13541               ratio<(1/threshold) ? interp(j2): # Down-right edge
13542               (w1 = 1/(1 + d1^exponent); w2 = 1/(1 + d2^exponent);
13543                (interp(j1)*w1 + interp(j2)*w2)/(w1 + w2)); # Smooth area
13544       value/=16)"
13545
13546    # Estimate remaining values.
13547    f "begin(
13548         const threshold = $1;
13549         const exponent = $2;
13550         interpolation = 0;
13551         boundary = 1;
13552         j1(x,y) = P[7*y + x + 24]; # eq. to 'j(x,y)', but faster
13553         j2(x,y) = P[7*x - y + 24]; # eq. to 'j(-y,x)', but faster
13554         interp(k) = -k#(0,-3) + 9*k#(0,-1) + 9*k#(0,1) - k#(0,3);
13555         d(k) = sum(abs([
13556                    k#(-1,-2) - k#(1,-2),
13557                    k#(-2,-1) - k#(0,-1), k#(0,-1) - k#(2,-1),
13558                    k#(-3,0) - k#(-1,0), k#(-1,0) - k#(1,0), k#(1,0) - k#(3,0),
13559                    k#(-2,1) - k#(0,1), k#(0,1) - k#(2,1),
13560                    k#(-1,2) - k#(1,2)
13561                ]));
13562       );
13563       if ((x%2) + (y%2)!=1,i,
13564         ref(crop(x - 3,y - 3,0,c,7,7,1,1),P);
13565         d1 = d(j1);
13566         d2 = d(j2);
13567         ratio = (1 + d1)/(1 + d2);
13568         value = ratio>threshold ? interp(j1) : # Horizontal edge
13569                 ratio<(1/threshold) ? interp(j2) : # Vertical edge
13570                 (w1 = 1/(1 + d1^exponent); w2 = 1/(1 + d2^exponent);
13571                  (interp(j1)*w1 + interp(j2)*w2)/(w1 + w2)); # Smooth area
13572         value/=16)"
13573  endl done
13574
13575#@cli seamcarve : _width[%]>=0,_height[%]>=0,_is_priority_channel={ 0 | 1 },_is_antialiasing={ 0 | 1 },\
13576# _maximum_seams[%]>=0
13577#@cli : Resize selected images with specified 2D geometry, using the seam-carving algorithm.
13578#@cli : Default values: 'height=100%', 'is_priority_channel=0', 'is_antialiasing=1' and 'maximum_seams=25%'.
13579#@cli : $ image.jpg seamcarve 60%
13580# The main code of this algorithm has been done by Andy (Garagecoder).
13581seamcarve : check "${2=100%}>=0 && ${5=25%}>=0" skip ${3=0},${4=1}
13582  e[^-1] "Resize image$? to $1x$2 using seam-carving algorithm, "${arg\ 1+!$3,with,without}" priority channel, "\
13583         ${arg\ 1+!$4,with,without}" anti-aliasing and maximum seams $5."
13584  repeat $! l[$>]
13585    nw={max(1,round(if(${is_percent\ $1},$1*w,$1)))}
13586    nh={max(1,round(if(${is_percent\ $2},$2*h,$2)))}
13587    if $nw!=w _seamcarve $nw,$3,$4,$5 fi
13588    if $nh!=h transpose _seamcarve $nh,$3,$4,$5 transpose fi
13589  endl done
13590
13591# Subroutine to remove/add vertical seams/
13592# $1 = desired width.
13593# $2 = is_priority_channel={ 0 | 1 }
13594# $3 = is_antialiasing={ 0 | 1 }
13595# $4 = max number of seams added/removed at once.
13596_seamcarve :
13597  do
13598    max_seams={max(1,round(if(${is_percent\ $4},$4*w,$4)))}
13599    ssms={max(min(round($1-w),w),1-w)}
13600    sms={min($max_seams,abs($ssms))}
13601
13602    # Compute potential map.
13603    if $2 s[0] c,{1-s} /. 256 fi
13604    +gradient[0] a[-2,-1] c abs. compose_channels. + n. 0,1
13605    if $2 +. .. a[0,1] c fi
13606
13607    # Add x-coordinates channel for anti-aliasing.
13608    if $3 100%,1,1,1,x r. [0],[0] a[0,-1] c fi
13609
13610    # Calculate low matrix (backwards propagation).
13611    .
13612    repeat h
13613      +rows. {$<+1} erode. 3
13614      j.. .,0,$<,0,0,-1 rm.
13615    done
13616
13617    # Initialise seams, top matrix.
13618    100%,100% +rows[1] 0
13619    nm[1] grad nm[2] low nm[3] seam nm[4] top
13620
13621    repeat h#0-1 nr={$>+1}
13622      +rows[low] $nr
13623
13624      # Find optimum matches between two 1D matrices.
13625      +*[4,5] +shift[4] 1 *. [5] +shift[5] 1 *. [4]
13626      +[-2,-1] j[5] [4] a[-3--1] c
13627      f. ">if(c,i,max(j(-1)+j(0,0,0,1),j(-2)+j(0,0,0,2)))"
13628      s. c shift... 1 +.. ... shift... 1 +[-3,-1]
13629      >[-2,-1] f. "<if(j(1)<0,1,-i)"
13630
13631      # Add matched row to seams.
13632      j[seam] .,0,$>
13633
13634      # Distribute matched pixels in top matrix.
13635      a[-2,-1] c f. "j(i,0,0,-1)" channels. 1
13636
13637      # Add next energy row to top matrix.
13638      +rows[grad] $nr +[top,-1]
13639    done
13640
13641    # Add / remove seams.
13642    max={iM*2} repeat $sms =. $max,{xm} done
13643    j[grad] .,0,100% rm[low,top] a[-2,-1] c
13644    f. "<if(c,i,j(j(0,0,0,1),1,0,0,0,1))" channels. 0
13645    +[0] 0.1 !=. $max
13646    w={w} h={h} s={0,s}
13647    if $ssms<0 * discard 0 r {$w-$sms},$h,1,$s,-1  # Remove seams.
13648    elif $ssms>0 # Add seams.
13649      -. 2 s[0] c
13650      repeat $s if $><($s-1) . fi a[$>,-1] c done
13651      permute cxyz a c discard -1 f "if(i<0,j(0,-1),i)"
13652      r {$w+$sms},$h,1,$s,-1
13653    fi
13654
13655    # Perform anti-aliasing step.
13656    if $3
13657      s c,{1-s} g. x,1 round !=. 1
13658      (0.5,0.5) +convolve[0] . rm..
13659      j[0] .,0,0,0,0,1,[1] rm[-2,-1]
13660    fi
13661
13662    rprogress {a=w/$1;if(a<1,a*100,100/a)}
13663  while w!=$1
13664
13665#@cli shift : vx[%],_vy[%],_vz[%],_vc[%],_boundary_conditions,_interpolation={ 0=nearest_neighbor | 1=linear } : (+)
13666#@cli : Shift selected images by specified displacement vector.
13667#@cli : Displacement vector can be non-integer in which case linear interpolation should be chosen.
13668#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13669#@cli : Default value: 'boundary_conditions=0' and 'interpolation=0'.
13670#@cli : $ image.jpg +shift[0] 50%,50%,0,0,0 +shift[0] 50%,50%,0,0,1 +shift[0] 50%,50%,0,0,2
13671
13672#@cli shrink_x : size_x>=0
13673#@cli : Shrink selected images along the x-axis.
13674#@cli : $ image.jpg shrink_x 30
13675shrink_x : check "$1>=0"
13676  e[^-1] "Shrink image$? along the x-axis with size $1."
13677  repeat $! z[$>] $1,{$>,w-$1-1} done
13678
13679#@cli shrink_xy : size>=0
13680#@cli : Shrink selected images along the xy-axes.
13681#@cli : $ image.jpg shrink_xy 30
13682shrink_xy : check "$1>=0"
13683  e[^-1] "Shrink image$? along the xy-axes with size $1."
13684  repeat $! z[$>] $1,$1,{$>,w-$1-1},{$>,h-$1-1} done
13685
13686#@cli shrink_xyz : size>=0
13687#@cli : Shrink selected images along the xyz-axes.
13688shrink_xyz : check "$1>=0"
13689  e[^-1] "Shrink image$? along the xyz-axes with size $1."
13690  repeat $! z[$>] $1,$1,$1,{$>,w-$1-1},{$>,h-$1-1},{$>,d-$1-1} done
13691
13692#@cli shrink_y : size_y>=0
13693#@cli : Shrink selected images along the y-axis.
13694#@cli : $ image.jpg shrink_y 30
13695shrink_y : check "$1>=0"
13696  e[^-1] "Shrink image$? along the y-axis with size $1."
13697  repeat $! z[$>] 0,$1,100%,{$>,h-$1-1} done
13698
13699#@cli shrink_z : size_z>=0
13700#@cli : Shrink selected images along the z-axis.
13701shrink_z : check "$1>=0"
13702  e[^-1] "Shrink image$? along the z-axis with size $1."
13703  repeat $! z[$>] 0,0,$1,100%,100%,{$>,d-$1-1} done
13704
13705#@cli slices : { [image0] | z0[%] },_{ [image1] | z1[%] } : (+)
13706#@cli : Keep only specified slices of selected images.
13707#@cli : Dirichlet boundary conditions are used when specified slices are out of range.
13708
13709#@cli sort : _ordering={ + | - },_axis={ x | y | z | c } : (+)
13710#@cli : Sort pixel values of selected images.
13711#@cli : If 'axis' is specified, the sorting is done according to the data of the first column/row/slice/channel
13712#@cli : of selected images.
13713#@cli : Default values: 'ordering=+' and 'axis=(undefined)'.
13714#@cli : $ 64 rand 0,100 +sort display_graph 400,300,3
13715
13716#@cli s : eq. to 'split'. : (+)
13717
13718#@cli split : { x | y | z | c }...{ x | y | z | c },_split_mode : \
13719# keep_splitting_values={ + | - },_{ x | y | z | c }...{ x | y | z | c },value1,_value2,... : (no arg) : (+)
13720#@cli : Split selected images along specified axes, or regarding to a sequence of scalar values
13721#@cli : (optionally along specified axes too).
13722#@cli : (eq. to 's').\n
13723#@cli : 'split_mode' can be { 0=split according to constant values | >0=split in N parts | \
13724# <0=split in parts of size -N }.
13725#@cli : Default value: 'split_mode=-1'.
13726#@cli : $ image.jpg split c
13727#@cli : $ image.jpg split y,3
13728#@cli : $ image.jpg split x,-128
13729#@cli : $ 1,20,1,1,"1,2,3,4" +split -,2,3 append[1--1] y
13730#@cli : $ (1,2,2,3,3,3,4,4,4,4) +split x,0 append[1--1] y
13731
13732#@cli split_tiles : M!=0,_N!=0,_is_homogeneous={ 0 | 1 }
13733#@cli : Split selected images as a MxN array of tiles.
13734#@cli : If M or N is negative, it stands for the tile size instead.
13735#@cli : Default values: 'N=M' and 'is_homogeneous=0'.
13736#@cli : $ image.jpg +local split_tiles 5,4 blur 3,0 sharpen 700 append_tiles 4,5 endlocal
13737split_tiles : skip ${2=$1},${3=0}
13738  if $3 e[^-1] "Split image$? as a $1x$2 array of homogeneous tiles."
13739  else e[^-1] "Split image$? as a $1x$2 array of tiles."
13740  fi
13741  repeat $! l[$<] s y,$2 s x,$1 if $3 r [0],[0],100%,100%,0 fi endl done
13742
13743#@cli undistort : -1<=_amplitude<=1,_aspect_ratio,_zoom,_center_x[%],_center_y[%],_boundary_conditions
13744#@cli : Correct barrel/pincushion distortions occurring with wide-angle lens.
13745#@cli : References:
13746#@cli : [1] Zhang Z. (1999). Flexible camera calibration by viewing a plane from unknown orientation.
13747#@cli : [2] Andrew W. Fitzgibbon (2001). Simultaneous linear estimation of multiple view geometry and lens distortion.
13748#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13749#@cli : Default values: 'amplitude=0.25', 'aspect_ratio=0', 'zoom=0', 'center_x=center_y=50%' \
13750# and 'boundary_conditions=0'.
13751undistort : check "${1=0.1}>=-1 && $1<=1 && ${6=0}>=0 && $6<=3" skip ${2=0},${3=0},${4=50%},${5=50%}
13752  e[^-1] "Undistort barrel/pincushion effect in image$?, with amplitude $1, aspect ratio $2, zoom factor $3,
13753          center ($4,$5) and "${"arg 1+$6,dirichlet,neumann,periodic,mirror"}" boundary conditions."
13754  repeat $! l[$>]
13755    center_x={${"is_percent $4"}?w*$4:$4}
13756    center_y={${"is_percent $5"}?h*$5:$5}
13757    f "
13758      const interpolation = 1;
13759      const boundary = $6;
13760      const center_x = "$center_x";
13761      const center_y = "$center_y";
13762      const alpha = cut($1,-0.999,0.999);
13763      const ratio = $2>=0?1+$2:1/(1-$2);
13764      const zoom = $3>=0?1+$3:1/(1-$3);
13765      const M = max(w,h);
13766      x = 2*(x - center_x)/(zoom*ratio*M);
13767      y = 2*(y - center_y)/(zoom*M);
13768      r = norm(x,y);
13769      nr = r/(1 - alpha*r^2);
13770      if (r>0,
13771        nx = nr/r*x; ny = nr/r*y,
13772        nx = x; ny = y
13773      );
13774      x = 0.5*nx*ratio*M + center_x;
13775      y = 0.5*ny*M + center_y;
13776      I(x,y)"
13777  endl done
13778
13779#@cli y : eq. to 'unroll'. : (+)
13780
13781#@cli unroll : _axis={ x | y | z | c } : (+)
13782#@cli : Unroll selected images along specified axis.
13783#@cli : (eq. to 'y').
13784#@cli : Default value: 'axis=y'.
13785#@cli : $ (1,2,3;4,5,6;7,8,9) +unroll y
13786
13787#@cli upscale_smart : width[%],_height[%],_depth,_smoothness>=0,_anisotropy=[0,1],sharpening>=0
13788#@cli : Upscale selected images with an edge-preserving algorithm.
13789#@cli : Default values: 'height=100%', 'depth=100%', 'smoothness=2', 'anisotropy=0.4' and 'sharpening=10'.
13790#@cli : $ image.jpg resize2dy 100 +upscale_smart 500%,500% append x
13791upscale_smart : skip ${2=100%},${3=100%} check "${4=2}>=0 && ${5=0.4}>=0 && $5<=1 && ${6=10}>=0"
13792  e[^-1] "Upscale image$? to $1x$2x$3, with smoothness $4, anisotropy $5 and sharpening $6."
13793  repeat $! l[$>]
13794    w={w} h={h}
13795    +r. $1,$2,$3,1,0 # Compute desired dimensions.
13796    if w<$w" && "h<$h # Test for downscaling
13797      rm. r. $1,$2,$3,100%,2
13798    else
13799      rm. +diffusiontensors 0,$5,1.2,1.2
13800      r[-2,-1] $1,$2,$3,100%,5
13801      smooth.. .,$4 rm.
13802      ac "sharpen. $6,10",ycbcr_y
13803    fi
13804  endl done
13805
13806#@cli warp : [warping_field],_mode,_interpolation,_boundary_conditions,_nb_frames>0 : (+)
13807#@cli : Warp selected images with specified displacement field.
13808#@cli : 'mode' can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=forward-relative }.
13809#@cli : 'interpolation' can be { 0=nearest-neighbor | 1=linear | 2=cubic }.
13810#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13811#@cli : Default values: 'mode=0', 'interpolation=1', 'boundary_conditions=1' and 'nb_frames=1'.
13812#@cli : $ image.jpg 100%,100%,1,2,'X=x/w-0.5;Y=y/h-0.5;R=(X*X+Y*Y)^0.5;A=atan2(Y,X);130*R*if(c==0,cos(4*A),sin(8*A))' \
13813# warp[-2] [-1],1,1,0 quiver[-1] [-1],10,1,1,1,100
13814#@cli : $$ https://gmic.eu/oldtutorial/_warp
13815
13816#@cli warp_patch : [warping_field],patch_width>=1,_patch_height>=1,_patch_depth>=1,_std_factor>0,_boundary_conditions.
13817#@cli : Patch-warp selected images, with specified 2D or 3D displacement field (in backward-absolute mode).
13818#@cli : Argument 'std_factor' sets the std of the gaussian weights for the patch overlap,
13819#@cli : equal to 'std = std_factor*patch_size'.
13820#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13821#@cli : Default values: 'std_factor=0.3' and 'boundary_conditions=3'.
13822warp_patch : check ${is_image_arg\ $1}" && isint(${2=3}) && $2>=1 && isint(${3=$2}) && $3>=1 &&
13823                    isint(${4=1}) && $4>=1 && isnum(${5=0.3}) && $5>0 && isint(${6=3}) && $6>=0 && $6<=3"
13824  e[^-1] "Patch-warp image$? with backward-absolute displacement field $1, using $2x$3x$4 patches, std factor $5
13825          and "${"arg 1+$6,dirichlet,neumann,periodic,mirror"}" boundary conditions."
13826  if $2<=1 pass$1 warp[^-1] .,0 rm. return fi
13827  repeat $! pass$1 l[$>,-1] nm={0,n}
13828    [0],[0],[0],1,1 a[0,-1] c # Add weighting channel
13829    100%,100%,100%,{0,s}
13830
13831    if s#1>=3 # 3D version
13832      eval[1] ">
13833        begin(
13834          const pw = $2;
13835          const ph = $3;
13836          const pd = $4;
13837          const stdf = $5;
13838          const boundary = $6;
13839          const pw1 = int(pw/2);
13840          const pw2 = pw - pw1 - 1;
13841          const ph1 = int(ph/2);
13842          const ph2 = ph - ph1 - 1;
13843          const pd1 = int(pd/2);
13844          const pd2 = pd - pd1 - 1;
13845          const pwhd = pw*ph*pd;
13846          return = vector(s);
13847
13848          # Pre-compute gaussian weights.
13849          if (stdf<5,
13850            ref(vectorpwhd(),weights);
13851            offw = 0;
13852            for (zw = -pd1, zw<=pd2, ++zw,
13853              for (yw = -ph1, yw<=ph2, ++yw,
13854                for (xw = -pw1, xw<=pw2, ++xw,
13855                  weights[offw++] = exp(-xw^2/(2*(stdf*pw)^2) - yw^2/(2*(stdf*ph)^2) - zw^2/(2*(stdf*pd)^2));
13856                );
13857              );
13858            );
13859          );
13860        );
13861        u = i(x,y,z,0);
13862        v = i(x,y,z,1);
13863        w = i(x,y,z,2);
13864        ref(crop(#0,u - pw1, v - ph1,w - pd1,pw,ph,pd,boundary),patch);
13865        stdf<5?
13866          draw(#2,patch,x - pw1,y - ph1,z - pd1,0,pw,ph,pd,s#0,-1,weights):
13867          draw(#2,patch,x - pw1,y - ph1,z - pd1,0,pw,ph,pd,s#0,-1);
13868        return"
13869    else # 2D version
13870      eval[1] ">
13871        begin(
13872          const pw = $2;
13873          const ph = $3;
13874          const stdf = $5;
13875          const boundary = $6;
13876
13877          const pw1 = int(pw/2);
13878          const pw2 = pw - pw1 - 1;
13879          const ph1 = int(ph/2);
13880          const ph2 = ph - ph1 - 1;
13881          const pwh = pw*ph;
13882          return = vector(s);
13883
13884          # Pre-compute gaussian weights.
13885          if (stdf<5,
13886            ref(vectorpwh(),weights);
13887            offw = 0;
13888            for (yw = -ph1, yw<=ph2, ++yw,
13889              for (xw = -pw1, xw<=pw2, ++xw,
13890                weights[offw++] = exp(-xw^2/(2*(stdf*pw)^2) - yw^2/(2*(stdf*ph)^2));
13891              );
13892            );
13893          );
13894        );
13895        u = i(x,y,z,0);
13896        v = i(x,y,z,1);
13897        ref(crop(#0,u - pw1, v - ph1,pw,ph,boundary),patch);
13898        stdf<5?
13899          draw(#2,patch,x - pw1,y - ph1,0,0,pw,ph,1,s#0,-1,weights):
13900          draw(#2,patch,x - pw1,y - ph1,0,0,pw,ph,1,s#0,-1);
13901        return"
13902    fi
13903    s. c,-{0,s-1} /[-2,-1] k. nm $nm
13904  endl done
13905
13906#@cli warp_rbf : xs0[%],ys0[%],xt0[%],yt0[%],...,xsN[%],ysN[%],xtN[%],ytN[%]
13907#@cli : Warp selected images using RBF-based interpolation.
13908#@cli : Each argument (xsk,ysk)-(xtk,ytk) corresponds to the coordinates of a keypoint
13909#@cli : respectively on the source and target images. The set of all keypoints define the overall image deformation.
13910#@cli : $ image.jpg +warp_rbf 0,0,0,0,100%,0,100%,0,100%,100%,100%,100%,0,100%,0,100%,50%,50%,70%,50%,25%,25%,25%,75%
13911warp_rbf :
13912  e[^-1] "Warp image$? using RBF interpolation, with keypoints ($*)."
13913  $=arg N={$#/4}
13914  if int($N)!=$N error[0--2] "Command 'warp_rbf': Wrong number of arguments ($#)." fi
13915  repeat $! l[$>]
13916    # Retrieve absolute keypoints coordinates.
13917    4,$N
13918    repeat wh a=${arg{1+$>}} isp=${"is_percent "$a} eval i[$>]=$isp?($>%2?w#0:h#0)*$a:$a done
13919    s. x,2 -. .. a[-2,-1] x permute. yzcx
13920
13921    # Generate warping field by RBF interpolation.
13922    rbf. {0,[w,h]} warp[0] .,1,1,3 rm.
13923  endl done
13924
13925#---------------------------------
13926#
13927#@cli :: Filtering
13928#
13929#---------------------------------
13930
13931#@cli bandpass : _min_freq[%],_max_freq[%]
13932#@cli : Apply bandpass filter to selected images.
13933#@cli : Default values: 'min_freq=0' and 'max_freq=20%'.
13934#@cli : $ image.jpg bandpass 1%,3%
13935#@cli : $$ https://gmic.eu/oldtutorial/_bandpass
13936bandpass : skip ${1=0},${2=20%}
13937  e[^-1] "Apply bandpass filter [$1,$2] to image$?."
13938  repeat $! l[$>]
13939    100%,100%,100% f. "sqrt((x/w-0.5)^2 + (y/h-0.5)^2 + (z/d-0.5)^2)"
13940    n. 0,1 ir. $1,$2 shift. {int(w/2)},{int(h/2)},{int(d/2)},0,2
13941    fft.. *... . *[-2,-1] ifft rm.
13942  endl done
13943
13944#@cli bilateral : [guide],std_deviation_s[%]>=0,std_deviation_r[%]>=0,_sampling_s>=0,_sampling_r>=0 : \
13945# std_deviation_s[%]>=0,std_deviation_r[%]>=0,_sampling_s>=0,_sampling_r>=0 : (+)
13946#@cli : Blur selected images by anisotropic (eventually joint/cross) bilateral filtering.
13947#@cli : If a guide image is provided, it is used for drive the smoothing filter.
13948#@cli : A guide image must be of the same xyz-size as the selected images.
13949#@cli : Set 'sampling' arguments to '0' for automatic adjustment.
13950#@cli : $ image.jpg repeat 5 bilateral 10,10 done
13951
13952#@cli b : eq. to 'blur'. : (+)
13953
13954#@cli blur : std_deviation>=0[%],_boundary_conditions,_kernel : \
13955# axes,std_deviation>=0[%],_boundary_conditions,_kernel : (+)
13956#@cli : Blur selected images by a deriche or gaussian filter (recursive implementation).
13957#@cli : (eq. to 'b').\n
13958#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann }.
13959#@cli : 'kernel' can be { 0=deriche | 1=gaussian }.
13960#@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }.
13961#@cli : Specifying one axis multiple times apply also the blur multiple times.
13962#@cli : Default values: 'boundary_conditions=1' and 'kernel=1'.
13963#@cli : $ image.jpg +blur 5,0 +blur[0] 5,1
13964#@cli : $ image.jpg +blur y,10%
13965#@cli : $$ https://gmic.eu/oldtutorial/_blur
13966
13967#@cli blur_angular : amplitude[%],_center_x[%],_center_y[%]
13968#@cli : Apply angular blur on selected images.
13969#@cli : Default values: 'center_x=center_y=50%'.
13970#@cli : $ image.jpg blur_angular 2%
13971#@cli : $$ https://gmic.eu/oldtutorial/_blur_angular
13972blur_angular : skip ${2=50%},${3=50%}
13973  e[^-1] "Apply angular blur on image$?, with amplitude $1 and center point ($2,$3)."
13974  euclidean2polar $2,$3,1.3,1
13975  repeat $! l[$>] 1,100% =. 1,50%,50% b. y,$1 convolve_fft.. . rm. endl done
13976  polar2euclidean $2,$3,1.3,1
13977
13978#@cli blur_bloom : _amplitude>=0,_ratio>=0,_nb_iter>=0,_blend_operator={ + | max | min },\
13979# _kernel={ 0=deriche | 1=gaussian | 2=box | 3=triangle | 4=quadratic },\
13980# _normalize_scales={ 0 | 1 },_axes
13981#@cli : Apply a bloom filter that blend multiple blur filters of different radii,
13982#@cli : resulting in a larger but sharper glare than a simple blur.
13983#@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }.
13984#@cli : Specifying one axis multiple times apply also the blur multiple times.
13985#@cli : Reference: Masaki Kawase, "Practical Implementation of High Dynamic Range Rendering", GDC 2004.
13986#@cli : Default values: 'amplitude=1', 'ratio=2', 'nb_iter=5', 'blend_operator=+', 'kernel=1', 'normalize_scales=0' \
13987# and 'axes=(all)'
13988#@cli : $ image.jpg blur_bloom ,
13989blur_bloom : check "${1=1}>=0 && ${2=2}>=0 && isint(${3=5}) && $3>=0 && isint(${5=1}) && $5>=0 && $5<=4 &&
13990                    isnum(${6=0})" skip "${4=+},${7=}"
13991  e[^-1] "Apply bloom effect on image$?, with amplitude $1, ratio $2, $3 iterations, blend operator '$4' and "\
13992         ${"arg 1+!$6,\"\",\"no \""}"scale normalization."
13993  if narg("$7") axes=$7, fi
13994  m "_bloom0 : b "$axes"$""1"
13995  m "_bloom1 : b "$axes"$""1,1,1"
13996  m "_bloom2 : boxfilter "$axes"{1+2*$""1},0,1"
13997  m "_bloom3 : boxfilter "$axes"{1+2*$""1},0,1,2"
13998  m "_bloom4 : boxfilter "$axes"{1+2*$""1},0,1,3"
13999  repeat $! l[$>] nm={n} mM={[im,iM]}
14000    [0] repeat $3 sigma={$1*($2^$>)} +_bloom$5[0] $sigma
14001    if $6 n. $mM fi
14002    -$4[1,-1]
14003    done
14004    n. $mM k. nm $nm
14005  endl done
14006  um _bloom0,_bloom1,_bloom2,_bloom3,_bloom4
14007
14008#@cli blur_linear : amplitude1[%],_amplitude2[%],_angle,_boundary_conditions={ 0=dirichlet | 1=neumann }
14009#@cli : Apply linear blur on selected images, with specified angle and amplitudes.
14010#@cli : Default values: 'amplitude2=0', 'angle=0' and 'boundary_conditions=1'.
14011#@cli : $ image.jpg blur_linear 10,0,45
14012#@cli : $$ https://gmic.eu/oldtutorial/_blur_linear
14013blur_linear : skip ${2=0},${3=0},${4=1}
14014  e[^-1] "Apply linear blur on image$?, with angle $3 deg. and amplitudes ($1,$2)."
14015  std1={if(${is_percent\ $1},$1*max(w,h),$1)}
14016  std2={if(${is_percent\ $2},$2*max(w,h),$2)}
14017  stdM={round(1.25*max($std1,$std2))}
14018  if $stdM<=0 return fi
14019  repeat $! l[$>]
14020    expand_xy $stdM,{$4!=0}
14021    {2*$stdM},{2*$stdM} gaussian. $1,$2,$3 normalize_sum.
14022    convolve_fft[0] [1] rm. shrink_xy $stdM
14023  endl done
14024
14025#@cli blur_radial : amplitude[%],_center_x[%],_center_y[%]
14026#@cli : Apply radial blur on selected images.
14027#@cli : Default values: 'center_x=center_y=50%'.
14028#@cli : $ image.jpg blur_radial 2%
14029#@cli : $$ https://gmic.eu/oldtutorial/_blur_radial
14030blur_radial : skip ${2=50%},${3=50%}
14031  e[^-1] "Apply radial blur on image$?, with amplitude $1 and center point ($2,$3)."
14032  euclidean2polar $2,$3,5,1 blur_x $1 polar2euclidean $2,$3,5,1
14033
14034#@cli blur_selective : sigma>=0,_edges>0,_nb_scales>0
14035#@cli : Blur selected images using selective gaussian scales.
14036#@cli : Default values: 'sigma=5', 'edges=0.5' and 'nb_scales=5'.
14037#@cli : $ image.jpg noise 20 cut 0,255 +local[-1] repeat 4 blur_selective , done endlocal
14038#@cli : $$ https://gmic.eu/oldtutorial/_blur_selective
14039blur_selective : check "${1=5}>=0 && ${2=0.5}>=0 && isint(${3=5}) && $3>0"
14040  e[^-1] "Blur image$? using $3 selective gaussian scales, with sigma $1 and edges $2."
14041  repeat $! l[$>] nm={0,n}
14042    +gradient_norm +. 1 ^. {-max(0.01,$2)} quantize. {$3+1},0,1 min. {$3-1} ri. ..
14043    repeat $3 +==. $> *. ... +[-2,-1] b.. {$1/($3+1)} done
14044  rm.. nm $nm endl done
14045
14046#@cli blur_x : amplitude[%]>=0,_boundary_conditions={ 0=dirichlet | 1=neumann }
14047#@cli : Blur selected images along the x-axis.
14048#@cli : Default value: 'boundary_conditions=1'.
14049#@cli : $ image.jpg +blur_x 6
14050#@cli : $$ https://gmic.eu/oldtutorial/_blur_x
14051blur_x : skip ${2=1}
14052  e[^-1] "Blur image$? along the x-axis, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
14053  deriche $1,0,x,$2
14054
14055#@cli blur_xy : amplitude_x[%],amplitude_y[%],_boundary_conditions={ 0=dirichlet | 1=neumann }
14056#@cli : Blur selected images along the X and Y axes.
14057#@cli : Default value: 'boundary_conditions=1'.
14058#@cli : $ image.jpg +blur_xy 6
14059#@cli : $$ https://gmic.eu/oldtutorial/_blur_y
14060blur_xy : skip ${2=$1},${3=1}
14061  e[^-1] "Blur image$? along the xy-axes, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
14062  deriche $1,0,x,$3 deriche $2,0,y,$3
14063
14064#@cli blur_xyz : amplitude_x[%],amplitude_y[%],amplitude_z,_boundary_conditions={ 0=dirichlet | 1=neumann }
14065#@cli : Blur selected images along the X, Y and Z axes.
14066#@cli : Default value: 'boundary_conditions=1'.
14067#@cli : $$ https://gmic.eu/oldtutorial/_blur_xyz
14068blur_xyz : skip ${4=1}
14069  e[^-1] "Blur image$? along the xyz-axes, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
14070  deriche $1,0,x,$4 deriche $2,0,y,$4 deriche $3,0,z,$4
14071
14072#@cli blur_y : amplitude[%]>=0,_boundary_conditions={ 0=dirichlet | 1=neumann }
14073#@cli : Blur selected images along the y-axis.
14074#@cli : Default value: 'boundary_conditions=1'.
14075#@cli : $ image.jpg +blur_y 6
14076#@cli : $$ https://gmic.eu/oldtutorial/_blur_y
14077blur_y : skip ${2=1}
14078  e[^-1] "Blur image$? along the y-axis, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
14079  deriche $1,0,y,$2
14080
14081#@cli blur_z : amplitude[%]>=0,_boundary_conditions={ 0=dirichlet | 1=neumann }
14082#@cli : Blur selected images along the z-axis.
14083#@cli : Default value: 'boundary_conditions=1'.
14084#@cli : $$ https://gmic.eu/oldtutorial/_blur_z
14085blur_z : skip ${2=1}
14086  e[^-1] "Blur image$? along the z-axis, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
14087  deriche $1,0,z,$2
14088
14089#@cli boxfilter : size>=0[%],_order,_boundary_conditions,_nb_iter>=0 : \
14090# axes,size>=0[%],_order,_boundary_conditions,_nb_iter>=0 : (+)
14091#@cli : Blur selected images by a box filter of specified size (fast recursive implementation).
14092#@cli : 'order' can be { 0=smooth | 1=1st-derivative | 2=2nd-derivative }.
14093#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann }.
14094#@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }.
14095#@cli : Specifying one axis multiple times apply also the blur multiple times.
14096#@cli : Default values: 'order=0', 'boundary_conditions=1' and 'nb_iter=1'.
14097#@cli : $ image.jpg +boxfilter 5%
14098#@cli : $ image.jpg +boxfilter y,3,1
14099
14100#@cli bump2normal
14101#@cli : Convert selected bumpmaps to normalmaps.
14102#@cli : $ 300,300 circle 50%,50%,128,1,1 blur 5% bump2normal
14103bump2normal :
14104  e[^-1] "Convert bumpmap$? to normalmap."
14105  repeat $! l[$>]
14106    channels 0 g xy,1 +f. 1 a c orientation
14107    * 127 + 128 round c 0,255
14108  endl done
14109
14110#@cli compose_freq
14111#@cli : Compose selected low and high frequency parts into new images.
14112#@cli : $ image.jpg split_freq 2% mirror[-1] x compose_freq
14113compose_freq :
14114  e[^-1] "Compose low and high frequency part$? into new images."
14115  repeat int($!/2) +[$>,{$>+1}] done
14116
14117#@cli convolve : [mask],_boundary_conditions,_is_normalized={ 0 | 1 },_channel_mode,\
14118# _xcenter,_ycenter,_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,\
14119# _xdilation,_ydilation,_zdilation,interpolation_type : (+)
14120#@cli : Convolve selected images by specified mask.
14121#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14122#@cli : 'channel_mode' can be { 0=sum input channels | 1=one-for-one | 2=expand }.
14123#@cli : 'interpolation_type' can be { 0=nearest-neighbor | 1=linear }.
14124#@cli : Default values: 'boundary_conditions=1', 'is_normalized=0', 'channel_mode=1', \
14125# 'xcenter=ycenter=zcenter=-1' (-1=centered), 'xstart=ystart=zstart=0', 'xend=yend=zend=-1' (-1=max coordinates), \
14126# 'xstride=ystride=zstride=1', 'xdilation=ydilation=zdilation=1' and 'interpolation_type=0'.
14127#@cli : $ image.jpg (0,1,0;1,-4,1;0,1,0) convolve[-2] [-1] keep[-2]
14128#@cli : $ image.jpg (0,1,0) resize[-1] 130,1,1,1,3 +convolve[0] [1]
14129#@cli : $$ https://gmic.eu/oldtutorial/_convolve
14130
14131#@cli convolve_fft : [mask],_boundary_conditions
14132#@cli : Convolve selected images with specified mask, in the fourier domain.
14133#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14134#@cli : $ image.jpg 100%,100% gaussian[-1] 20,1,45 +convolve_fft[0] [1]
14135convolve_fft : check ${is_image_arg\ $1}" && isin(${2=2},0,1,2,3)"
14136  e[^-1] "Convolve image$? with mask $1, in the fourier domain."
14137  pass$1 store. kernel
14138  repeat $! l[$>] if w
14139    w0,h0,d0={[w,h,d]}
14140    $kernel
14141    if $2!=2 r[0] {[w#0,h#0,d#0]+2*round([w#1>1?w#1:0,h#1>1?h#1:0,d#1>1?d#1:0]/2)},100%,0,$2,0.5,0.5 fi
14142    r ${-max_whd},100%,0,0,0.5,0.5 r 100%,100%,100%,${-max_s}
14143    fft[1] fft[0]
14144    +*[1,2] +*[0,3] +[-2,-1] *[1,3] *[0,2] -[0,1]
14145    ifft rm.
14146    shift {-int(([w,h,d]-1)/2)},0,2
14147    r $w0,$h0,$d0,100%,0,0,0.5,0.5
14148  fi endl done
14149
14150#@cli correlate : [mask],_boundary_conditions,_is_normalized={ 0 | 1 },_channel_mode,\
14151# _xcenter,_ycenter,_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,\
14152# _xdilation,_ydilation,_zdilation,interpolation_type : (+)
14153#@cli : Correlate selected images by specified mask.
14154#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14155#@cli : 'channel_mode' can be { 0=sum input channels | 1=one-for-one | 2=expand }.
14156#@cli : 'interpolation_type' can be { 0=nearest-neighbor | 1=linear }.
14157#@cli : Default values: 'boundary_conditions=1', 'is_normalized=0', 'channel_mode=1', \
14158# 'xcenter=ycenter=zcenter=-1' (-1=centered), 'xstart=ystart=zstart=0', 'xend=yend=zend=-1' (-1=max coordinates), \
14159# 'xstride=ystride=zstride=1', 'xdilation=ydilation=zdilation=1' and 'interpolation_type=0'.
14160#@cli : $ image.jpg (0,1,0;1,-4,1;0,1,0) correlate[-2] [-1] keep[-2]
14161#@cli : $ image.jpg +crop 40%,40%,60%,60% +correlate[0] [-1],0,1
14162
14163#@cli cross_correlation : [mask]
14164#@cli : Compute cross-correlation of selected images with specified mask.
14165#@cli : $ image.jpg +shift -30,-20 +cross_correlation[0] [1]
14166cross_correlation : check ${is_image_arg\ $1}
14167  e[^-1] "Compute cross-correlation of image$? with mask $1."
14168  repeat $! pass$1 0 l[$>,-1]
14169    norm fft.. fft. [-2,-1] *.. [-5] *. [-6]
14170    -[-2,-1] *[-5,-3] *[-3,-2] +[-3,-2] ifft rm.
14171  endl done
14172
14173#@cli curvature
14174#@cli : Compute isophote curvatures on selected images.
14175#@cli : $ image.jpg blur 10 curvature
14176curvature :
14177  e[^-1] "Compute isophote curvatures on image$?."
14178  repeat $! l[$>]
14179    if d==1
14180      +g xy,0 hessian... xxxyyy                          # ixx ixy iyy ix iy
14181      *... .. *[-4] . *[-4] -2                           # ixx -2iyixy ixiyy ix iy
14182      +[-4,-3] *... ..                                   # ixx -2ixiyixy+ix^2iyy ix iy
14183      sqr[-2,-1] *[-4] . +[-4,-3]                        # iy^2ixx-2ixiyixy+ix^2iyy ix^2 iy^2
14184      +[-2,-1] +. 0.1 ^. 1.5 /                           # (iy^2ixx+2ixiyixy+ix^2iyy)/(ix^2+iy^2)
14185    else
14186      +inn +gradient_norm.. laplacian...                 # inn+iee inn in
14187      -[-3,-2] +. 0.1 /[-2,-1]                           # iee in
14188      +inn. laplacian.. -                                # iee/in
14189    fi
14190  endl done
14191
14192#@cli dct : _{ x | y | z }...{ x | y | z } : (no arg)
14193#@cli : Compute the discrete cosine transform of selected images, optionally along the specified axes only.
14194#@cli : Output images are always evenly sized, so this command may change the size of the selected images.
14195#@cli : Default values: (no arg)
14196#@cli : See also: ''idct''.
14197#@cli : $ image.jpg +dct +idct[-1] abs[-2] +[-2] 1 log[-2]
14198#@cli : $$ https://gmic.eu/oldtutorial/_dct-and-idct
14199dct : skip ${1=0}
14200  ('"$1"')
14201  is_axes={im>=_'x'" && "iM<=_'z'}
14202  if $is_axes
14203    e[0--3] "Compute discrete cosine transform of image$? along axes '$1'."
14204    repeat w
14205      axis={i[$>]}
14206      if $axis==_'x' repeat $!-1 l[$>] if w>1 _dct fi endl done
14207      elif $axis==_'y' repeat $!-1 l[$>] if h>1 permute yxzc _dct permute yxzc fi endl done
14208      elif $axis==_'z' repeat $!-1 l[$>] if d>1 permute zxyc _dct permute yzxc fi endl done
14209      fi
14210    done
14211    rm.
14212  else
14213    rm.
14214    e[0--3] "Compute discrete cosine transform of image$?."
14215    noarg
14216    repeat $! l[$>]
14217      if w>1 _dct fi
14218      if h>1 permute yxzc _dct permute yxzc fi
14219      if d>1 permute zxyc _dct permute yzxc fi
14220    endl done
14221  fi
14222
14223# 1D direct transform (DCT-II) along the x-axis, for a single image.
14224_dct :
14225  if w%2 r {w+1},100%,100%,100%,0,1 fi
14226  s x l[1--1:2] a x mirror x endl mv[1] $! a x
14227  fft x
14228  100%,1,1,1,2*cos(-x*pi/(2*w)) *[0,2]
14229  100%,1,1,1,2*sin(-x*pi/(2*w)) *[1,2]
14230  -
14231  +z[0] 0,0 /. {sqrt(2)} j.. .,0,0,0 rm. * {sqrt(2/w)}  # Make the transform orthogonal.
14232
14233#@cli deblur : amplitude[%]>=0,_nb_iter>=0,_dt>=0,_regul>=0,_regul_type={ 0=Tikhonov | 1=meancurv. | 2=TV }
14234#@cli : Deblur image using a regularized Jansson-Van Cittert algorithm.
14235#@cli : Default values: 'nb_iter=10', 'dt=20', 'regul=0.7' and 'regul_type=1'.
14236#@cli : $ image.jpg blur 3 +deblur 3,40,20,0.01
14237deblur : check "${2=10}>=0 && ${3=20}>=0 && ${4=0.7}>=0" skip ${5=1}
14238  e[^-1] "Deblur image$? with a regularized Jansson-Van Cittert algorithm, with sigma $1, $2 iterations,
14239          time step $3 and regularization $4."
14240  repeat $! l[$>] nm={0,n}
14241    [0]
14242    repeat $2
14243      if $5>=2 +curvature.                         # TV regularization.
14244      elif $5>=1 +iee.                             # Meancurv. regularization.
14245      else +laplacian.                               # Tikhonov regularization.
14246      fi
14247      *. $4
14248      +b.. $1 -. [-4]                                # Data fidelity term.
14249      -[-2,-1]
14250      *. {$3/(0.0001+max(abs(im),abs(iM)))}          # Adaptive time step.
14251      +[-2,-1]                                       # Update image.
14252      done
14253    rm..
14254  nm $nm endl done
14255
14256#@cli deblur_goldmeinel : sigma>=0,_nb_iter>=0,_acceleration>=0,_kernel_type={ 0=deriche | 1=gaussian }.
14257#@cli : Deblur selected images using Gold-Meinel algorithm
14258#@cli : Default values: 'nb_iter=8', 'acceleration=1' and 'kernel_type=1'.
14259#@cli : $ image.jpg +blur 1 +deblur_goldmeinel[-1] 1
14260###### : (contribution from Jérôme Boulanger).
14261deblur_goldmeinel : check "$1>=0 && ${2=8}>=0 && ${3=1}>=0" skip ${4=1}
14262  e[^-1] "Deblur image$? using Gold-Meinel algorithm, with sigma $1, $2 iterations, acceleration $3 and "\
14263         ${arg\ 1+!$4,"",quasi-}"gaussian kernel."
14264  repeat $! l[$>]
14265    [0] repeat $2
14266      +b. $1,1,$4 +/[0,-1] rm.. ^. $3 *[-1,-2] # u *= f / Hu
14267    done rm[0]
14268  endl done
14269
14270#@cli deblur_richardsonlucy : sigma>=0, nb_iter>=0, _kernel_type={ 0=deriche | 1=gaussian }.
14271#@cli : Deblur selected images using Richardson-Lucy algorithm.
14272#@cli : Default values: 'nb_iter=50' and 'kernel_type=1'.
14273#@cli : $ image.jpg +blur 1 +deblur_richardsonlucy[-1] 1
14274###### : (contribution from Jérôme Boulanger).
14275deblur_richardsonlucy : check "$1>=0 && ${2=50}>=0" skip ${3=1}
14276  e[^-1] "Deblur image$? using Richardson-Lucy algorithm, with sigma $1, $2 iterations and "\
14277         ${arg\ 1+!$3,"",quasi-}"gaussian kernel."
14278  repeat $! l[$>]
14279    [0] repeat $2
14280      +b. $1,1,{$3!=0} max. 1e-6 +/[0,-1] rm.. b. $1,1,{$3!=0} *[-1,-2] # u *= H ( f / Hu )
14281    done rm[0]
14282  endl done
14283
14284#@cli deconvolve_fft :  [kernel],_regularization>=0
14285#@cli : Deconvolve selected images by specified mask in the fourier space.
14286#@cli : Default value: 'regularization>=0'.
14287#@cli : $ image.jpg +gaussian 5 +convolve_fft[0] [1] +deconvolve_fft[-1] [1]
14288deconvolve_fft : check ${is_image_arg\ $1}" && ${2=.001}>=0"
14289  e[^-1] "Deconvolve image$? with mask $1 and regularization $2, in the fourier domain."
14290  repeat $! pass$1 0 l[$>,-1]
14291    w2={0,int(w/2)} h2={0,int(h/2)} d2={0,int(d/2)}
14292    r[1] [0],[0],[0],1,0,0,0.5,0.5,0.5,0.5 shift[1] -$w2,-$h2,-$d2,0,2
14293    fft[0] fft[2]                 # a b a' b'
14294    +l[-1,-2] sqr + + $2 endl     # a b a' b' (a'^2+b'^2+eps)
14295    +*[-4] ...                    # a b a' b' (a'^2+b'^2) ba'
14296    +*[-6] ...                    # a b a' b' (a'^2+b'^2) ba' ab'
14297    -[-2,-1]                      # a b a' b' (a'^2+b'^2) ba'-ab'
14298    *[-6,-4]                      # aa' b b' (a'^2+b'^2) ba'-ab'
14299    *[-4,-3]                      # aa' bb' (a'^2+b'^2) ba'-ab'
14300    +[-4,-3]                      # aa'+bb' (a'^2+b'^2) ba'-ab'
14301    /. .. /[-3,-2]                # divide (aa'+bb') and (ba'-ab') by (a'^2+b'^2)
14302    ifft rm.
14303  endl done
14304
14305#@cli deinterlace : _method={ 0 | 1 }
14306#@cli : Deinterlace selected images ('method' can be { 0=standard or 1=motion-compensated }).
14307#@cli : Default value: 'method=0'.
14308#@cli : $ image.jpg +rotate 3,1,1,50%,50% resize 100%,50% resize 100%,200%,1,3,4 shift[-1] 0,1 add +deinterlace 1
14309deinterlace : skip ${1=0}
14310  e[^-1] "Deinterlace image$? with "${arg\ 1+!$1,motion-compensated,standard}" method."
14311  repeat $! l[$>]
14312    wh={w},{h}
14313    s y a[0--1:2] y a[^0] y ri.. .,0 r 100%,200%,1,100%,5
14314    if $1!=0 +displacement. ..,0.05 warp... .,1,1,1 rm. fi
14315    + / 2 c 0,255 r $wh
14316  endl done
14317
14318#@cli denoise : [guide],std_deviation_s[%]>=0,_std_deviation_r[%]>=0,_patch_size>0,_lookup_size>0,_smoothness,\
14319# _fast_approx={ 0 | 1 } : \
14320# std_deviation_s[%]>=0,_std_deviation_r[%]>=0,_patch_size>0,_lookup_size>0,_smoothness,\
14321# _fast_approx={ 0 | 1 } : (+)
14322#@cli : Denoise selected images by non-local patch averaging.
14323#@cli : Default values: 'std_deviation_p=10', 'patch_size=5', 'lookup_size=6' and 'smoothness=1'.
14324#@cli : $ image.jpg +denoise 5,5,8
14325
14326#@cli denoise_haar : _threshold>=0,_nb_scales>=0,_cycle_spinning>0
14327#@cli : Denoise selected images using haar-wavelet thresholding with cycle spinning.
14328#@cli : Set 'nb_scales==0' to automatically determine the optimal number of scales.
14329#@cli : Default values: 'threshold=1.4', 'nb_scale=0' and 'cycle_spinning=10'.
14330#@cli : $ image.jpg noise 20 cut 0,255 +denoise_haar[-1] 0.8
14331denoise_haar : check "${1=1.4}>=0 && isint(${2=0}) && $2>=0 && isint(${3=10}) && $3>0"
14332  e[^-1] "Denoise image$? using haar-wavelet thresholding, with threshold $1, "\
14333          ${arg\ 1+($2>0),auto,$2}" scales and $3 spinning cycles."
14334  repeat $! l[$>] nm={0,n}
14335    nb_scales={min(if($2,$2,32),int(log2(min(w,h))-1))}
14336    w={w} h={h} d={d} sigma=${-std_noise}
14337    r {round(w,2^($nb_scales+1),1)},{round(h,2^($nb_scales+1),1)},{if(d==1,1,round(d,2^($nb_scales+1),1))},100%,0,0
14338    +f 0
14339    repeat $3
14340      dx={round(u(0,{4*$nb_scales}))}
14341      dy={round(u(0,{4*$nb_scales}))}
14342      dz={if($d==1,0,round(u(0,{4*$nb_scales})))}
14343      +shift[0] $dx,$dy,$dz,0,2
14344      haar. $nb_scales
14345      threshold. {$1*$sigma},1
14346      ihaar. $nb_scales
14347      shift. {-$dx},{-$dy},{-$dz},0,2
14348      +[-2,-1]
14349    done
14350    rm[0] / $3 r $w,$h,$d,100%,0
14351  nm $nm endl done
14352
14353#@cli denoise_patchpca : _strength>=0,_patch_size>0,_lookup_size>0,_spatial_sampling>0
14354#@cli : Denoise selected images using the patch-pca algorithm.
14355#@cli : Default values: 'patch_size=7', 'lookup_size=11', 'details=1.8' and 'spatial_sampling=5'.
14356#@cli : $ image.jpg +noise 20 cut[-1] 0,255 +denoise_patchpca[-1] ,
14357denoise_patchpca : check "${1=1.8} && $1>=0 && isint(${2=7}) && $2>0 && isint(${3=11}) && $3>0 && isint(${4=5}) && $4>0"
14358  e[^-1] "Denoise image$? using patch-pca, with strength $1, patch size $2, lookup size $3 and spatial sampling $4."
14359  repeat $! l[$>] nm={n}
14360    N2={$2*$2} M2={$3*$3} stdnoise=${-std_noise}
14361    100%,100%,1,100% nm. aggreg
14362    100%,100% nm. weights
14363    f[0] "*
14364    begin(
14365      n1 = int($2/2); n2 = $2 - n1 - 1;
14366      m1 = int($3/2); m2 = $3 - m1 - 1;
14367      patch(x,y) = crop(x - n1,y - n1,0,c,$2,$2,1,1,1);
14368      ngauss(x) = exp(-x*x/(2*n1*n1));
14369      ref(vector"$N2"(0),zero);
14370      ref(vector"$N2"(0),mask);
14371      for (l = 0; q = -n1, q<=n2, ++q,
14372        for (p = -n1, p<=n2, ++p, mask[l++] = ngauss(p)*ngauss(q)
14373        )
14374      )
14375    );
14376
14377    if (!(x%$4) && !(y%$4),  # Sub-sampling
14378      ref(patch(x,y),X);
14379
14380      # Build correlation matrix for PCA.
14381      ref(vector"{$N2*$N2}"(0),M);
14382      for (q = -m1, q<=m2, ++q,
14383        for (p = -m1, p<=m2, ++p,
14384          ref(patch(x + p,y + q) - X,Xk);
14385          M += mul(Xk,Xk,"$N2");
14386        )
14387      );
14388      M/="$M2";
14389      eig = eig(M);
14390      lambda = sqrt(abs(eig[0,"$N2"]));
14391
14392      # Determine number of lambdas to keep and project neighboring patches.
14393      for (k = 0, k<size(lambda) && lambda[k]>=$1*"$stdnoise", ++k);
14394      Qt = eig["$N2","{$N2*$N2}"];
14395      Q = transpose(Qt,"$N2");
14396      for (q = -m1, q<=m2, ++q,
14397        for (p = -m1, p<=m2, ++p,
14398          pY = Qt*(patch(x + p,y + q) - X);
14399          copy(pY[k],zero[0],size(pY) - k);
14400          (Y = Q*pY)+=X;
14401          draw(#"$aggreg",Y,x + p - n1,y + q - n1,0,c,$2,$2,1,1,-1,mask);
14402          draw(#"$weights",mask,x + p - n1,y + q - n1,0,c,$2,$2,1,1,-1);
14403        )
14404      );
14405    0);0"
14406    max[weights] 0.01 /[aggreg,weights] k[aggreg] nm $nm
14407  endl done
14408
14409#@cli deriche : std_deviation>=0[%],order={ 0 | 1 | 2 },axis={ x | y | z | c },_boundary_conditions : (+)
14410#@cli : Apply Deriche recursive filter on selected images, along specified axis and with
14411#@cli : specified standard deviation, order and boundary conditions.
14412#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann }.
14413#@cli : Default value: 'boundary_conditions=1'.
14414#@cli : $ image.jpg deriche 3,1,x
14415#@cli : $ image.jpg +deriche 30,0,x deriche[-2] 30,0,y add
14416#@cli : $$ https://gmic.eu/oldtutorial/_deriche
14417
14418#@cli dilate : size>=0 : size_x>=0,size_y>=0,size_z>=0 : \
14419# [kernel],_boundary_conditions,_is_real={ 0=binary-mode | 1=real-mode } : (+)
14420#@cli : Dilate selected images by a rectangular or the specified structuring element.
14421#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann }.
14422#@cli : Default values: 'size_z=1', 'boundary_conditions=1' and 'is_real=0'.
14423#@cli : $ image.jpg +dilate 10
14424
14425#@cli dilate_circ : _size>=0,_boundary_conditions,_is_normalized={ 0 | 1 }
14426#@cli : Apply circular dilation of selected images by specified size.
14427#@cli : Default values: 'boundary_conditions=1' and 'is_normalized=0'.
14428#@cli : $ image.jpg +dilate_circ 7
14429dilate_circ : check $1>=0 skip ${2=1},${3=0}
14430  e[^-1] "Apply circular dilation of image$? by size $1, boundary conditions $2 and is_normalized $3."
14431  if $1<2 return fi
14432  shape_circle $1 dilate[^-1] .,$2,$3 rm.
14433
14434#@cli dilate_oct : _size>=0,_boundary_conditions,_is_normalized={ 0 | 1 }
14435#@cli : Apply octagonal dilation of selected images by specified size.
14436#@cli : Default values: 'boundary_conditions=1' and 'is_normalized=0'.
14437#@cli : $ image.jpg +dilate_oct 7
14438dilate_oct : check $1>=0 skip ${2=1},${3=0}
14439  e[^-1] "Apply octagonal dilation of image$? by size $1, boundary conditions $2 and is_normalized $3."
14440  if $1<2 return fi
14441  if $1&1 ss={$1} else ss={$1+1} fi
14442  i[0] (0,1,0;1,1,1;0,1,0) i[1] (1,1,1;1,1,1;1,1,1)
14443  repeat $!-2
14444    r={round(($ss-1)*sqrt(2)/(1+sqrt(2))/2)}
14445    q={round(($ss-1)/(1+sqrt(2))/2)}
14446    if $r>0 repeat $r dilate. [0],$2,$3 done fi
14447    if $q>0 repeat $q dilate. [1],$2,$3 done fi
14448  mv. 2 done rm[0,1]
14449
14450_kr_circle :
14451  if $1%2==0 2,2,1,1,1 else 1,1,1,1,1 fi r. $1,$1,1,1,0,0,0.5,0.5
14452  distance. 1 round. 0.5 ir. 0,{$1/2}
14453
14454_jf_circle :
14455  {round($1)},{round($1)}
14456  center={0.5*(w-1)}
14457  f. 'sqrt((x-$center)^2+(y-$center)^2)'
14458  if !(w%2)
14459    round. 0.0001,-1
14460    t1={sqrt(((round($1)-1)/2)^2+0.25)}
14461    t2={sqrt(((round($1)+1)/2)^2+0.25)}
14462    k={$1-round($1)+0.5}
14463    t={$t1+($t2-$t1)*$k}
14464    ir. 0,$t
14465  else ir. 0,{$1/2-0.25}
14466  fi
14467
14468#@cli dilate_threshold : size_x>=1,size_y>=1,size_z>=1,_threshold>=0,_boundary_conditions
14469#@cli : Dilate selected images in the (X,Y,Z,I) space.
14470#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann }.
14471#@cli : Default values: 'size_y=size_x', 'size_z=1', 'threshold=255' and 'boundary_conditions=1'.
14472dilate_threshold : check "isint($1) && $1>=1 && isint(${2=$1}) && $2>=1 && isint(${3=1}) && $3>=1 && ${4=255}>=0 &&
14473                          isint(${5=1}) && $5>=0"
14474  e[^-1] "Dilate image$? with mask $1x$2x$3, threshold $4 and "${arg\ $5,dirichlet,neumann}" boundary conditions."
14475  l[]
14476    dx1={int($1/2)} dx2={$1-$dx1-1}
14477    dy1={int($2/2)} dy2={$2-$dy1-1}
14478    dz1={int($3/2)} dz2={$3-$dz1-1}
14479    (-$dx1,$dx1) (-$dy1;$dy1) (-$dz1/$dz1) r $1,$2,$3,1,3 a c round r {w*h*d},3,1,1,-1 transpose.
14480    i.. 1,100%,1,1,254 1,100%,1,1,255 a x
14481    ('{^}') rm.. replace_str "254,","(v=j(" replace_str ",255",",0,0,$5);if(abs(v-i)<=$4,v,-1e20))" list={t}
14482    rm
14483  endl
14484  f 'max($list)'
14485
14486#@cli divergence
14487#@cli : Compute divergence of selected vector fields.
14488#@cli : $ image.jpg luminance +gradient append[-2,-1] c divergence[-1]
14489divergence :
14490  e[^-1] "Compute divergence of vector field$?."
14491  repeat $! l[$>]
14492    if s==1 g x,0
14493    elif s==2 s c g.. x,0 g. y,0 +
14494    elif s==3 s c g... x,0 g.. y,0 g. z,0 +
14495    else error[] "Command '$0': Cannot compute divergence of image ["$>"] (has "{s}">3 channels)."
14496    fi
14497  endl done
14498
14499#@cli dog : _sigma1>=0[%],_sigma2>=0[%]
14500#@cli : Compute difference of gaussian on selected images.
14501#@cli : Default values: 'sigma1=2%' and 'sigma2=3%'.
14502#@cli : $ image.jpg dog 2,3
14503dog : check "${1=2%}>=0 && ${2=3%}>=0"
14504  e[^-1] "Compute difference of gaussian on image$?, with standard deviations $1 and $2."
14505  repeat $! l[$>]
14506    [0] parallel "b[0] $1","b[1] $2" - abs
14507  endl done
14508
14509#@cli diffusiontensors : _sharpness>=0,0<=_anisotropy<=1,_alpha[%],_sigma[%],is_sqrt={ 0 | 1 }
14510#@cli : Compute the diffusion tensors of selected images for edge-preserving smoothing algorithms.
14511#@cli : Default values: 'sharpness=0.7', 'anisotropy=0.3', 'alpha=0.6', 'sigma=1.1' and 'is_sqrt=0'.
14512#@cli : $ image.jpg diffusiontensors 0.8 abs pow 0.2
14513#@cli : $$ https://gmic.eu/oldtutorial/_diffusiontensors
14514diffusiontensors : check "${1=0.7}>=0 && ${2=0.3}>=0 && $2<=1" skip ${3=0.6},${4=1.1},${5=0}
14515  e[^-1] "Compute diffusion tensors for image$?, with sharpness $1, anisotropy $2, alpha $3 and sigma $4."
14516  p1={if($5,0.5,1)*max($1,1e-2)}
14517  p2={$p1/(1e-7+1-$2)}
14518  b $3 n 0,255 structuretensors 0 b $4
14519  repeat $! l[$>]
14520    eigen max.. 0
14521    if s==2 s.. c +[-3,-2] +.. 1 +^.. -$p1 ^... -$p2 a[-3,-1] c                   # 2D
14522    else s.. c +[-4--2] +.. 1 +^.. -$p1 r. 100%,100%,100%,2 ^... -$p2 a[-3,-1] c  # 3D
14523    fi
14524    eigen2tensor
14525  endl done
14526
14527#@cli edges : _threshold[%]>=0
14528#@cli : Estimate contours of selected images.
14529#@cli : Default value: 'edges=15%'
14530#@cli : $ image.jpg +edges 15%
14531edges : skip ${1=15%}
14532  e[^-1] "Estimate image contours of image$?, with threshold $1."
14533  gradient_norm b 0.5 >= $1 distance 0 equalize negate c 30%,70% n 0,1
14534
14535#@cli erode : size>=0 : size_x>=0,size_y>=0,_size_z>=0 : \
14536# [kernel],_boundary_conditions,_is_real={ 0=binary-mode | 1=real-mode } : (+)
14537#@cli : Erode selected images by a rectangular or the specified structuring element.
14538#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann }.
14539#@cli : Default values: 'size_z=1', 'boundary_conditions=1' and 'is_real=0'.
14540#@cli : $ image.jpg +erode 10
14541
14542#@cli erode_circ : _size>=0,_boundary_conditions,_is_normalized={ 0 | 1 }
14543#@cli : Apply circular erosion of selected images by specified size.
14544#@cli : Default values: 'boundary_conditions=1' and 'is_normalized=0'.
14545#@cli : $ image.jpg +erode_circ 7
14546erode_circ : check $1>=0 skip ${2=1},${3=0}
14547  e[^-1] "Apply circular erosion of image$? by size $1, boundary conditions $2 and is_normalized $3."
14548  if $1<2 return fi
14549  shape_circle $1 erode[^-1] .,$2,$3 rm.
14550
14551#@cli erode_oct : _size>=0,_boundary_conditions,_is_normalized={ 0 | 1 }
14552#@cli : Apply octagonal erosion of selected images by specified size.
14553#@cli : Default values: 'boundary_conditions=1' and 'is_normalized=0'.
14554#@cli : $ image.jpg +erode_oct 7
14555erode_oct : check $1>=0 skip ${2=1},${3=0}
14556  e[^-1] "Apply octagonal erosion of image$? by size $1, boundary conditions $2 and is_normalized $3."
14557  if $1<2 return fi
14558  if $1&1 ss={$1} else ss={$1+1} fi
14559  i[0] (0,1,0;1,1,1;0,1,0) i[1] (1,1,1;1,1,1;1,1,1)
14560  repeat $!-2
14561    r={round(($ss-1)*sqrt(2)/(1+sqrt(2))/2)}
14562    q={round(($ss-1)/(1+sqrt(2))/2)}
14563    if $r>0 repeat $r erode. [0],$2,$3 done fi
14564    if $q>0 repeat $q erode. [1],$2,$3 done fi
14565  mv. 2 done rm[0,1]
14566
14567#@cli erode_threshold : size_x>=1,size_y>=1,size_z>=1,_threshold>=0,_boundary_conditions
14568#@cli : Erode selected images in the (X,Y,Z,I) space.
14569#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann }.
14570#@cli : Default values: 'size_y=size_x', 'size_z=1', 'threshold=255' and 'boundary_conditions=1'.
14571erode_threshold : check "isint($1) && $1>=1 && isint(${2=$1}) && $2>=1 && isint(${3=1}) && $3>=1 && ${4=255}>=0 &&
14572                         isint(${5=1}) && $5>=0"
14573  e[^-1] "Erode image$? with mask $1x$2x$3, threshold $4 and "${arg\ $5,dirichlet,neumann}" boundary conditions."
14574  l[]
14575    dx1={int($1/2)} dx2={$1-$dx1-1}
14576    dy1={int($2/2)} dy2={$2-$dy1-1}
14577    dz1={int($3/2)} dz2={$3-$dz1-1}
14578    (-$dx1,$dx1) (-$dy1;$dy1) (-$dz1/$dz1) r $1,$2,$3,1,3 a c round r {w*h*d},3,1,1,-1 transpose.
14579    i.. 1,100%,1,1,254 1,100%,1,1,255 a x
14580    ('{^}') rm.. replace_str "254,","(v=j(" replace_str ",255",",0,0,$5);if(abs(v-i)<=$4,v,1e20))" list={t}
14581    rm
14582  endl
14583  f 'min($list)'
14584
14585#@cli fft : _{ x | y | z }...{ x | y | z } : (+)
14586#@cli : Compute the direct fourier transform (real and imaginary parts) of selected images,
14587#@cli : optionally along the specified axes only.
14588#@cli : See also: ''ifft''.
14589#@cli : $ image.jpg luminance +fft append[-2,-1] c norm[-1] log[-1] shift[-1] 50%,50%,0,0,2
14590#@cli : $ image.jpg w2={int(w/2)} h2={int(h/2)} fft shift $w2,$h2,0,0,2 ellipse $w2,$h2,30,30,0,1,0 \
14591# shift -$w2,-$h2,0,0,2 ifft remove[-1]
14592#@cli : $$ https://gmic.eu/oldtutorial/_fft
14593
14594#@cli g : eq. to 'gradient'. : (+)
14595
14596#@cli gradient : { x | y | z }...{ x | y | z },_scheme : (no arg) : (+)
14597#@cli : Compute the gradient components (first derivatives) of selected images.
14598#@cli : (eq. to 'g').\n
14599#@cli : 'scheme' can be { -1=backward | 0=centered | 1=forward | 2=sobel | 3=rotation-invariant (default) | \
14600# 4=deriche | 5=vanvliet }.
14601#@cli : (no arg) compute all significant components.
14602#@cli : Default value: 'scheme=0'.
14603#@cli : $ image.jpg gradient
14604#@cli : $$ https://gmic.eu/oldtutorial/_gradient
14605
14606#@cli gradient_norm
14607#@cli : Compute gradient norm of selected images.
14608#@cli : $ image.jpg gradient_norm equalize
14609#@cli : $$ https://gmic.eu/oldtutorial/_gradient_norm
14610gradient_norm :
14611  e[^-1] "Compute gradient norm of image$?."
14612  repeat $! l[$>] g sqr s c + sqrt endl done
14613
14614#@cli gradient_orientation : _dimension={1,2,3}
14615#@cli : Compute N-d gradient orientation of selected images.
14616#@cli : Default value: 'dimension=3'.
14617#@cli : $ image.jpg +gradient_orientation 2
14618gradient_orientation : check "${1=3}==1 || $1==2 || $1==3"
14619  e[^-1] "Compute $1-d gradient orientation of image$?."
14620  repeat $! l[$<]
14621    if $1==1 g x +abs. +. 1e-8 -/
14622    elif $1==2 g xy +sqr +[-2,-1] +. 1e-8 sqrt. /... . /[-2,-1]
14623    else g xyz +sqr +[-3--1] +. 1e-8 sqrt. /[-4,-3] . /[-2,-1]
14624    fi
14625  endl done
14626
14627#@cli guided : [guide],radius[%]>=0,regularization[%]>=0 : radius[%]>=0,regularization[%]>=0 : (+)
14628#@cli : Blur selected images by guided image filtering.
14629#@cli : If a guide image is provided, it is used to drive the smoothing process.
14630#@cli : A guide image must be of the same xyz-size as the selected images.
14631#@cli : This command implements the filtering algorithm described in:
14632#@cli : He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering",
14633#@cli : IEEE Transactions on Pattern Analysis and Machine Intelligence, vol.35, no.6, pp.1397,1409, June 2013
14634#@cli : $ image.jpg +guided 5,400
14635
14636#@cli haar : scale>0
14637#@cli : Compute the direct haar multiscale wavelet transform of selected images.
14638#@cli : See also: ''ihaar''.
14639#@cli : $$ https://gmic.eu/oldtutorial/_haar
14640haar : check "isint(${1=1}) && $1>=0"
14641  e[^-1] "Compute haar transform of image$? with $1 scales."
14642  repeat $! l[$>]
14643    _haar
14644    repeat $1-1
14645      w={max(0,round(w/2^(1+$>))-1)}
14646      h={max(0,round(h/2^(1+$>))-1)}
14647      d={max(0,round(d/2^(1+$>))-1)}
14648      +z 0,0,0,$w,$h,$d _haar. j.. . rm.
14649    done
14650  endl done
14651
14652_haar : # Mono-scale direct haar transform.
14653  _haar_x _haar_y _haar_z
14654
14655_haar_x : # Direct haar transform along the x-axis.
14656  if w<=1 return fi
14657  if w%2 error[0--6] "Command 'haar': Invalid image width="{w}" (is not even)." fi
14658  +shift -1 r 50% +-[1] [0] +[0,1] / {sqrt(2)} a x
14659
14660_haar_y : # Direct haar transform along the y-axis.
14661  if h<=1 return fi
14662  if h%2 error[0--6] "Command 'haar': Invalid image height="{h}" (is not even)." fi
14663  +shift 0,-1 r 100%,50% +-[1] [0] +[0,1] / {sqrt(2)} a y
14664
14665_haar_z : # Direct haar transform along the z-axis.
14666  if d<=1 return fi
14667  if d%2 error[0--6] "Command 'haar': Invalid image depth="{h}" (is not even)." fi
14668  +shift 0,0,-1 r 100%,100%,50% +-[1] [0] +[0,1] / {sqrt(2)} a z
14669
14670#@cli heat_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 }
14671#@cli : Apply iterations of the heat flow on selected images.
14672#@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'.
14673#@cli : $ image.jpg +heat_flow 20
14674heat_flow : skip ${1=10},${2=30},${3=0}
14675  e[^-1] "Apply $1 iterations of the heat flow on image$?, with time step $2."
14676  pde_flow $1,$2,laplacian,$3
14677
14678#@cli hessian : { xx | xy | xz | yy | yz | zz }...{ xx | xy | xz | yy | yz | zz } : (no arg) : (+)
14679#@cli : Compute the hessian components (second derivatives) of selected images.
14680#@cli : (no arg) compute all significant components.
14681#@cli : $ image.jpg hessian
14682
14683#@cli idct : _{ x | y | z }...{ x | y | z } : (no arg)
14684#@cli : Compute the inverse discrete cosine transform of selected images, optionally along the specified axes only.
14685#@cli : Output images are always evenly sized, so this command may change the size of the selected images.
14686#@cli : (dct images obtained with the 'dct' command are evenly sized anyway).
14687#@cli : Default values: (no arg)
14688#@cli : See also: ''dct''.
14689#@cli : $$ https://gmic.eu/oldtutorial/_dct-and-idct
14690idct : skip ${1=0}
14691  ('"$1"')
14692  is_axes={im>=_'x'" && "iM<=_'z'}
14693  if $is_axes
14694    e[0--3] "Compute inverse discrete cosine transform of image$? along axes '$1'."
14695    repeat w
14696      axis={i[$>]}
14697      if $axis==_'x' repeat $!-1 l[$>] if w>1 _idct fi endl done
14698      elif $axis==_'y' repeat $!-1 l[$>] if h>1 permute yxzc _idct permute yxzc fi endl done
14699      elif $axis==_'z' repeat $!-1 l[$>] if d>1 permute zxyc _idct permute yzxc fi endl done
14700      fi
14701    done
14702    rm.
14703  else
14704    rm.
14705    e[0--3] "Compute inverse discrete cosine transform of image$?."
14706    noarg
14707    repeat $! l[$>]
14708      if w>1 _idct fi
14709      if h>1 permute yxzc _idct permute yxzc fi
14710      if d>1 permute zxyc _idct permute yzxc fi
14711    endl done
14712  fi
14713
14714# 1D inverse transform (DCT-III) along the x-axis, for a single image.
14715_idct :
14716  if w%2 r {w+1},100%,100%,100%,0,0 fi
14717  / {sqrt(2/w)} +z[0] 0,0 *. {sqrt(2)} j.. .,0,0,0 rm. # Make the transform orthogonal.
14718  +mirror x shift. 1 *. -1
14719  100%,1,1,1,cos(x*pi/(2*w))
14720  100%,1,1,1,sin(x*pi/(2*w))
14721  +*[0,3] +*[1,2] +[-2,-1]
14722  *[0,2] *[1,2] -[0,1]
14723  ifft x k[0] / 2
14724  s x,2 mirror. x
14725  r[0] 200%,100%,100%,100%,4,0,0
14726  r[1] 200%,100%,100%,100%,4,0,1
14727  +
14728
14729#@cli iee
14730#@cli : Compute gradient-orthogonal-directed 2nd derivative of image(s).
14731#@cli : $ image.jpg iee
14732iee :
14733  e[^-1] "Compute gradient-orthogonal-directed 2nd derivative of image$?."
14734  repeat $! l[$>]
14735    if d==1
14736      +g xy,0 hessian... xxxyyy      # ixx ixy iyy ix iy
14737      *... .. *[-4] . *[-4] -2       # ixx -2iyixy ixiyy ix iy
14738      +[-4,-3] *... ..               # ixx -2ixiyixy+ix^2iyy ix iy
14739      sqr[-2,-1] *[-4] . +[-4,-3]    # iy^2ixx-2ixiyixy+ix^2iyy ix^2 iy^2
14740      +[-2,-1] +. 1e-8 /             # (iy^2ixx+2ixiyixy+ix^2iyy)/(ix^2+iy^2)
14741    else
14742      +inn laplacian.. -
14743    fi
14744  endl done
14745
14746#@cli ifft : _{ x | y | z }...{ x | y | z } : (+)
14747#@cli : Compute the inverse fourier transform (real and imaginary parts) of selected images.
14748#@cli : optionally along the specified axes only.
14749#@cli : See also: ''fft''.
14750#@cli : $$ https://gmic.eu/oldtutorial/_fft
14751
14752#@cli ihaar : scale>0
14753#@cli : Compute the inverse haar multiscale wavelet transform of selected images.
14754#@cli : See also: ''haar''.
14755#@cli : $$ https://gmic.eu/oldtutorial/_haar
14756ihaar : check "isint(${1=1}) && $1>=0"
14757  e[^-1] "Compute inverse haar transform of image$? with $1 scales."
14758  repeat $! l[$>]
14759    repeat $1-1
14760      w={max(0,round(w/2^(1+$<))-1)}
14761      h={max(0,round(h/2^(1+$<))-1)}
14762      d={max(0,round(d/2^(1+$<))-1)}
14763      +z 0,0,0,$w,$h,$d _ihaar. j.. . rm.
14764    done
14765    _ihaar
14766  endl done
14767
14768_ihaar : # Mono-scale inverse haar transform.
14769  _ihaar_x _ihaar_y _ihaar_z
14770
14771_ihaar_x : # Inverse haar transform along the x-axis.
14772  if w<=1 return fi
14773  if w%2 error[0--6] "Command 'ihaar': Invalid image width="{w}" (is not even)." fi
14774  s x,2 r 200% (-1,1) *[-2,-1] + / {sqrt(2)}
14775
14776_ihaar_y : # Inverse haar transform along the y-axis.
14777  if h<=1 return fi
14778  if h%2 error "Command 'ihaar': Invalid image height="{h}" (is not even)." fi
14779  s y,2 r 100%,200% (-1;1) r. {-2,w} *[-2,-1] + / {sqrt(2)}
14780
14781_ihaar_z : # Inverse haar transform along the z-axis.
14782  if d<=1 return fi
14783  if d%2 error "Command 'ihaar': Invalid image depth="{h}" (is not even)." fi
14784  s z,2 r 100%,100%,200% (-1/1) r. {-2,w},{-2,h} *[-2,-1] + / {sqrt(2)}
14785
14786#@cli ilaplacian : { nb_iterations>0 | 0 },_[initial_estimate]
14787#@cli : Invert selected Laplacian images.
14788#@cli : If given 'nb_iterations' is '0', inversion is done in Fourier space (single iteration),
14789#@cli : otherwise, by applying 'nb_iterations' of a Laplacian-inversion PDE flow.
14790#@cli : Note that the resulting inversions are just estimation of possible/approximated solutions.
14791#@cli : Default values: 'nb_iterations=0' and '[initial_estimated]=(undefined)'.
14792#@cli : $ image.jpg +laplacian +ilaplacian[-1] 0
14793ilaplacian : check "${1=0}>=0" skip "${2=}"
14794  is_estimate=${"is_image_arg $2"} nb_iter={round($1)}
14795  if !$nb_iter # Inversion in Fourier space
14796    if $is_estimate
14797      e[0--4] "Invert Laplacian image$? in Fourier space, with initial estimate $2."
14798      pass$2 1 ia=${-average_colors} rm.
14799    else
14800      e[0--4] "Invert Laplacian image$? in Fourier space."
14801      ia=0
14802    fi
14803    repeat $! l[$>]
14804      fft 100%,100%,1,1,"2*(cos(x*2*pi/w) + cos(y*2*pi/h)) - 4" =. 1
14805      /[-3,-2] . rm.
14806      = 0 ifft rm.
14807    endl done + '"begin(S = resize(["$ia"],s,0)); S"'
14808
14809  else # Inversion with PDE-flow
14810    if $is_estimate
14811      e[0--4] "Invert Laplacian image$? using $1 iterations of PDE flow and
14812                   initial estimate $2."
14813      repeat $! pass$2 0 l[$>,-1]
14814        *[0] 5 i[1] (0,5,0;5,0,5;0,5,0)
14815        repeat $1 +convolve. [1] -. [0] +[-2,-1] /. 21 done k. # Optimized semi-implicit scheme, with dt=5
14816      endl done
14817    else
14818      e[0--4] "Invert Laplacian image$? using $1 iterations of PDE flow."
14819      repeat $! l[$>]
14820        * 5 (0,5,0;5,0,5;0,5,0) +f.. 0
14821        repeat $1 +convolve. [1] -. [0] +[-2,-1] /. 21 done k. # Optimized semi-implicit scheme, with dt=5
14822      endl done
14823    fi
14824  fi
14825
14826#@cli inn
14827#@cli : Compute gradient-directed 2nd derivative of image(s).
14828#@cli : $ image.jpg inn
14829inn :
14830  e[^-1] "Compute gradient-directed 2nd derivative of image$?."
14831  repeat $! l[$>]
14832    if d==1
14833      +g xy,0 hessian... xxxyyy  # ixx ixy iyy ix iy
14834      *[-5] .. *[-4] . *[-4] 2   # ixixx 2iyixy iyy ix iy
14835      +[-5,-4] *[-4] ..          # ix^2ixx+2ixiyixy iyy ix iy
14836      sqr[-2,-1] *... . +[-4,-3] # ix^2ixx+2ixiyixy+iy^2iyy ix^2 iy^2
14837      +[-2,-1] +. 1e-8 /         # (ix^2ixx+2ixiyixy+iy^2iyy)/(ix^2+iy^2)
14838    else
14839      +g xyz,0 hessian[-4] xxxyxzyyyzzz          # ixx ixy ixz iyy iyz izz ix iy iz
14840      *[-9] ... *[-8] .. *[-8] 2 *[-7] . *[-7] 2 # ixixx 2iyixy 2izixz iyy iyz izz ix iy iz
14841      +[-9--7] *[-7] ...                         # ix^2ixx+2ixiyixy+2ixizixy iyy iyz izz ix iy iz
14842      *[-6] .. *[-5] . *[-5] 2                   # ix^2ixx+2ixiyixy+2ixizixy iyiyy 2iziyz izz ix iy iz
14843      +[-6,-5] *[-5] .. +[-6,-5]                 # ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz izz ix iy iz
14844      sqr[-3--1] *[-4] . +[-5,-4]                # ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz+iz^2izz ix^2 iy^2 iz^2
14845      +[-3--1] +. 1e-8 /                         # (ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz+iz^2izz)/(ix^2+iy^2+iz^2)
14846    fi
14847  endl done
14848
14849#@cli inpaint : [mask] : [mask],0,_fast_method : \
14850# [mask],_patch_size>=1,_lookup_size>=1,_lookup_factor>=0,_lookup_increment!=0,_blend_size>=0,\
14851# 0<=_blend_threshold<=1,_blend_decay>=0,_blend_scales>=1,_is_blend_outer={ 0 | 1 } : (+)
14852#@cli : Inpaint selected images by specified mask.
14853#@cli : If no patch size (or 0) is specified, inpainting is done using a fast average or median algorithm.
14854#@cli : Otherwise, it used a patch-based reconstruction method, that can be very time consuming.
14855#@cli : 'fast_method' can be { 0=low-connectivity average | 1=high-connectivity average | 2=low-connectivity median | \
14856# 3=high-connectivity median }.
14857#@cli : Default values: 'patch_size=0', 'fast_method=1', 'lookup_size=22', 'lookup_factor=0.5', 'lookup_increment=1', \
14858# 'blend_size=0', 'blend_threshold=0', 'blend_decay=0.05', 'blend_scales=10' and 'is_blend_outer=1'.
14859#@cli : $ image.jpg 100%,100% ellipse 50%,50%,30,30,0,1,255 ellipse 20%,20%,30,10,0,1,255 +inpaint[-2] [-1] remove[-2]
14860#@cli : $ image.jpg 100%,100% circle 30%,30%,30,1,255,0,255 circle 70%,70%,50,1,255,0,255 \
14861# +inpaint[0] [1],5,15,0.5,1,9,0 remove[1]
14862
14863#@cli inpaint_pde : [mask],_nb_scales[%]>=0,_diffusion_type={ 0=isotropic | 1=delaunay-guided | \
14864# 2=edge-guided | 3=mask-guided },_diffusion_iter>=0
14865#@cli : Inpaint selected images by specified mask using a multiscale transport-diffusion algorithm.
14866#@cli : If 'diffusion type==3', non-zero values of the mask (e.g. a distance function) are used
14867#@cli : to guide the diffusion process.
14868#@cli : Default values: 'nb_scales=75%', 'diffusion_type=1' and 'diffusion_iter=20'.
14869#@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_pde[0] [1]
14870inpaint_pde : check ${is_image_arg\ $1}" && ${2=75%}>=0 && isint(${3=1}) && $3>=0 && $3<=3 && ${4=20}>=0"
14871  s0="isotropic" s1="delaunay-guided" s2="edge-guided" s3="mask-guided"
14872  e[^-1] "Inpaint image$? by mask $1, using a multiscale diffusion algorithm with $2 scales
14873              and $4 iterations of "${s$3}" diffusion."
14874  repeat $! nm={n} pass$1 l[$>,-1]
14875    nb_scalesM={ceil(log2(max(w,h,d)))}
14876    nb_scales={round(${"is_percent $2"}?$nb_scalesM*$2:$2)}
14877    nb_scales={max(1,min($nb_scales,$nb_scalesM))}
14878    nb_iter={max(5,$4)}
14879
14880    repeat $nb_scales
14881      {0,"S = 2^"$<"; round([ max(1,w/S), max(1,h/S), max(1,d/S), s ])"}
14882      100%,100%,100%
14883      eval[1] "const wl1 = w#-1 - 1;    const hl1 = h#-1 - 1;    const dl1 = d#-1 - 1;
14884               const w1 = max(1,w - 1); const h1 = max(1,h - 1); const d1 = max(1,d - 1);
14885               !i?(
14886                 X = round(x*wl1/w1); Y = round(y*hl1/h1); Z = round(z*dl1/d1);
14887                 I(#-2,X,Y,Z) += I(#0,x,y,z);
14888                 ++i(#-1,X,Y,Z)
14889               );I"
14890      +max. 1 /[-3,-1] !=. 0
14891
14892      if !$> # First scale: Initialize by value propagation
14893        im={-2,im} +-.. {$im-1} *. ..
14894        +distance.. 1 *. -1 watershed.. . rm.
14895        +. {$im-1} mv. -3
14896      fi
14897
14898      if $>>0" || "$nb_scales==1
14899
14900        # Apply diffusion iterations.
14901        ri... ..,3
14902        if $3==0 # Isotropic diffusion
14903          repeat $nb_iter j... ..,0,0,0,0,1,. b... 0.5 done
14904
14905        elif $3==1 # Delaunay-guided
14906          +distance. 1 100%,100%,100%,{d==1?2:3}
14907          eval.. "* # Apply specific gradient scheme for distance function
14908            const boundary = 1;
14909            maxabs(a,b) = (abs(a)>abs(b)?a:b);
14910            ix = maxabs(j(1) - i,i - j(-1));
14911            iy = maxabs(j(0,1) - i,i - j(0,-1));
14912            d>1?(
14913              iz = maxabs(j(0,0,1) - i,i - j(0,0,-1));
14914              copy(I(#-1),[ ix,iy,iz ],3,whd);
14915            ):copy(I(#-1),[ ix,iy ],2,whd)"
14916          rm.. orientation.
14917          repeat $nb_iter
14918            j[-4] ...,0,0,0,0,1,..
14919            +warp[-4] .,1,2,1 *.. -1 warp[-5] ..,1,2,1 +[-5,-1] /[-4] 2
14920          done
14921          rm.
14922
14923        elif $3==2 # Edge-guided
14924          repeat $nb_iter
14925            +diffusiontensors... 0,1,1.5,0.5
14926            j[-4] ...,0,0,0,0,1,..
14927            smooth[-4] .,1,10,0 rm.
14928          done
14929
14930        else # Mask-guided
14931          +r[1] .,2 g. a[-{d==1?2:3}--1] c orientation.
14932          repeat $nb_iter
14933            j[-4] ...,0,0,0,0,1,..
14934            +warp[-4] .,1,2,1 *.. -1 warp[-5] ..,1,2,1 +[-5,-1] /[-4] 2
14935          done rm.
14936        fi
14937        j... ..,0,0,0,0,1,.
14938      fi
14939      rm[-2,-1]
14940    done
14941    nm. $nm rv[0,-1] rm.
14942  endl rm. done
14943
14944#@cli inpaint_flow : [mask],_nb_global_iter>=0,_nb_local_iter>=0,_dt>0,_alpha>=0,_sigma>=0
14945#@cli : Apply iteration of the inpainting flow on selected images.
14946#@cli : Default values: 'nb_global_iter=10', 'nb_local_iter=100', 'dt=5', 'alpha=1' and 'sigma=3'.
14947#@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 inpaint_flow[0] [1]
14948inpaint_flow : check ${is_image_arg\ $1}" && ${2=10}>=0 && ${3=100}>=0 && ${4=5}>0 && ${5=1}>=0 && ${6=3}>=0"
14949  e[^-1] "Apply $2x$3 iterations of the inpainting flow on image$?, with mask $1, time step $4, alpha $5 and sigma $6."
14950  repeat $! pass$1 0 l[$>,-1]
14951    r. [0],[0],[0],1,0 inpaint.. [1]
14952    repeat $2
14953      progress {100*$>/($2-1)}
14954      +diffusiontensors.. 0,1,$5,$6,0 *. .. smooth... .,$3,$4,0 rm.
14955    done
14956    progress 100
14957  endl rm. done
14958
14959#@cli inpaint_holes : maximal_area[%]>=0,_tolerance>=0,_is_high_connectivity={ 0 | 1 }
14960#@cli : Inpaint all connected regions having an area less than specified value.
14961#@cli : Default values: 'maximal_area=4', 'tolerance=0' and 'is_high_connectivity=0'.
14962#@cli : $ image.jpg noise 5%,2 +inpaint_holes 8,40
14963inpaint_holes : check "${1=4}>=0 && ${2=0}>=0" skip ${3=0}
14964  e[^-1] "Inpaint holes with area less than $1 pixels in image$?, with tolerance $2 and "\
14965         ${arg\ 1+!$3,high,low}" connectivity."
14966  repeat $! l[$>]
14967    100%,100%,100%
14968    area={if(${is_percent\ $1},$1*w*h*d,$1)}
14969    repeat s#0 sh[0] $> +area. $2,$3 <=. $1 -|[1,-1] rm. done
14970    if im k[0] whd={w},{h},{d} r 1,1,1,100%,2 r $whd,100%
14971    else inpaint[0] [1],0,{2*!$2+!!$3} k[0] fi
14972  endl done
14973
14974#@cli inpaint_morpho : [mask]
14975#@cli : Inpaint selected images by specified mask using morphological operators.
14976#@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_morpho[0] [1]
14977inpaint_morpho : check ${is_image_arg\ $1}
14978  e[^-1] "Inpaint image$? by mask $1, using morphological operators."
14979  repeat $! pass$1 0 l[$>,-1]
14980    nm={0,n} im={0,im} iM={0,iM} im1={$im-1} iM1={$iM+1}
14981    channels. 0 ==. 0
14982    +f[0] $im1 j. [0],0,0,0,0,1,..
14983    do
14984      +dilate. 3
14985      replace.. $im1,$iM1
14986      erode.. 3
14987      replace.. $iM1,$im1
14988      +[-2,-1] /. 2
14989      j. ...,0,0,0,0,1,..
14990    while im==$im1
14991    k. nm $nm
14992  endl done
14993
14994#@cli inpaint_matchpatch : [mask],_nb_scales={ 0=auto | >0 },_patch_size>0,_nb_iterations_per_scale>0,\
14995# _blend_size>=0,_allow_outer_blending={ 0 | 1 },_is_already_initialized={ 0 | 1 }
14996#@cli : Inpaint selected images by specified binary mask, using a multi-scale matchpatch algorithm.
14997#@cli : Default values: 'nb_scales=0', 'patch_size=9', 'nb_iterations_per_scale=10', 'blend_size=5',\
14998# 'allow_outer_blending=1' and 'is_already_initialized=0'.
14999#@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_matchpatch[0] [1]
15000inpaint_matchpatch : check ${is_image_arg\ $1}"&& ${2=0}>=0 && isint(${3=9}) && $3>0 && isint(${4=10}) && $4>0 &&
15001                           isint(${5=5}) && $5>=0" skip ${6=1},${7=0}
15002  e[^-1] "Inpaint image$? with mask $1, using a multiscale patch-matching algorithm with "\
15003         ${"if $2 u \"$2 \" else u auto- fi"}\
15004         "scales, $3x$3 patches, $4 iterations per scale and blending size $5."
15005  repeat $! pass$1 0 l[$>,-1] nm={0,n}
15006
15007    # Init variables and images.
15008    nm={0,n} nm img,mask
15009    nb_scales={max(1,round(if($2,$2,log2(min(w,h)/16)),1,1))}
15010    visu_size=${fitscreen[]" "{0,[w,h,1]},25%,50%}
15011    slices[img] 0 r[mask] [img],[img],1,1,0 !=[mask] 0
15012    if !$7 inpaint_pde[img] [mask],75% fi # Quick first estimate.
15013    im={img,im} -[img] $im
15014
15015    first_iter=1 iter=0
15016    repeat $nb_scales
15017      scale={100*(0.5^$<)}
15018      e[] "> Process scale "{1+$>}"/"$nb_scales" -> "$scale%
15019      progress {100*$>/max(1,$nb_scales-1)}
15020
15021      # Compute image and mask at current scales.
15022      +r[img,mask] $scale%,$scale%,1,100%,2 nm[-2,-1] scaled_img,scaled_mask
15023      >=[scaled_mask] 0.95
15024      if {scaled_mask,!iM} rm[scaled_img,scaled_mask] continue fi # Skip scale (if too low).
15025      +f[scaled_img] -4096 +j[scaled_img] .,0,0,0,0,1,[scaled_mask] rm.. nm. scaled_reference
15026
15027      coef={0.5^($nb_scales-$iter)}
15028      patch_size={v=round(max(min($3,5),$3*$coef));v+(1-(v%2))}
15029      patch_size={min(w,h,$patch_size)}
15030      blend_size={v=if($5,round(max(3,$5*$coef)));v+(1-(v%2))}
15031      iter+=1
15032      ==[scaled_mask] 0
15033
15034      if $first_iter # First iteration.
15035
15036        # Estimate initial correspondence map.
15037        100%,100%,1,1,x +f. y mv[scaled_mask] $! a[-3--1] c
15038        matchpatch[scaled_img] [scaled_reference],$patch_size,$patch_size,1,4,4,0,0,.
15039        rm[scaled_reference,-1]
15040        nm. correspondence
15041        first_iter=0
15042
15043      else # Standard iteration.
15044
15045        # Upscale correspondence map from previous scale.
15046        *[correspondence] 2 r[correspondence] 200%,200%,1,2 r[correspondence] [scaled_img],[scaled_img],1,2,0,1
15047        100%,100%,1,1,x +f. y a[-2,-1] c
15048        f[scaled_mask] "*if(i,1,
15049                           upc = i(#"$correspondence",x-1,y,0,0); vpc = i(#"$correspondence",x-1,y,0,1);
15050                           ucp = i(#"$correspondence",x,y-1,0,0); vcp = i(#"$correspondence",x,y-1,0,1);
15051                           ucc = i(#"$correspondence",x,y,0,0); vcc = i(#"$correspondence",x,y,0,1);
15052                           i(#-1,x,y,0,0) = (ucc==upc && vcc==vpc)?upc + 1:ucc;
15053                           i(#-1,x,y,0,1) = (ucc==ucp && vcc==vcp)?vcp + 1:vcc;
15054                           0)"
15055        rm[correspondence] nm. correspondence
15056        a[correspondence] [scaled_mask],c
15057
15058        # Refine correspondence map iteratively with matchpatch.
15059        nbs1={max(1,$nb_scales-1)}
15060        nb_iter={round(max(1,$4*(($<+1)/$nbs1)^2))}
15061
15062        repeat $nb_iter
15063          _inpaint_matchpatch[scaled_img] [correspondence],[scaled_mask],$blend_size,$6
15064          +matchpatch[scaled_img] [scaled_reference],$patch_size,$patch_size,1,4,4,0,0,[correspondence]
15065          j[correspondence] . rm.
15066          if {*1} w1[scaled_img] $visu_size,0 fi
15067          if {*2} w2[correspondence] $visu_size,1 fi
15068        done
15069        rm[scaled_img,scaled_mask,scaled_reference] channels[correspondence] 0,1
15070      fi
15071
15072    done
15073    progress 100
15074
15075    # Generate final result.
15076    if $correspondence
15077      ==[mask] 0
15078      _inpaint_matchpatch[img] [correspondence],[mask],$5,$6
15079      rm[correspondence]
15080    fi
15081    +[img] $im
15082    nm[0] $nm
15083  endl rm[mask] done
15084
15085# _inpaint_matchpatch : [correspondence_map],[mask],blend_size>0,_allow_outer_blending={ 0 | 1 }
15086_inpaint_matchpatch :
15087  pass$1 1 pass$2 {!$3" || "!$4}
15088  if !$3
15089    warp[0] [1],0,0,1
15090  else
15091    if $4 erode. $3 fi
15092    f[0] "*begin(
15093      boundary = 1;
15094      const patch_size = $3;
15095      const p2 = int(patch_size/2);
15096      const p1 = patch_size - p2 - 1;
15097      avg = resize([0],s#0);
15098
15099      # Pre-compute gaussian kernel for patch blending.
15100      wpq = resize([0],patch_size^2);
15101      g = 0;
15102      for (q = -p1, q<=p2, ++q,
15103        for (p = -p1, p<=p2, ++p,
15104          wpq[g++] = exp(-(p^2 + q^2)/(2*(0.3*patch_size)^2));
15105        );
15106      );
15107    );
15108    if (i#2,I,
15109      g = 0;
15110      avg = 0;
15111      norm = 0;
15112      for (q = -p1, q<=p2, ++q,
15113        for (p = -p1, p<=p2, ++p,
15114          U = I(#1,x + p,y + q);
15115          w = wpq[g++];
15116          norm+=w;
15117          avg+=w*I(#0,U[0,2] - [p,q]);
15118        );
15119      );
15120      avg/norm)"
15121  fi
15122  k[0]
15123
15124# _inpaint_warping2d : fill-in zero-valued vectors in absolute 2d warping field (works even when 'spectrum>2').
15125_inpaint_warping2d :
15126  repeat $! l[$>]
15127    100%,100%,100%,2,"> begin(const S = s#0; zero0 = vectorS(); zero1 = [0,0]; N = 0); I(#-1)==zero0?zero1:[++N,1]"
15128    s. c distance. 1 *. -1
15129    watershed.. . rm.
15130
15131    # Propagate offsets in each distinct voronoi cell.
15132    repeat 2
15133      f.. ">i?I:( # Forward propagation
15134        nP = vectors();
15135        const sP = size(nP);
15136        r = i(#-1);
15137        (P = J(-1,-1))[0] && j(#-1,-1,-1)==r?(nP[0] = ++P[0]; nP[1] = ++P[1]; sP>2?copy(nP[2],P[2],sP-2)):
15138        (P = J(0,-1))[0] && j(#-1,0,-1)==r  ?(nP[0] = P[0];   nP[1] = ++P[1]; sP>2?copy(nP[2],P[2],sP-2)):
15139        (P = J(1,-1))[0] && j(#-1,1,-1)==r  ?(nP[0] = --P[0]; nP[1] = ++P[1]; sP>2?copy(nP[2],P[2],sP-2)):
15140        (P = J(-1,0))[0] && j(#-1,-1,0)==r  ?(nP[0] = ++P[0]; nP[1] = P[1]; sP>2?copy(nP[2],P[2],sP-2));
15141        nP)"
15142      f.. "<i?I:( # Backward propagation
15143        nP = vectors();
15144        const sP = size(nP);
15145        r = i(#-1);
15146        (P = J(1,1))[0] && j(#-1,1,1)==r  ?(nP[0] = --P[0]; nP[1] = --P[1]; sP>2?copy(nP[2],P[2],sP-2)):
15147        (P = J(0,1))[0] && j(#-1,0,1)==r  ?(nP[0] = P[0];   nP[1] = --P[1]; sP>2?copy(nP[2],P[2],sP-2)):
15148        (P = J(-1,1))[0] && j(#-1,-1,1)==r?(nP[0] = ++P[0]; nP[1] = --P[1]; sP>2?copy(nP[2],P[2],sP-2)):
15149        (P = J(1,0))[0] && j(#-1,1,0)==r  ?(nP[0] = --P[0]; nP[1] = P[1]; sP>2?copy(nP[2],P[2],sP-2));
15150        nP)"
15151    done
15152    rm.
15153  endl done
15154
15155#@cli kuwahara : size>0
15156#@cli : Apply Kuwahara filter of specified size on selected images.
15157#@cli : $ image.jpg kuwahara 9
15158kuwahara : check $1>0
15159  e[^-1] "Apply Kuwahara filter of size $1 on image$?."
15160  repeat $! l[$>]
15161    s={s}
15162    +dilate $1 compose_channels. min
15163    +erode[0] $1 compose_channels. max
15164    -[-2,-1]
15165    $1,1,1,1,{1/$1} convolve[0] . transpose. convolve[0] . rm.
15166    p={int($1/2)}
15167    a[-2,-1] c
15168    f "v1=i(x-"$p",y-"$p",0,"$s",0,1); \
15169       v2=i(x+"$p",y-"$p",0,"$s",0,1); \
15170       v3=i(x-"$p",y+"$p",0,"$s",0,1); \
15171       v4=i(x+"$p",y+"$p",0,"$s",0,1); \
15172       vm=min(v1,v2,v3,v4); \
15173       if(c>="$s",i, \
15174         if(vm==v1,i(x-"$p",y-"$p",0,c,0,1),
15175           if(vm==v2,i(x+"$p",y-"$p",0,c,0,1),
15176             if(vm==v3,i(x-"$p",y+"$p",0,c,0,1),
15177               i(x+"$p",y+"$p",0,c,0,1)))))"
15178    channels 0,{s-2}
15179  endl done
15180
15181#@cli laplacian
15182#@cli : Compute Laplacian of selected images.
15183#@cli : $ image.jpg laplacian
15184laplacian :
15185  e[^-1] "Compute Laplacian of image$?."
15186  repeat $! l[$>]
15187    hessian ${arg\ 1+(d==1),xxyyzz,xxyy} +
15188  endl done
15189
15190#@cli lic : _amplitude>0,_channels>0
15191#@cli : Render LIC representation of selected vector fields.
15192#@cli : Default values: 'amplitude=30' and 'channels=1'.
15193#@cli : $ 400,400,1,2,'if(c==0,x-w/2,y-h/2)' +lic 200,3 quiver[-2] [-2],10,1,1,1,255
15194lic : skip ${1=30},${2=1}
15195  e[^-1] "Render LIC representation of 2D vector field$?, with amplitude $1 and $2 channel(s)."
15196  repeat $! l[$>] nm={0,n}
15197     channels 0,1 / {max(abs(im),abs(iM))} vector2tensor
15198     100%,100%,100%,$2 rand. 0,255 smooth. ..,$1 rm..
15199     equalize
15200  nm $nm endl done
15201
15202#@cli map_tones : _threshold>=0,_gamma>=0,_smoothness>=0,nb_iter>=0
15203#@cli : Apply tone mapping operator on selected images, based on Poisson equation.
15204#@cli : Default values: 'threshold=0.1', 'gamma=0.8', 'smoothness=0.5' and 'nb_iter=30'.
15205#@cli : $ image.jpg +map_tones ,
15206map_tones : skip ${1=0.1},${2=0.8},${3=0.5},${4=30}
15207  e[^-1] "Apply tone mapping operator on image$?, with threshold $1, gamma $2, smoothness $3 and $4 iterations."
15208  repeat $! l[$>]
15209
15210    # Estimate target divergence for each channel.
15211    +l s c repeat $! l[$>]
15212      g xy,1 a c +norm orientation..
15213      m={im} M={iM} b. $3 n. $m,$M
15214      *. 'alpha=$1*iM;(alpha/(1e-10+i))*(i/(1e-10+alpha))^$2'
15215      * s c g.. x,-1 g. y,-1 +
15216    endl done a c * 0.25 endl
15217
15218   # Start Poisson-PDE iterations
15219    repeat $4 +laplacian.. *. 0.25 +. ... -. .. *. 800 +[-3,-1] /.. 801 c.. 0,255 done rm.
15220
15221  endl done
15222
15223#@cli map_tones_fast : _radius[%]>=0,_power>=0
15224#@cli : Apply fast tone mapping operator on selected images.
15225#@cli : Default values: 'radius=3%' and 'power=0.3'.
15226#@cli : $ image.jpg +map_tones_fast ,
15227map_tones_fast : check "${1=3%}>=0 && ${2=0.3}>=0"
15228  e[^-1] "Apply fast tone mapping operator on image$?, with radius $1 and power $2."
15229  repeat $! l[$>]
15230    +luminance b. $1 n 0,1
15231    +*. 2 -. 1 abs. *. {$2*log(10)} exp.
15232    <=.. 0.5 ri. ...
15233    +*... -1 +. 1 ^. .. *. -1 +. 1 *. ...
15234    ^[-4,-2] ==.. 0 *[-3,-2] +
15235  endl done n 0,255
15236
15237#@cli meancurvature_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 }
15238#@cli : Apply iterations of the mean curvature flow on selected images.
15239#@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'.
15240#@cli : $ image.jpg +meancurvature_flow 20
15241meancurvature_flow : skip ${1=10},${2=30},${3=0}
15242  e[^-1] "Apply $1 iterations of the mean curvature flow on image$?, with time step $2."
15243  pde_flow $1,$2,iee,$3
15244
15245#@cli median : size>=0,_threshold>0 : (+)
15246#@cli : Apply (opt. thresholded) median filter on selected images with structuring element size x size.
15247#@cli : $ image.jpg +median 5
15248
15249#@cli nlmeans : [guide],_patch_radius>0,_spatial_bandwidth>0,_tonal_bandwidth>0,_patch_measure_command : \
15250# _patch_radius>0,_spatial_bandwidth>0,_tonal_bandwidth>0,_patch_measure_command
15251#@cli : Apply non local means denoising of Buades et al, 2005. on selected images.
15252#@cli : The patch is a gaussian function of 'std_patch_radius'.
15253#@cli : The spatial kernel is a rectangle of radius 'spatial_bandwidth'.
15254#@cli : The tonal kernel is exponential (`exp(-d^2/_tonal_bandwidth^2)`)
15255#@cli : with `d` the euclidean distance between image patches.
15256#@cli : Default values: 'patch_radius=4', 'spatial_bandwidth=4', 'tonal_bandwidth=10' and 'patch_measure_command=-norm'.
15257#@cli : $ image.jpg +noise 10 nlmeans[-1] 4,4,{0.6*${-std_noise}}
15258nlmeans:
15259  if ${"is_image_arg $1"}
15260
15261    # Guided-filtering
15262    check "${2=4}>0 && ${3=4}>0 && ${4=10}>0" skip "${5=-norm}"
15263    e[^-1] "Apply non-local means denoising on image$?, with guide $1, patch size $2, spatial bandwidth $3,
15264            tonal bandwidth $4 and patch measure command '$5'."
15265    pass$1 0 l. $5 k[0] endl # [1] preprocessed image used to compute weights.
15266    repeat $!-1 l[$>,-1]
15267      100%,100%,100%,100%,{-1.0/($4*$4)} # [2] compute a scaling.
15268      nlmeans_core[0] [1],[2],$2,$3 rm. # Apply the NLM denoising with image 1 and 2 as parameter.
15269    endl done
15270    rm.
15271
15272  else
15273
15274    # Non-guided filtering
15275    check "${1=4}>0 && ${2=4}>0 && ${3=10}>0" skip "${4=-norm}"
15276    e[^-1] "Apply non-local means denoising on image$?, with patch size $1, spatial bandwidth $2,
15277            tonal bandwidth $3 and patch measure command '$4'."
15278    repeat $! l[$>]
15279      +l $4 k[0] endl # [1] preprocessed image used to compute weights.
15280      100%,100%,100%,100%,{-1.0/($3*$3)} # [2] compute a scaling.
15281      nlmeans_core[0] [1],[2],$1,$2 k[0] # Apply the NLM denoising with image 1 and 2 as parameter.
15282    endl done
15283  fi
15284
15285#@cli nlmeans_core: _reference_image,_scaling_map,_patch_radius>0,_spatial_bandwidth>0
15286#@cli : Apply non local means denoising using a image for weight and a map for scaling
15287nlmeans_core : check ${is_image_arg\ $1}" && "${is_image_arg\ $2}" && $3>0 && $4>0"
15288  e[^-1] "Apply non-local means denoising using weight images $1, scaling map $2, patch size $3 and
15289          spatial bandwidth $4."
15290  pass$1 0 pass$2 0
15291  repeat $!-2 l[$>,-1,-2]
15292    # [0] original, [1] weights, [2] scaling [3] sum(weights * patch), [4] sum(weights), [5] max(weights)
15293    100%,100%,100%,{0,s},0 100%,100%,100%,{1,s},0 100%,100%,100%,{1,s},1e-6
15294    if d#0==1
15295      repeat 2*$4+1 j={$>-$4} repeat 2*$4+1 i={$>-$4}
15296        if $i!=0||$j!=0
15297          # Compute shifted images [6] and weight [7]
15298          +shift[0,1] $i,$j,0,0,2 -[7] [1]
15299          sqr[7] b[7] $3 *[7] [2] exp[7]
15300          # Accumulate weights
15301          *[6] [7] max[5] [7] +[4,7] +[3,6]
15302        fi
15303      done done
15304    else
15305      repeat 2*$4+1 k={$>-$4} repeat 2*$4+1 j={$>-$4} repeat 2*$4+1 i={$>-$4}
15306        if $i!=0||$j!=0||$k!=0
15307          # Compute shifted images [6] and weight [7]
15308          +shift[0,1] $i,$j,0,0,2 -[7] [1]
15309          sqr[7] b[7] $3 *[7] [2] exp[7]
15310          # Accumulate weights
15311          *[6] [7] max[5] [7] +[4,7] +[3,6]
15312        fi
15313      done done done
15314    fi
15315    rm[1,2]
15316    *[0] [3] +[1,0] +[1,2] # Add central patch
15317    / # Normalize
15318  endl done
15319
15320#@cli normalize_local : _amplitude>=0,_radius>0,_n_smooth>=0[%],_a_smooth>=0[%],_is_cut={ 0 | 1 },_min=0,_max=255
15321#@cli : Normalize selected images locally.
15322#@cli : Default values: 'amplitude=3', 'radius=16', 'n_smooth=4%', 'a_smooth=2%', 'is_cut=1', 'min=0' and 'max=255'.
15323#@cli : $ image.jpg normalize_local 8,10
15324normalize_local :
15325  check "${1=3}>=0 && ${2=16}>0 && isbool(${5=1})" skip ${3=4%},${4=2%},${6=0},${7=255}
15326  e[^-1] "Normalize image$? locally, with amplitude $1, radius $2, neighborhood smoothness $3 and
15327          average smoothness $4."
15328  repeat $! l[$>]
15329    +l erode {2*$2+1} s c min endl
15330    +l.. dilate {2*$2+1} s c max endl
15331    +b... $4 b[-3,-2] $3
15332    +-.. ... +. 0.01 -[-5] [-4] /[-5,-1]
15333    *[-3,-2] {$1+1} *. -$1 +... . +[-2,-1]
15334    if $5 max.. $6 min. $7 fi
15335    -. .. *[-3,-1] +
15336    if $5 c $6,$7 fi
15337  endl done
15338
15339#@cli normalized_cross_correlation : [mask]
15340#@cli : Compute normalized cross-correlation of selected images with specified mask.
15341#@cli : $ image.jpg +shift -30,-20 +normalized_cross_correlation[0] [1]
15342normalized_cross_correlation : check ${is_image_arg\ $1}
15343  e[^-1] "Compute normalized cross-correlation of image$? with mask $1."
15344  pass$1 0 norm repeat $!-1 . l[$>,-1]
15345    fft.. fft. [-2,-1] *.. [-5] *. [-6]
15346    -[-2,-1] *[-5,-3] *[-3,-2] +[-3,-2] [-2,-1] a[-2,-1] c norm.
15347    /... . /[-2,-1] ifft rm.
15348  endl done rm.
15349
15350#@cli percentile : [mask],0<=_min_percentile[%]<=100,0<=_max_percentile[%]<=100.
15351#@cli : Apply percentile averaging filter to selected images.
15352#@cli : Default values: 'min_percentile=0' and 'max_percentile=100'.
15353#@cli : $ image.jpg shape_circle 11,11 +percentile[0] [1],25,75
15354percentile : check ${"is_image_arg $1"}" && inrange(${2=0},0,100) && inrange(${3=100},0,100) && $2<=$3"
15355  vmin,vmax={_[${"is_percent $2"}?100*$2:$2,${"is_percent $3"}?100*$3:$3]}
15356  e[^-1] "Apply percentile averaging filter to image$?, with mask $1, "\
15357         "min percentile "$vmin"% and max percentile "$vmax"%."
15358
15359  # Generate code for masking.
15360  pass$1 0 !=. 0 N={is} if !$N rm. return fi 128,$N
15361  eval.. ">
15362    begin(
15363      p = 0;
15364      const w2 = int(w/2);
15365      const h2 = int(h/2);
15366    );
15367    i?(
15368      out = string('N[',p,']=j(',x - w2,',',y - h2,');');
15369      copy(i(#-1,0,p++),out,size(out));
15370    )"
15371  discard. 0 code={t} rm[-2,-1]
15372
15373  # Apply filter.
15374  f "
15375    begin( N = vector"$N"() );
15376    const boundary = 1;
15377    const sS = size(N) - 1;
15378    const s0 = round(sS*"$vmin"%);
15379    const s1 = round(sS*"$vmax"%);
15380    const ds = 1 + s1 - s0;
15381    "$code"
15382    S = sort(N);
15383    res = 0; for (s = s0, s<=s1, ++s, res+=S[s]); res/=ds"
15384
15385#@cli peronamalik_flow : K_factor>0,_nb_iter>=0,_dt,_keep_sequence={ 0 | 1 }
15386#@cli : Apply iterations of the Perona-Malik flow on selected images.
15387#@cli : Default values: 'K_factor=20', 'nb_iter=5', 'dt=5' and 'keep_sequence=0'.
15388#@cli : $ image.jpg +heat_flow 20
15389peronamalik_flow : check "${1=20}>0 && ${2=5}>=0" skip ${3=5},${4=0}
15390  e[^-1] "Apply $2 iterations of the Perona-Malik flow on image$?, with K factor $1 and time step $3."
15391  m "_peronamalik_flow :
15392   +gradient xy,0 a[-2,-1] c norm. b. 0.8 /. $1 sqr. *. -1 exp. a[-2,-1] c
15393   f. '\"s1=s-1;
15394          C=i(x,y,z,s-1);
15395          if(c>=s1,0,
15396           (C+i(x+1,y,z,s-1,0,1))*(j(1,0,0,0,0,1)-i) -
15397           (C+i(x-1,y,z,s-1,0,1))*(i-j(-1,0,0,0,0,1)) +
15398           (C+i(x,y+1,z,s-1,0,1))*(j(0,1,0,0,0,1)-i) -
15399           (C+i(x,y-1,z,s-1,0,1))*(i-j(0,-1,0,0,0,1)))\"'"
15400  pde_flow $2,$3,_peronamalik_flow,$4
15401  um _peronamalik_flow
15402
15403#@cli phase_correlation : [destination]
15404#@cli : Estimate translation vector between selected source images and specified destination.
15405#@cli : $ image.jpg +shift -30,-20 +phase_correlation[0] [1] unroll[-1] y
15406phase_correlation : check ${"is_image_arg $1"}
15407  e[^-1] "Estimate shift between source image$? and destination $1."
15408  pass$1
15409  repeat $!-1
15410    normalized_cross_correlation[$>] .
15411    l[$>] eval "
15412      store([xM>=w/2?xM - w:xM,
15413             yM>=h/2?yM - h:yM,
15414             zM>=d/2?zM - d:zM]*=-1,'res',1,1,1,3)"
15415    endl
15416    $res nm. "[phase correlation]" rv[$>,-1] rm.
15417  done rm.
15418
15419#@cli pde_flow : _nb_iter>=0,_dt,_velocity_command,_keep_sequence={ 0 | 1 }
15420#@cli : Apply iterations of a generic PDE flow on selected images.
15421#@cli : Default values: 'nb_iter=10', 'dt=30', 'velocity_command=laplacian' and 'keep_sequence=0'.
15422#@cli : $ image.jpg +pde_flow 20
15423pde_flow : skip ${1=10},${2=30},${3=laplacian},${4=0}
15424  e[^-1] "Apply $1 iterations of the velocity flow '$3' on image$?, with time step $2."
15425  repeat $! l[$<]
15426    repeat $1
15427      +$3. *. {$2/(0.01+max(abs(im),abs(iM)))}
15428      if $4 +. .. else +[-2,-1] fi
15429    done
15430    if $4 rm[0] fi
15431    a x
15432  endl done
15433  if $4 s x,$1 fi
15434
15435#@cli periodize_poisson
15436#@cli : Periodize selected images using a Poisson solver in Fourier space.
15437#@cli : $ image.jpg +periodize_poisson array 2,2,2
15438periodize_poisson :
15439  e[^-1] "Periodize image$? using Poisson solver in Fourier space."
15440  repeat $! l[$>]
15441    s c repeat $! l[$>]
15442      mM={[im,iM]} sum={0,ia}
15443      laplacian ilaplacian 0 + $sum c $mM
15444    endl done a c
15445  endl done
15446
15447#@cli rbf : dx,_x0,_x1,_phi(r) : dx,dy,_x0,_y0,_x1,_y1,_phi(r) : dx,dy,dz,x0,y0,z0,x1,y1,z1,phi(r)
15448#@cli : Reconstruct 1D/2D or 3D image from selected sets of keypoints, by RBF-interpolation.
15449#@cli : A set of keypoints is represented by a vector-valued image, where each pixel represents a single keypoint.
15450#@cli : Vector components of a keypoint have the following meaning:
15451#@cli :   - For 1D reconstruction: [ x_k, f1(k),...fN(k) ].
15452#@cli :   - For 2D reconstruction: [ x_k,y_k, f1(k),...,fN(k) ].
15453#@cli :   - For 3D reconstruction: [ x_k,y_k,z_k, f1(k),...,fN(k) ].
15454#@cli : Values 'x_k','y_k' and 'z_k' are the spatial coordinates of keypoint 'k'.
15455#@cli : Values 'f1(k),..,fN(k)' are the 'N' components of the vector value of keypoint 'k'.
15456#@cli : The command reconstructs an image with specified size 'dx'x'dy'x'dz', with 'N' channels.
15457#@cli : Default values: 'x0=y0=z0=0', 'x1=dx-1', 'y1=dy-1', 'z1=dz-1', 'phi(r)=r^2*log(1e-5+r)'.
15458#@cli : $ sp colorful r2dx 400 100%,100% noise_poissondisk. 10 1,{is},1,5 \
15459# eval[-2] "begin(p=0);i?(I[#-1,p++]=[x,y,I(#0)])" to_rgb[1] mul[0,1] dilate_circ[0] 5 +rbf[-1] {0,[w,h]} c[-1] 0,255
15460#@cli : $ 32,1,1,5,u([400,400,255,255,255]) rbf 400,400 c 0,255
15461rbf :
15462  $=a d_phi_r=r^2*log(1e-5+r)
15463  if isin($#,1,3,4) # 1D reconstruction
15464    dx,x0,x1=$a1,{$#>1?[$a2,$a3]:[0,$a1-1]}
15465    phi_r={`$#>3?['$a4']:'$d_phi_r'`}
15466    check $dx>0
15467    e[^-1] "Reconstruct 1D image from keypoint set$?, with size "$dx", "\
15468     "from ("$x0") to ("$x1") and phi(r) = "$phi_r.
15469    repeat $! l[$>] nm={n} if !w $dx elif whd==1 channels. 1,100% r. $dx,1,1,100% else
15470      r 1,{whd},1,100%,-1 permute. cyzx
15471      $dx,1,1,{w-1},"*
15472        begin(
15473          phi(r) = ("$phi_r");
15474          ref(crop(#0,0,0,0,0,1,h#0,1,1),X);
15475          ref(crop(#0,1,0,0,0,s,h#0,1,1,1),F);
15476          ref(vector(#h#0^2),M);
15477          repeat (h#0,k,
15478            for (l = 0, l<=k, ++l,
15479              r = abs(X[k] - X[l]);
15480              M[k*h#0 + l] = M[l*h#0 + k] = phi(r);
15481            )
15482          );
15483          ref(solve(M,F,s),W);
15484          const fx = ("$x1-$x0")/(w-1);
15485        );
15486        ref(vectors(),res); x = (x - "$x0")*fx;
15487        repeat (h#0,k, r = abs(x - X[k]); res+=W[s*k,s]*phi(r))"
15488      k. nm $nm
15489    fi endl done
15490
15491  elif isin($#,2,6,7) # 2D reconstruction
15492    dx,dy,x0,y0,x1,y1=$a1,$a2,{$#>2?[$a3,$a4,$a5,$a6]:[0,0,[$a1,$a2]-1]}
15493    phi_r={`$#>6?['$a7']:'$d_phi_r'`}
15494    check $dx>0" && "$dy>0
15495    e[^-1] "Reconstruct 2D image from keypoint set$?, with size "$dx,$dy", "\
15496     "from ("$x0,$y0") to ("$x1,$y1") and phi(r) = "$phi_r.
15497    repeat $! l[$>] nm={n} if !w $dx,$dy elif whd==1 channels 2,100% r. $dx,$dy,1,100% else
15498      r 1,{whd},1,100%,-1 permute. cyzx
15499      $dx,$dy,1,{w-2},"*
15500        begin(
15501          phi(r) = ("$phi_r");
15502          ref(crop(#0,0,0,0,0,1,h#0,1,1),X);
15503          ref(crop(#0,1,0,0,0,1,h#0,1,1),Y);
15504          ref(crop(#0,2,0,0,0,s,h#0,1,1,1),F);
15505          ref(vector(#h#0^2),M);
15506          repeat (h#0,k,
15507            for (l = 0, l<=k, ++l,
15508              r = norm(X[k] - X[l],Y[k] - Y[l]);
15509              M[k*h#0 + l] = M[l*h#0 + k] = phi(r);
15510            )
15511          );
15512          ref(solve(M,F,s),W);
15513          const fx = ("$x1-$x0")/(w-1);
15514          const fy = ("$y1-$y0")/(h-1);
15515        );
15516        ref(vectors(),res); x = (x - "$x0")*fx; y = (y - "$y0")*fy;
15517        repeat (h#0,k, r = norm(x - X[k], y - Y[k]); res+=W[s*k,s]*phi(r))"
15518      k. nm $nm
15519    fi endl done
15520
15521  elif isin($#,3,9,10) # 3D reconstruction
15522    dx,dy,dz,x0,y0,z0,x1,y1,z1=$a1,$a2,$a3,{$#>3?[$a4,$a5,$a6,$a7,$a8,$a9]:[0,0,0,[$a1,$a2,$a3]-1]}
15523    phi_r={`$#>9?['$arg10']:'$d_phi_r'`}
15524    check $dx>0" && "$dy>0" && "$dz>0
15525    e[^-1] "Reconstruct 3D image from keypoint set$?, with size "$dx,$dy,$dz", "\
15526     "from ("$x0,$y0,$z0") to ("$x1,$y1,$z1") and phi(r) = "$phi_r.
15527    repeat $! l[$>] nm={n} if !w $dx,$dy,$dz elif whd==1 channels 3,100% r. $dx,$dy,$dz,100% else
15528      r 1,{whd},1,100%,-1 permute. cyzx
15529      $dx,$dy,$dz,{w-3},"*
15530        begin(
15531          phi(r) = ("$phi_r");
15532          ref(crop(#0,0,0,0,0,1,h#0,1,1),X);
15533          ref(crop(#0,1,0,0,0,1,h#0,1,1),Y);
15534          ref(crop(#0,2,0,0,0,1,h#0,1,1),Z);
15535          ref(crop(#0,3,0,0,0,s,h#0,1,1,1),F);
15536          ref(vector(#h#0^2),M);
15537          repeat (h#0,k,
15538            for (l = 0, l<=k, ++l,
15539              r = norm(X[k] - X[l],Y[k] - Y[l],Z[k] - Z[l]);
15540              M[k*h#0 + l] = M[l*h#0 + k] = phi(r);
15541            )
15542          );
15543          ref(solve(M,F,s),W);
15544          const fx = ("$x1-$x0")/(w - 1);
15545          const fy = ("$y1-$y0")/(h - 1);
15546          const fz = ("$z1-$z0")/(d - 1);
15547        );
15548        ref(vectors(),res); x = (x - "$x0")*fx; y = (y - "$y0")*fy; z = (z - "$z0")*fz;
15549        repeat (h#0,k, r = norm(x - X[k], y - Y[k], z - Z[k]); res+=W[s*k,s]*phi(r))"
15550      k. nm $nm
15551    fi endl done
15552
15553  else error[0--2] "Command 'rbf': invalid arguments '$*'."
15554  fi
15555
15556#@cli red_eye : 0<=_threshold<=100,_smoothness>=0,0<=attenuation<=1
15557#@cli : Attenuate red-eye effect in selected images.
15558#@cli : Default values: 'threshold=75', 'smoothness=3.5' and 'attenuation=0.1'.
15559#@cli : $ image.jpg +red_eye ,
15560red_eye : skip ${1=75},${2=3.5},${3=0.1}
15561  e[^-1] "Attenuate red-eye effect in image$?, with threshold $1, smoothness $2 and attenuation $3."
15562  to_rgb rgb2ycbcr repeat $! l[$>]
15563    s c -. 128 +>=. $1% b. $2 sqrt. *. -1 +. 1
15564    n. $3,1 *[-2,-1] +. 128 a c ycbcr2rgb
15565  endl done
15566
15567#@cli remove_hotpixels : _mask_size>0, _threshold[%]>0
15568#@cli : Remove hot pixels in selected images.
15569#@cli : Default values: 'mask_size=3' and 'threshold=10%'.
15570#@cli : $ image.jpg noise 10,2 +remove_hotpixels ,
15571remove_hotpixels : check ${1=3}>0 skip ${2=10%}
15572  e[^-1] "Remove hot pixels in image$?, with mask size $1 and threshold $2."
15573  repeat $! l[$>]
15574    +median $1 +- abs. >=. $2
15575    *.. . ==. 0 *[-3,-1] +
15576  endl done
15577
15578#@cli remove_pixels : number_of_pixels[%]>=0
15579#@cli : Remove specified number of pixels (i.e. set them to 0) from the set of non-zero pixels in selected images.
15580#@cli : $ image.jpg +remove_pixels 50%
15581remove_pixels : check "$1>=0"
15582  e[^-1] "Remove $1 of the non-zero pixels in image$?."
15583  repeat $! l[$>]
15584    +norm !=. 0
15585    N={is}                                      # Number of non-zero pixels.
15586    n={round(if(${"is_percent $1"},$N*$1,$1))} # Number of pixels to remove.
15587    if $n<=0 rm.        # No pixels to remove.
15588    elif $n>=$N rm. f 0 # All pixels to remove.
15589    elif $n>int($N/2)   # More pixels to remove than to keep.
15590      remove_pixels. {$N-$n} ==. 0 *
15591    else                       # Less pixels to remove than to keep.
15592      d={d} r 100%,{d*h},1,100%,-1  # Force image to be in 2D.
15593
15594      # Retrieve coordinates of all non-zero pixels.
15595      100%,1,1,1,x 1,{-2,h},1,1,y +[-2,-1] 1 r[-2,-1] ..,.
15596      *[-2,-1] ... rm...
15597      y[-2,-1] a[-2,-1] x discard. y,0
15598
15599      # Generate a 1xN vector with at least n non-zero pixels.
15600      do
15601        1,100%,1,1 rand. 0,{h} <=. {$n*1.25}
15602        if is>=$n break else rm. fi
15603      while 1
15604
15605      # Generate a 1xn vector of coordinates to 'remove'.
15606      r. 2 *[-2,-1] discard. y,0
15607      i.. 1,100% rand.. 0,1 a[-2,-1] x sort. +,y
15608      rows. 0,{$n-1} -. 1 z. 1,3
15609
15610      # Set those pixels to 0 using a 3D object.
15611      i.. ({'CImg3d'},{h},{h})
15612      1,100%,1,1,1 1,100%,1,1,y a[-2,-1] x
15613      3,100% 1,100%,1,1,1 y[-5--1] a[-5--1] y
15614      if s#0<=3 j3d.. .,0,0,0,1,0,0,0,0
15615      else [0],[0],1,1,1 j3d. ..,0,0,0,1,0,0,0,0 *[0,-1]
15616      fi
15617      rm.
15618
15619      r 100%,{h/$d},$d,100%,-1  # Resize to original dimension (eventually 3D).
15620    fi
15621  endl done
15622
15623#@cli rolling_guidance : std_deviation_s[%]>=0,std_deviation_r[%]>=0,_precision>=0
15624#@cli : Apply the rolling guidance filter on selected image.
15625#@cli : Rolling guidance filter is a fast image abstraction filter, described in:
15626#@cli : "Rolling Guidance Filter", Qi Zhang Xiaoyong, Shen Li, Xu Jiaya Jia, ECCV'2014.
15627#@cli : Default values: 'std_deviation_s=4', 'std_deviation_r=10' and 'precision=0.5'.
15628#@cli : $ image.jpg +rolling_guidance , +-
15629rolling_guidance : check "${1=4}>=0 && ${2=10}>=0 && ${3=0.5}>=0"
15630  e[^-1] "Apply rolling guidance filter on image$?, with standard deviations ($1,$2) and precision $3."
15631  precision={2^-$3}
15632  repeat $! l[$>]
15633    +b $1
15634    repeat 100
15635      if c>1 +norm. +bilateral... .,$1,$2 rm..
15636      else +bilateral.. .,$1,$2
15637      fi
15638      -.. . std={-2,sqrt(iv)} rm..
15639      if $std<$precision break fi
15640    done
15641    k.
15642  endl done
15643
15644#@cli sharpen : amplitude>=0 : amplitude>=0,edge>=0,_alpha,_sigma : (+)
15645#@cli : Sharpen selected images by inverse diffusion or shock filters methods.
15646#@cli : 'edge' must be specified to enable shock-filter method.
15647#@cli : Default values: 'alpha=0' and 'sigma=0'.
15648#@cli : $ image.jpg sharpen 300
15649#@cli : $ image.jpg blur 5 sharpen 300,1
15650
15651#@cli smooth : amplitude[%]>=0,_sharpness>=0,0<=_anisotropy<=1,_alpha[%],_sigma[%],_dl>0,_da>0,\
15652# _precision>0,_interpolation,_fast_approx={ 0 | 1 } : \
15653# nb_iterations>=0,_sharpness>=0,_anisotropy,_alpha,_sigma,_dt>0,0 : [tensor_field],_amplitude>=0,_dl>0,_da>0,\
15654# _precision>0,_interpolation,_fast_approx={ 0 | 1 } : \
15655# [tensor_field],_nb_iters>=0,_dt>0,0 : (+)
15656#@cli : Smooth selected images anisotropically using diffusion PDE's, with specified field of
15657#@cli : diffusion tensors.
15658#@cli : 'interpolation' can be { 0=nearest | 1=linear | 2=runge-kutta }.
15659#@cli : Default values: 'sharpness=0.7', 'anisotropy=0.3', 'alpha=0.6', 'sigma=1.1', 'dl=0.8', 'da=30', \
15660# 'precision=2', 'interpolation=0' and 'fast_approx=1'.
15661#@cli : $ image.jpg repeat 3 smooth 40,0,1,1,2 done
15662#@cli : $ image.jpg 100%,100%,1,2 rand[-1] -100,100 repeat 2 smooth[-1] 100,0.2,1,4,4 done warp[0] [-1],1,1
15663#@cli : $$ https://gmic.eu/oldtutorial/_smooth
15664
15665#@cli split_freq : smoothness>0[%]
15666#@cli : Split selected images into low and high frequency parts.
15667#@cli : $ image.jpg split_freq 2%
15668split_freq :
15669  e[^-1] "Split image$? into low and high frequency parts, with smoothness $1."
15670  repeat $! l[$>] +b $1 -[0] [1] rv endl done
15671
15672#@cli solve_poisson : "laplacian_command",_nb_iterations>=0,_time_step>0,_nb_scales>=0
15673#@cli : Solve Poisson equation so that applying 'laplacian[n]' is close to the result of 'laplacian_command[n]'.
15674#@cli : Solving is performed using a multi-scale gradient descent algorithm.
15675#@cli : If 'nb_scales=0', the number of scales is automatically determined.
15676#@cli : Default values: 'nb_iterations=60', 'dt=5' and 'nb_scales=0'.
15677#@cli : $ image.jpg command "foo : gradient x" +solve_poisson foo +foo[0] +laplacian[1]
15678solve_poisson : check "${2=60}>=0 && ${3=5}>0 && ${4=0}>=0"
15679  e[^-1] "Solve Poisson equation for image$?, for laplacian command '$1', with $2 iterations, time step $3 and "\
15680         ${arg\ 1+($4==0),$4,auto}" scales."
15681  repeat $! l[$>]
15682    [0]
15683    repeat if($4,$4,int(max(log2(max(w,h))-1,1)))
15684      f={2^$<}
15685      r[1] {0,max(1,w/$f)},{0,max(1,h/$f)},1,100%,3
15686      +ri[0] [1],2 l. -$1 k[0] endl
15687      repeat $2 +laplacian.. -. .. *. {$3/max(1e-8,abs(im),abs(iM))} +[-3,-1] done
15688      rm.
15689    done
15690    rm[0]
15691  endl done
15692
15693#@cli split_details : _nb_scales>0,_base_scale[%]>=0,_detail_scale[%]>=0
15694#@cli : Split selected images into 'nb_scales' detail scales.
15695#@cli : If 'base_scale'=='detail_scale'==0, the image decomposition is done with 'a trous' wavelets.
15696#@cli : Otherwise, it uses laplacian pyramids with linear standard deviations.
15697#@cli : Default values: 'nb_scales=4', 'base_scale=0' and 'detail_scale=0'.
15698#@cli : $ image.jpg split_details ,
15699split_details : check "isint(${1=4}) && $1>0 && ${2=0}>=0 && ${3=0}>=0"
15700  if ($2)==0" && "($3)==0
15701    e[^-1] "Split image$? using $1 spatial scales and 'a trous' wavelets."
15702    repeat $! l[$<]
15703      repeat $1-1
15704        +f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2);
15705              i(x - d2) + i(x + d2) + 4*i(x - d) + 4*i(x + d) + 6*i;"
15706        /. 16
15707        if h>1
15708          f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2);
15709               i(x,y - d2) + i(x,y + d2) + 4*i(x,y - d) + 4*i(x,y + d) + 6*i;"
15710          /. 16
15711        fi
15712        if d>1
15713          f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2);
15714               i(x,y,z - d2) + i(x,y,z + d2) + 4*i(x,y,z - d) + 4*i(x,y,z + d) + 6*i;"
15715          /. 16
15716        fi
15717        -.. .
15718      done rv
15719    endl done
15720  else
15721    e[^-1] "Split image$? using $1 spatial scales with base scale $2 and detail scale $3."
15722    repeat $! l[$<]
15723      ss={max(0.3,if(${is_percent\ $2},$2*max(w,h),$2))}
15724      se={max(0.3,if(${is_percent\ $3},$3*max(w,h),$3))}
15725      ds={$se-$ss}
15726      repeat $1-1 +b. {$ss+$>*$ds/max(1,$1-2)} -.. . rv[-2,-1] done
15727    endl done
15728  fi
15729
15730#@cli structuretensors : _scheme={ 0=centered | 1=forward/backward } : (+)
15731#@cli : Compute the structure tensor field of selected images.
15732#@cli : Default value: 'scheme=1'.
15733#@cli : $ image.jpg structuretensors abs pow 0.2
15734#@cli : $$ https://gmic.eu/oldtutorial/_structuretensors
15735
15736#@cli solidify : _smoothness[%]>=0,_diffusion_type={ 0=isotropic | 1=delaunay-oriented | 2=edge-oriented },\
15737# _diffusion_iter>=0
15738#@cli : Solidify selected transparent images.
15739#@cli : Default values: 'smoothness=75%', 'diffusion_type=1' and 'diffusion_iter=20'.
15740#@cli : $ image.jpg 100%,100% circle[-1] 50%,50%,25%,1,255 append c +solidify , display_rgba
15741solidify : check "${1=75%}>=0 && isint(${2=1}) && $2>=0 && $2<=2 && ${3=20}>=0"
15742  s0="isotropic" s1="delaunay-oriented" s2="edge-oriented"
15743  e[^-1] "Solidify transparent image$? with smoothness $1 and $3 iterations of "${s$2}" diffusion."
15744  repeat $! l[$>] split_opacity
15745    if $!>1 <=. 128 inpaint_pde.. [1],${1-3} rm. c 0,255 fi
15746  endl done
15747
15748#@cli syntexturize : _width[%]>0,_height[%]>0
15749#@cli : Resynthetize 'width'x'height' versions of selected micro-textures by phase randomization.
15750#@cli : The texture synthesis algorithm is a straightforward implementation of the method described in :
15751#@cli : <http://www.ipol.im/pub/art/2011/ggm_rpn/>.
15752#@cli : Default values: 'width=height=100%'.
15753#@cli : $ image.jpg crop 2,282,50,328 +syntexturize 320,320
15754syntexturize : check "${1=100%}>0 && ${2=$1}>0"
15755  e[^-1] "Resynthetize $1x$2 versions of texture$? by phase randomization."
15756  repeat $! l[$>]
15757
15758    # Prepare input image data.
15759    mM={[im,iM]} repeat s sh. $> sum$>={is} var$>={iv} rm. done  # Retrieve some stats for post-normalization.
15760    nw={if(${is_percent\ $1},$1*w,$1)}
15761    nh={if(${is_percent\ $2},$2*h,$2)}
15762    repeat s sum$>*={$nw*$nh/(w*h)} done # Re-estimate output (0,0) frequency.
15763
15764    if $nw>w||$nh>h # Spot extension required when rendering on bigger image.
15765      periodize_poisson
15766      100%,100% rectangle. 5,5,{w-6},{h-6},1,1 b. 2 n. 0,1
15767      $nw,$nh,1,{-2,s} fc. ${average_colors...}
15768      j. ...,{(w-{-2,w})/2},{(h-{-2,h})/2},0,0,1,..
15769      rm[-3,-2]
15770    else
15771      r $nw,$nh,1,100%,0,0,0.5,0.5
15772      periodize_poisson
15773    fi
15774    fft
15775
15776    # Compute coherent random phase.
15777    100%,100% rand. {-pi},{pi}
15778    =. 0
15779    if !(w%2) =. {(u<0.5)*pi},{int(w/2)} fi
15780    if !(h%2) =. {(u<0.5)*pi},0,{int(h/2)} fi
15781    if !(h%2)&&!(h%2) =. {(u<0.5)*pi},{int(w/2)},{int(h/2)} fi
15782
15783    # Add random phase to fft of input image.
15784    +sin. cos..
15785    +*[-4,-1] +*[-4,-3] +[-2,-1]
15786    *[-5,-3] *[-3,-2] -[-3,-2]
15787
15788    # Get synthetized result and normalize it.
15789    repeat s =.. ${sum$>},0,0,0,$> =. 0,0,0,0,$> done
15790    ifft rm.
15791    repeat s sh. $> avg={ia} -. $avg *. {sqrt(${var$>}/if(iv,iv,1))} +. $avg rm. done
15792    c $mM
15793
15794  endl done
15795
15796#@cli syntexturize_matchpatch : _width[%]>0,_height[%]>0,_nb_scales>=0,_patch_size>0,_blending_size>=0,_precision>=0
15797#@cli : Resynthetize 'width'x'height' versions of selected micro-textures using a patch-matching algorithm.
15798#@cli : If 'nbscales==0', the number of scales used is estimated from the image size.
15799#@cli : Default values: 'width=height=100%', 'nb_scales=0', 'patch_size=7', 'blending_size=5' and 'precision=1'.
15800#@cli : $ image.jpg crop 25%,25%,75%,75% syntexturize_matchpatch 512,512
15801syntexturize_matchpatch : check "${1=100%}>0 && ${2=$1}>0 && isint(${3=0}) && $3>=0 && isint(${4=7}) && $4>0 &&
15802                                 ${5=5}>=0 && ${6=1}>=0"
15803  e[^-1] "Resynthetize $1x$2 version(s) of texture$? using a patch-matching algorithm with "\
15804         ${"if $3 u \"$3 \" else u auto- fi"}"scales, $4x$4 patches, blending size $5 and precision $6."
15805  repeat $! l[$>]
15806    nb_scales={round(if($3,$3,log2(min(w,h)/16)),1,1)}
15807    width={if(${"is_percent $1"},round(w*$1,1,1),$1)}
15808    height={if(${"is_percent $2"},round(h*$2,1,1),$2)}
15809
15810    repeat $nb_scales
15811      scale={100*(0.5^$<)}
15812      +r[0] $scale%,$scale%,1,3,2
15813
15814      if !$>
15815
15816        # Initialization.
15817        {1+round(w*$width/{0,w},1,1)},{1+round(h*$height/{0,h},1,1)},1,1
15818
15819        noise. 0.2,2 ==. 1 +distance. 1 *. -1
15820        label_fg.. 0 watershed.. . rm.
15821
15822        100%,100%,1,1,x +f. y a[-2,-1] c channels. 0,2
15823        +blend. ..,shapeaverage -.. . rm.
15824        channels. 0,1
15825
15826        {-2,iM+1} rand. 0,{-4,w} +rand. 0,{-4,h} a[-2,-1] c
15827        map... . rm. +[-2,-1] round.
15828        s. c %.. {-3,w} %. {-3,h} a[-2,-1] c
15829
15830      else
15831
15832        # Upscale.
15833        rv[-2,-1] channels. 0,1
15834        *. 2 r. 200%,200%,1,2,1
15835        f. "*upc = i(x - 1,y,0,0);
15836             vpc = i(x - 1,y,0,1);
15837             ucp = i(x,y - 1,0,0);
15838             vcp = i(x,y - 1,0,1);
15839             ucc = i(x,y,0,0);
15840             vcc = i(x,y,0,1);
15841             if (ucc==upc && vcc==vpc && c==0, upc + 1,
15842             if (ucc==ucp && vcc==vcp && c==1, vcp + 1,i))"
15843      fi
15844
15845      # Synthesis.
15846      psize={-2,min(w,h,$4)}
15847      repeat 1+$6*$<
15848        psynth={int(max(3,$5*$scale%))}
15849        +warp_patch.. .,$psynth
15850        matchpatch. ...,$psize,$psize,1,4,4,0,0,.. rm..
15851      done
15852      rm..
15853
15854    done
15855    warp_patch.. .,$5
15856    rm. r $width,$height,1,100%,0,0,0.5,0.5
15857  endl done
15858
15859# _syntexturize_matchpatch : [correspondence_map],blend_size>0
15860_syntexturize_matchpatch : check ${is_image_arg\ $1}" && isint(${2=3}) && $2>=0"
15861  if $2<=1 pass$1 warp[^-1] .,0 rm.
15862  else repeat $! pass$1 l[$>,-1]
15863    [1],[1],1,[0]
15864    f. "*begin(
15865          boundary = 1;
15866          const patch_size = $2;
15867          const p2 = int(patch_size/2);
15868          const p1 = patch_size - p2 - 1;
15869          avg = resize([0],s#0);
15870
15871          # Pre-compute gaussian kernel.
15872          wpq = resize([0],patch_size^2);
15873          g = 0;
15874          for (q = -p1, q<=p2, ++q,
15875            for (p = -p1, p<=p2, ++p,
15876              wpq[g++] = exp(-(p^2 + q^2)/(2*(0.3*patch_size)^2));
15877            );
15878          );
15879        );
15880        g = 0;
15881        avg = 0;
15882        norm = 0;
15883        for (q = -p1, q<=p2, ++q,
15884          for (p = -p1, p<=p2, ++p,
15885            U = I(#1,x + p,y + q);
15886            w = wpq[g++];
15887            avg+=w*I(#0,U - [p,q]);
15888            norm+=w;
15889          );
15890        );
15891        avg/norm"
15892    k.
15893  endl done fi
15894
15895#@cli tv_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 }
15896#@cli : Apply iterations of the total variation flow on selected images.
15897#@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'.
15898#@cli : $ image.jpg +tv_flow 40
15899tv_flow : skip ${1=10},${2=30},${3=0}
15900  e[^-1] "Apply $1 iterations of the total variation flow on image$?, with time step $2."
15901  pde_flow $1,$2,curvature,$3
15902
15903#@cli unsharp : radius[%]>=0,_amount>=0,_threshold[%]>=0
15904#@cli : Apply unsharp mask on selected images.
15905#@cli : Default values: 'amount=2' and 'threshold=0'.
15906#@cli : $ image.jpg blur 3 +unsharp 1.5,15 cut 0,255
15907unsharp : check "${2=2}>=0" skip ${3=0}
15908  e[^-1] "Apply unsharp mask on image$?, with radius $1, amount $2 and threshold $3."
15909  repeat $!
15910    +b[$>] $1 -. [$>]
15911    if $3 +norm. >=. $3 *[-2,-1] fi
15912    *. $2 -[$>,-1]
15913  done
15914
15915#@cli unsharp_octave : _nb_scales>0,_radius[%]>=0,_amount>=0,threshold[%]>=0
15916#@cli : Apply octave sharpening on selected images.
15917#@cli : Default values: 'nb_scales=4', 'radius=1', 'amount=2' and 'threshold=0'.
15918#@cli : $ image.jpg blur 3 +unsharp_octave 4,5,15 cut 0,255
15919unsharp_octave : check "${1=4}>0 && ${3=2}>=0" skip ${2=1},${4=0}
15920  e[^-1] "Apply octave sharpening on image$?, with $1 scales, radius $2, amount $3 and threshold $4."
15921  repeat $! l[$>] nm={0,n}
15922    +f 0 weight=0
15923    repeat $1
15924      +unsharp[0] {$2*2^-$<},$3,$4 *. {2^-$>}
15925      weight+={2^-$>}
15926      +[1,-1]
15927    done
15928    rm[0] / $weight
15929  nm $nm endl done
15930
15931#@cli vanvliet : std_deviation>=0[%],order={ 0 | 1 | 2 | 3 },axis={ x | y | z | c },_boundary_conditions : (+)
15932#@cli : Apply Vanvliet recursive filter on selected images, along specified axis and with
15933#@cli : specified standard deviation, order and boundary conditions.
15934#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann }.
15935#@cli : Default value: 'boundary_conditions=1'.
15936#@cli : $ image.jpg +vanvliet 3,1,x
15937#@cli : $ image.jpg +vanvliet 30,0,x vanvliet[-2] 30,0,y add
15938
15939#@cli voronoi
15940#@cli : Compute the discrete Voronoi diagram of non-zero pixels in selected images.
15941#@cli : $ 400,400 noise 0.2,2 eq 1 +label_fg 0 voronoi[-1] +gradient[-1] xy,1 append[-2,-1] c \
15942# norm[-1] ==[-1] 0 map[-2] 2,2 mul[-2,-1] normalize[-2] 0,255 dilate_circ[-2] 4 reverse max
15943voronoi :
15944  e[^-1] "Compute the discrete Voronoi diagram of non-zero pixels in image$?."
15945  repeat $! l[$>] s c repeat $! l[$>]
15946    +!=. 0 distance. 1 *. -1
15947    watershed.. . rm.
15948  endl done a c endl done
15949
15950#@cli watermark_fourier : text,_size>0
15951#@cli : Add a textual watermark in the frequency domain of selected images.
15952#@cli : Default value: 'size=33'.
15953#@cli : $ image.jpg +watermark_fourier "Watermarked!" +display_fft remove[-3,-1] normalize 0,255 \
15954# append[-4,-2] y append[-2,-1] y
15955watermark_fourier : check ${2=33}>0
15956  e[^-1] "Add textual watermark '$1' with size $2 in the frequency domain of image$?."
15957  i[0] 0 t[0] "$1",0,0,$2,1,1 >=[0] 0.5 autocrop[0] 0
15958  repeat $!-1 w2={int(w/2)} h2={int(h/2)}
15959    fft.
15960    shift[-2,-1] $w2,$h2,0,0,2
15961    [0],[0],1,{s}
15962    j[-3,-2] .,3,3,0,0,1,[0]
15963    mirror[0] x
15964    j[-3,-2] .,{{-2,w}-2-{0,w}},3,0,0,1,[0]
15965    mirror[0] y
15966    j[-3,-2] .,{{-2,w}-2-{0,w}},{{-2,h}-2-{0,h}},0,0,1,[0]
15967    mirror[0] x
15968    j[-3,-2] .,3,{{-2,h}-2-{0,h}},0,0,1,[0]
15969    mirror[0] y
15970    rm.
15971    shift[-2,-1] -$w2,-$h2,0,0,2
15972    ifft[-2,-1] rm.
15973  mv. 1 done
15974  rm[0]
15975
15976#@cli watershed : [priority_image],_is_high_connectivity={ 0 | 1 } : (+)
15977#@cli : Compute the watershed transform of selected images.
15978#@cli : Default value: 'is_high_connectivity=1'.
15979#@cli : $ 400,400 noise 0.2,2 eq 1 +distance 1 mul[-1] -1 label[-2] watershed[-2] [-1] mod[-2] 256 map[-2] 0 reverse
15980
15981#---------------------------------
15982#
15983#@cli :: Features Extraction
15984#
15985#---------------------------------
15986
15987#@cli area : tolerance>=0,is_high_connectivity={ 0 | 1 }
15988#@cli : Compute area of connected components in selected images.
15989#@cli : Default values: 'is_high_connectivity=0'.
15990#@cli : $ image.jpg luminance stencil[-1] 1 +area 0
15991#@cli : $$ https://gmic.eu/oldtutorial/_area
15992area : check "$1>=0" skip ${2=0}
15993  e[^-1] "Compute area of connected components in image$?, with tolerance $1 and "\
15994         ${arg\ 1+!$2,high,low}" connectivity."
15995  repeat $! l[$>] s c
15996    repeat $! label[$>] $1,$2 nb={$>,1+iM} +histogram[$>] $nb,0,{$nb-1} map[$>] . rm. done
15997  a c endl done
15998
15999#@cli area_fg : tolerance>=0,is_high_connectivity={ 0 | 1 }
16000#@cli : Compute area of connected components for non-zero values in selected images.
16001#@cli : Similar to 'area' except that 0-valued pixels are not considered.
16002#@cli : Default values: 'is_high_connectivity=0'.
16003#@cli : $ image.jpg luminance stencil[-1] 1 +area_fg 0
16004area_fg : check "$1>=0" skip ${2=0}
16005  e[^-1] "Compute area of foreground connected components in image$?, with tolerance $1 and "\
16006         ${arg\ 1+!$2,high,low}" connectivity."
16007  repeat $! l[$>] s c
16008    repeat $! label_fg[$>] $1,$2 nb={$>,1+iM} +histogram[$>] $nb,0,{$nb-1} =. 0 map[$>] . rm. done
16009  a c endl done
16010
16011#@cli at_line : x0[%],y0[%],z0[%],x1[%],y1[%],z1[%]
16012#@cli : Retrieve pixels of the selected images belonging to the specified line (x0,y0,z0)-(x1,y1,z1).
16013#@cli : $ image.jpg +at_line 0,0,0,100%,100%,0 line[0] 0,0,100%,100%,1,0xFF00FF00,255,0,0
16014at_line : check ${7=100%}>=0
16015  e[^-1] "Retrieve pixels of image$?, belonging to line ($1,$2,$3)-($4,$5,$6)."
16016  repeat $! l[$>]
16017    x0={if(${is_percent\ $1},(w-1)*$1,$1)}
16018    y0={if(${is_percent\ $2},(h-1)*$2,$2)}
16019    z0={if(${is_percent\ $3},(d-1)*$3,$3)}
16020    x1={if(${is_percent\ $4},(w-1)*$4,$4)}
16021    y1={if(${is_percent\ $5},(h-1)*$5,$5)}
16022    z1={if(${is_percent\ $6},(d-1)*$6,$6)}
16023    ($x0,$x1^$y0,$y1^$z0,$z1)
16024    r. {1+max(abs($x1-$x0),abs($y1-$y0),abs($z1-$z0))},1,1,3,3
16025    round. 1 warp[0] .,0,0,0 rm.
16026  endl done
16027
16028#@cli at_quadrangle : x0[%],y0[%],x1[%],y1[%],x2[%],y2[%],x3[%],y3[%],_interpolation,_boundary_conditions : \
16029# x0[%],y0[%],z0[%],x1[%],y1[%],z1[%],x2[%],y2[%],z2[%],x3[%],y3[%],z3[%],_interpolation,_boundary_conditions
16030#@cli : Retrieve pixels of the selected images belonging to the specified 2D or 3D quadrangle.
16031#@cli : 'interpolation' can be { 0=nearest-neighbor | 1=linear | 2=cubic }.
16032#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
16033#@cli : $ image.jpg params=5%,5%,95%,5%,60%,95%,40%,95% +at_quadrangle $params polygon.. 4,$params,0.5,255
16034at_quadrangle : check "$#>=8 && $#<=14 && $#!=11"
16035  _at_quadrangle{$#<12?2:3} $*
16036
16037_at_quadrangle2 : check "${9=1}>=0 && $9<=2 && ${10=0}>=0 && $10<=3"
16038  repeat $! l[$>]
16039    x0={round(${"is_percent $1"}?(w-1)*$1:$1)}
16040    y0={round(${"is_percent $2"}?(h-1)*$2:$2)}
16041    x1={round(${"is_percent $3"}?(w-1)*$3:$3)}
16042    y1={round(${"is_percent $4"}?(h-1)*$4:$4)}
16043    x2={round(${"is_percent $5"}?(w-1)*$5:$5)}
16044    y2={round(${"is_percent $6"}?(h-1)*$6:$6)}
16045    x3={round(${"is_percent $7"}?(w-1)*$7:$7)}
16046    y3={round(${"is_percent $8"}?(h-1)*$8:$8)}
16047    ($x0,$x1;$x3,$x2^$y0,$y1;$y3,$y2)
16048    r. {P0=[$x0,$y0];P1=[$x1,$y1];P2=[$x2,$y2];P3=[$x3,$y3];\
16049        1+round([max(norm(P1-P0),norm(P3-P2)),max(norm(P3-P0),norm(P2-P1))])},1,2,3
16050    warp.. .,0,$9,$10 rm.
16051  endl done
16052
16053_at_quadrangle3 : check "${13=1}>=0 && $13<=2 && ${14=0}>=0 && $14<=3"
16054  repeat $! l[$>]
16055    x0={round(${"is_percent $1"}?(w-1)*$1:$1)}
16056    y0={round(${"is_percent $2"}?(h-1)*$2:$2)}
16057    z0={round(${"is_percent $3"}?(h-1)*$3:$3)}
16058    x1={round(${"is_percent $4"}?(w-1)*$4:$4)}
16059    y1={round(${"is_percent $5"}?(h-1)*$5:$5)}
16060    z1={round(${"is_percent $6"}?(h-1)*$6:$6)}
16061    x2={round(${"is_percent $7"}?(w-1)*$7:$7)}
16062    y2={round(${"is_percent $8"}?(h-1)*$8:$8)}
16063    z2={round(${"is_percent $9"}?(h-1)*$9:$9)}
16064    x3={round(${"is_percent $10"}?(w-1)*$10:$10)}
16065    y3={round(${"is_percent $11"}?(h-1)*$11:$11)}
16066    z3={round(${"is_percent $12"}?(h-1)*$12:$12)}
16067    ($x0,$x1;$x3,$x2^$y0,$y1;$y3,$y2^$z0,$z1;$z3,$z2)
16068    r. {P0=[$x0,$y0,$z0];P1=[$x1,$y1,$z1];P2=[$x2,$y2,$z2];P3=[$x3,$y3,$z2];\
16069        1+round([max(norm(P1-P0),norm(P3-P2)),max(norm(P3-P0),norm(P2-P1))])},1,3,3
16070    warp.. .,0,$13,$14 rm.
16071  endl done
16072
16073#@cli barycenter
16074#@cli : Compute the barycenter vector of pixel values.
16075#@cli : $ 256,256 ellipse 50%,50%,20%,20%,0,1,1 deform 20 +barycenter +ellipse[-2] {@0,1},5,5,0,10
16076barycenter :
16077  e[^-1] "Compute the barycenter vector of pixel values of image$?."
16078  norm repeat $! l[$>] nm={0,b}
16079    sum={is}
16080    if $sum>0
16081      if d>1 +* 'z' z={is} rm. else z=0 fi
16082      if h>1 +* 'y' y={is} rm. else y=0 fi
16083      * 'x' x={is} rm.
16084      ({$x/$sum};{$y/$sum};{$z/$sum})
16085    else ({w/2},{h/2},{d/2}) rm..
16086    fi
16087  nm "[barycenter of '"$nm"']" endl done
16088
16089#@cli delaunay
16090#@cli : Generate discrete 2D Delaunay triangulation of non-zero pixels in selected images.
16091#@cli : Input images must be scalar.
16092#@cli : Each pixel of the output image is a triplet (a,b,c) meaning the pixel belongs to
16093#@cli : the Delaunay triangle 'ABC' where 'a','b','c' are the labels of the pixels 'A','B','C'.
16094#@cli : $ 400,400 rand 32,255 100%,100% noise. 0.4,2 eq. 1 mul +delaunay
16095#@cli : $ image.jpg b 1% 100%,100% noise. 0.8,2 eq. 1 mul +delaunay channels 0,2
16096delaunay :
16097  e[^-1] "Generate discrete 2D Delaunay triangulation of non-zero pixels in image$?."
16098  repeat $! l[$>]
16099
16100    # Retrieve point coordinates.
16101    +slices 0 norm. !=. 0 {is+1},1,1,2 +f.. ">begin(p = 0); i?(I[#-1,++p] = [x,y]; p):0"
16102
16103    # Compute voronoi diagram of point cloud.
16104    distance... 1 *... -1 watershed. ... rm...
16105
16106    # Detect delaunay triangles.
16107    100%,100%,1,3
16108    _delaunay 1,0,0,1 _delaunay -1,0,0,-1 _delaunay -1,0,0,1 _delaunay 0,-1,1,0 rm..
16109
16110    # Map initial labels.
16111    s. c +warp[0] [1],0,0 point. 0,0 map[-4--2] . rm. a[-3--1] c
16112    k.
16113  endl done
16114
16115_delaunay :
16116  f.. "*
16117     const boundary = 1;
16118     p0 = i;
16119     p1 = j($1,$2);
16120     p2 = j($3,$4);
16121     if (p0!=p1 && p0!=p2 && p1!=p2,
16122       P0 = I[#-3,p0];
16123       P1 = I[#-3,p1];
16124       P2 = I[#-3,p2];
16125       polygon(#-1,3,P0,P1,P2,1,[p0,p1,p2]);
16126    ); i"
16127
16128#@cli detect_skin : 0<=tolerance<=1,_skin_x,_skin_y,_skin_radius>=0
16129#@cli : Detect skin in selected color images and output an appartenance probability map.
16130#@cli : Detection is performed using CbCr chromaticity data of skin pixels.
16131#@cli : If arguments 'skin_x', 'skin_y' and 'skin_radius' are provided, skin pixels are learnt
16132#@cli : from the sample pixels inside the circle located at ('skin_x','skin_y') with radius 'skin_radius'.
16133#@cli : Default value: 'tolerance=0.5' and 'skin_x=skiny=radius=-1'.
16134detect_skin : check "${1=0.5}>=0 && $1<=1" skip ${2=-1},${3=-1},${4=-1}
16135  if $2<0||$3<=0||$4<=0
16136    e[0--3] "Detect skin in image$?, using tolerance $1."
16137    m0=120.9292108800069
16138    m1=142.5745272918084
16139    A=0.09749985486268997
16140    B=0.06388871371746063
16141    C=0.05250053107738495
16142    to_rgb srgb2rgb rgb2ycbcr channels 1,2
16143    repeat $! l[$>]
16144      whd={w},{h},{d} r {w*h*d},2,1,1,-1
16145      s y -[0] $m0 -[1] $m1 a y
16146      i[0] ($A,$B;$B,$C) +m* rm[0]
16147      * s y + *. {$1-1} exp.
16148      r $whd,1,-1
16149    endl done
16150  else
16151    e[0--3] "Detect skin in image$?, using tolerance $1 and target circle at ($2,$3) with radius $4."
16152    to_rgb srgb2rgb rgb2ycbcr channels 1,2
16153    repeat $! l[$>]
16154      100%,100% circle[1] $2,$3,$4,1,1 +f[1] 'if(i,y,-1)' f[1] 'if(i,x,-1)' discard[1,2] -1 a[1,2] c
16155      +warp[0] [1],0,0 rm[1]
16156      s[1] c
16157      m0={1,ia} -[1] $m0
16158      m1={2,ia} -[2] $m1
16159      M={h} a[1,2] x +transpose[1] rv[1,2] m*[1,2] /[1] $M invert[1]
16160      rv whd={w},{h},{d} r[1] {w*h*d},2,1,1,-1
16161      s[1] y -[1] $m0 -[2] $m1 a[1,2] y +m* rm[0]
16162      * s y + *. {$1-1} exp.
16163      r $whd,1,-1
16164    endl done
16165  fi
16166
16167#@cli displacement : [source_image],_smoothness,_precision>=0,_nb_scales>=0,_iteration_max>=0,is_backward={ 0 | 1 },\
16168# _[guide] : (+)
16169#@cli : Estimate displacement field between specified source and selected target images.
16170#@cli : If 'smoothness>=0', regularization type is set to isotropic, else to anisotropic.
16171#@cli : If 'nbscales==0', the number of scales used is estimated from the image size.
16172#@cli : Default values: 'smoothness=0.1', 'precision=5', 'nb_scales=0', 'iteration_max=10000', 'is_backward=1' \
16173# and '[guide]=(unused)'.
16174#@cli : $ image.jpg +rotate 3,1,0,50%,50% +displacement[-1] [-2] quiver[-1] [-1],15,1,1,1,{1.5*iM}
16175
16176#@cli distance : isovalue[%],_metric : isovalue[%],[metric],_method : (+)
16177#@cli : Compute the unsigned distance function to specified isovalue, opt. according to a custom metric.
16178#@cli : 'metric' can be { 0=chebyshev | 1=manhattan | 2=euclidean | 3=squared-euclidean }.
16179#@cli : 'method' can be { 0=fast-marching | 1=low-connectivity dijkstra | 2=high-connectivity dijkstra | \
16180# 3=1+return path | 4=2+return path }.
16181#@cli : Default value: 'metric=2' and 'method=0'.
16182#@cli : $ image.jpg threshold 20% distance 0 pow 0.3
16183#@cli : $ 400,400 set 1,50%,50% +distance[0] 1,2 +distance[0] 1,1 distance[0] 1,0 mod 32 threshold 16 append c
16184#@cli : $$ https://gmic.eu/oldtutorial/_distance
16185
16186#@cli fftpolar
16187#@cli : Compute fourier transform of selected images, as centered magnitude/phase images.
16188#@cli : $ image.jpg fftpolar ellipse 50%,50%,10,10,0,1,0 ifftpolar
16189fftpolar :
16190  e[^-1] "Compute fourier transform of image$?, as centered magnitude/phase images."
16191  repeat $! l[$<]
16192    fft complex2polar shift {-round(w/2)},{-round(h/2)},{-round(d/2)},0,2
16193  endl done
16194
16195#@cli histogram : _nb_levels>0[%],_value0[%],_value1[%] : (+)
16196#@cli : Compute the histogram of selected images.
16197#@cli : If value range is set, the histogram is estimated only for pixels in the specified
16198#@cli : value range. Argument 'value1' must be specified if 'value0' is set.
16199#@cli : Default values: 'nb_levels=256', 'value0=0%' and 'value1=100%'.
16200#@cli : $ image.jpg +histogram 64 display_graph[-1] 400,300,3
16201
16202#@cli histogram_nd : nb_levels>0[%],_value0[%],_value1[%]
16203#@cli : Compute the 1D,2D or 3D histogram of selected multi-channels images (having 1,2 or 3 channels).
16204#@cli : If value range is set, the histogram is estimated only for pixels in the specified
16205#@cli : value range.
16206#@cli : Default values: 'value0=0%' and 'value1=100%'.
16207#@cli : $ image.jpg channels 0,1 +histogram_nd 256
16208histogram_nd : check $1>0 skip ${2=0%},${3=100%}
16209  e[^-1] "Compute histogram of multi-channels image$?, using $1 levels in range [$1,$2]."
16210  percent_nblevels=${"is_percent $1"}
16211  percent_min=${"is_percent $2"}
16212  percent_max=${"is_percent $3"}
16213  repeat $! l[$>] s={s}
16214    r {w*h*d},{min(3,s)},1,1,-1
16215    vmin=$2 vmax=$3
16216    if $percent_min||$percent_max
16217      im={im} iM={iM}
16218      vmin={if($percent_min,$im+($iM-$im)*$2,$2)}
16219      vmax={if($percent_max,$im+($iM-$im)*$3,$3)}
16220    fi
16221    dv={$vmax-$vmin}
16222    nb_levels={max(1,round(if($percent_nblevels,$1*(1+$vmax-$vmin),$1)))}
16223    f 'if(i>=$vmin&&i<=$vmax,if(i==$vmax,$nb_levels-1,int((i-$vmin)*$nb_levels/($vmax-$vmin))),-1)'
16224    pointcloud 1,$nb_levels,{if($s>1,$nb_levels,1)},{if($s>2,$nb_levels,1)}
16225  endl done
16226
16227#@cli histogram_cumul : _nb_levels>0,_is_normalized={ 0 | 1 },_val0[%],_val1[%]
16228#@cli : Compute cumulative histogram of selected images.
16229#@cli : Default values: 'nb_levels=256', 'is_normalized=0', 'val0=0%' and 'val1=100%'.
16230#@cli : $ image.jpg +histogram_cumul 256 histogram[0] 256 display_graph 400,300,3
16231histogram_cumul : check ${1=256}>0 skip ${2=0},${3=0%},${4=100%}
16232  arg 1+!$2,"normalized ",""
16233  e[^-1] "Compute "${}"cumulative histogram of image$?, using $1 levels."
16234  histogram $1,$3,$4 cumulate if $2 repeat $! /[$>] {$>,iM} done fi
16235
16236#@cli histogram_pointwise : nb_levels>0[%],_value0[%],_value1[%]
16237#@cli : Compute the histogram of each vector-valued point of selected images.
16238#@cli : If value range is set, the histogram is estimated only for values in the specified
16239#@cli : value range.
16240#@cli : Default values: 'value0=0%' and 'value1=100%'.
16241histogram_pointwise : skip ${2=0%},${3=100%}
16242  e[^-1] "Compute the pointwise histogram of vector-valued points in image$?, with $1 levels."
16243  repeat $! l[$>] nm={0,n}
16244    nb_levels={round(if(${is_percent\ $1},(iM-im)*$1,$1))}
16245    value0={if(${is_percent\ $2},im+(iM-im)*$2,$2)}
16246    value1={if(${is_percent\ $3},im+(iM-im)*$3,$3)}
16247    - $value0 * {$nb_levels/max(1,abs($value1-$value0))} c 0,{$nb_levels-1} round
16248    w={w} h={h} d={d} r {w*h*d},{s},1,1,-1
16249    i.. (0,{w-1}) r.. .,.,1,1,3 round..
16250    r[-2,-1] 300%,100%,1,1,4 shift. 1 +[-2,-1] y.
16251    i.. ({'CImg3d'},{h/3},{h/3})
16252    (1,0;1,{h/3-1}) r. 2,{-2,h/3},1,1,3 round.
16253    3,100%,1,1,1 1,100%,1,1,-1 y[-5,-3,-2] a[-5--1] y
16254    {$w*$h*$d},$nb_levels j3d. ..,0,0,0,1,0,0,0 rm..
16255    r $w,$h,$d,$nb_levels,-1
16256  nm $nm endl done
16257
16258#@cli hough : _width>0,_height>0,gradient_norm_voting={ 0 | 1 }
16259#@cli : Compute hough transform (theta,rho) of selected images.
16260#@cli : Default values: 'width=512', 'height=width' and 'gradient_norm_voting=1'.
16261#@cli : $ image.jpg +blur 1.5 hough[-1] 400,400 blur[-1] 0.5 add[-1] 1 log[-1]
16262hough : check "${1=512}>0 && ${2=$1}>0" skip ${3=1}
16263  e[^-1] "Compute $1x$2 hough transform of image$?, "${arg\ 1+!$3,with,without}" gradient norm voting."
16264  slices 50% luminance repeat $! l[$>] nm={0,n}
16265    rhomax={sqrt(w^2+h^2)/2}
16266    g (0,{w-1}) (0;{{-2,h}-1}) r[-2,-1] {-3,w},{-3,h},1,1,3 -.. {w/2} -. {h/2}
16267    complex2polar[-4--1] -. ... polar2complex[-2,-1] rm.
16268    +<. 0 *. {pi} +[-3,-1] abs. %.. {2*pi}
16269    *. {$2/$rhomax} *.. {0.5*$1/pi}
16270    y[-3--1] x {w} mv[-4] $! if !$3 f. 1 fi
16271    a y pointcloud 1 r $1,$2,1,1,0
16272  nm $nm endl done
16273
16274#@cli ifftpolar
16275#@cli : Compute inverse fourier transform of selected images, from centered magnitude/phase images.
16276ifftpolar :
16277  e[^-1] "Compute inverse fourier transform of image$?, from centered magnitude/phase images."
16278  repeat int($!/2) l[$>,{$>+1}]
16279    shift {round(w/2)},{round(h/2)},{round(d/2)},0,2 polar2complex ifft rm.
16280  endl done
16281
16282#@cli isophotes : _nb_levels>0
16283#@cli : Render isophotes of selected images on a transparent background.
16284#@cli : Default value: 'nb_levels=64'
16285#@cli : $ image.jpg blur 2 isophotes 6 dilate_circ 5 display_rgba
16286isophotes : skip ${1=64}
16287  e[^-1] "Render isophote maps from images$?, with $1 levels."
16288  to_rgba repeat $! l[$>]
16289    +luminance repeat $1 +isoline3d[1] {$>*255/($1-1)} done rm[1] +3d[^0] col3d. 1
16290    [0],[0] j3d. ..,0,0,0,1,0,0,0 rm.. *
16291  endl done
16292
16293#@cli label : _tolerance>=0,is_high_connectivity={ 0 | 1 },_is_L2_norm={ 0 | 1 } : (+)
16294#@cli : Label connected components in selected images.
16295#@cli : Default values: 'tolerance=0', 'is_high_connectivity=0' and 'is_L2_norm=1'.
16296#@cli : $ image.jpg luminance threshold 60% label normalize 0,255 map 0
16297#@cli : $ 400,400 set 1,50%,50% distance 1 mod 16 threshold 8 label mod 255 map 2
16298#@cli : $$ https://gmic.eu/oldtutorial/_label
16299
16300#@cli label_fg : tolerance>=0,is_high_connectivity={ 0 | 1 }
16301#@cli : Label connected components for non-zero values (foreground) in selected images.
16302#@cli : Similar to 'label' except that 0-valued pixels are not labeled.
16303#@cli : Default value: 'is_high_connectivity=0'.
16304label_fg : check "$1>=0" skip ${2=0}
16305  e[^-1] "Label foreground connected components on image [1], with tolerance $1 and "\
16306         ${arg\ 1+!$2,high,low}" connectivity."
16307  repeat $! l[$>]
16308    if d>1 +z -1,-1,-1,{w-1},{h-1},{d-1} label. $1,$2 z. 1,1,1,{w-1},{h-1},{d-1}
16309    else +z -1,-1,{w-1},{h-1} label. $1,$2 z. 1,1,{w-1},{h-1}
16310    fi
16311    !=.. 0 * +histogram {1+iM} =. 0
16312    >. 0 cumulate. map.. . rm.
16313  endl done
16314
16315#@cli laar
16316#@cli : Extract the largest axis-aligned rectangle in non-zero areas of selected images.
16317#@cli : Rectangle coordinates are returned in status, as a sequence of numbers x0,y0,x1,y1.
16318#@cli : $ shape_cupid 256 coords=${-laar} normalize 0,255 to_rgb rectangle $coords,0.5,0,128,0
16319laar :
16320  e[^-1] "Extract the largest axis-aligned rectangle in non-zero areas of image$?."
16321  res= sep=
16322  repeat $! l[$>]
16323    +channels 0 gt. 0 nm. shape
16324    +cumulate[shape] xy nm. cumul
16325    val={i[-1,2]}
16326    if !$val res.=-1,-1,-1,-1        # All black
16327    elif $val==wh res.=0,0,{[w,h]-1} # All white
16328    else
16329
16330      # Step 1: Compute largest square.
16331      Rin,Rout=0,{min(w,h)+1}
16332      P0,P=
16333      do
16334        Rmid={int(($Rin+$Rout)/2)}
16335        Q=${_laar\ $Rmid,$Rmid,$P}
16336        if narg($Q) Rin=$Rmid P0=$Q P=$Q else Rout=$Rmid P= fi
16337      while $Rin!=$Rout-1
16338      if $Rin==1 P=${_laar\ 1,1} fi
16339
16340      # Step 2: Compute largest rectangle, for W>H.
16341      maxA,maxW,maxH=0 maxcoords=
16342      P=$P0 W,H=$Rin
16343      for $H>0
16344        A={$W*$H} if $A>$maxA maxA,maxW,maxH=$A,$W,$H maxP={[$P][0,2]} fi
16345        nW={$W+1}
16346        Q=${_laar\ $nW,$H,$P}
16347        if narg($Q) W=$nW P=$Q
16348        elif $H>1
16349          pH={$H-1}
16350          Q=${_laar\ $nW,$pH}
16351          if narg($Q) W,H=$nW,$pH P=$Q
16352          else H-=2 P=
16353          fi
16354        else break
16355        fi
16356      done
16357
16358      # Step 3: Compute largest rectangle, for H>W.
16359      P=$P0 W,H=$Rin
16360      for $W>0
16361        A={$W*$H} if $A>$maxA maxA,maxW,maxH=$A,$W,$H maxP={[$P][0,2]} fi
16362        nH={$H+1}
16363        Q=${_laar\ $W,$nH,$P}
16364        if narg($Q) H=$nH P=$Q
16365        elif $W>1
16366          pW={$W-1}
16367          Q=${_laar\ $pW,$nH}
16368          if narg($Q) W,H=$pW,$nH P=$Q
16369          else W-=2 P=
16370          fi
16371        else break
16372        fi
16373      done
16374
16375      res.=$sep$maxP,{[$maxP]+[$maxW,$maxH]-1} sep=,
16376    fi
16377    rm[shape,cumul]
16378  endl done u $res
16379
16380# $1,$2: Rectangle width and height.
16381# ${3--1}: Coordinates of candidate locations. If empty, full-search is done.
16382# Return the list of upper-left rectangle coordinates that fit.
16383# Images [shape] and [cumul] must be defined.
16384_laar : skip "${3=}"
16385  fn="cumul(x0,y0,x1,y1) = (
16386        _px0 = x0 - 1;
16387        _py0 = y0 - 1;
16388        i(#"$cumul,"x1,y1) + i(#"$cumul",_px0,_py0) - i(#"$cumul",x1,_py0) - i(#"$cumul",_px0,y1);
16389      );"
16390  res=
16391  if narg($3) # Test suggested locations
16392    (${3--1}) r. 2,{w/2},1,1,-1 s. x a[-2,-1] c
16393    f. ${fn}"x1 = i0 + $1 - 1; y1 = i1 + $2 - 1;
16394       i(#"$shape",i0,i1) && x1<w#"$shape" && y1<h#"$shape" && cumul(i0,i1,x1,y1)==$1*$2?I:[-1,-1]"
16395    l. discard y,-1 if w s c a x res={^} fi rm endl
16396  else # Test all shape points
16397    +f[cumul] *${fn}"x1 = x + $1 - 1; y1 = y + $2 - 1;
16398              i(#"$shape",x,y) && x1<w#"$shape" && y1<h#"$shape" && cumul(x,y,x1,y1)==$1*$2"
16399    if iM 100%,100%,1,2,"i(#-1)?[x,y]:[-1,-1]" discard. -1 if w s. y,2 a[-2,-1] x res={^} fi rm. fi
16400    rm.
16401  fi
16402  u $res
16403
16404#@cli max_patch : _patch_size>=1
16405#@cli : Return locations of maximal values in local patch-based neighborhood of given size for selected images.
16406#@cli : Default value: 'patch_size=16'.
16407#@cli : $ image.jpg norm +max_patch 16
16408max_patch : check "isint(${1=16}) && $1>=1"
16409  e[^-1] "Return locations of maximal values in local patch neighborhood of size $1, in image$?."
16410  repeat $! +dilate[$>] $1 ==[$>,-1] done
16411
16412#@cli min_patch : _patch_size>=1
16413#@cli : Return locations of minimal values in local patch-based neighborhood of given size for selected images.
16414#@cli : Default value: 'patch_size=16'.
16415#@cli : $ image.jpg norm +min_patch 16
16416min_patch : check "isint(${1=16}) && $1>=1"
16417  e[^-1] "Return locations of minimal values in local patch neighborhood of size $1, in image$?."
16418  repeat $! +erode[$>] $1 ==[$>,-1] done
16419
16420#@cli minimal_path : x0[%]>=0,y0[%]>=0,z0[%]>=0,x1[%]>=0,y1[%]>=0,z1[%]>=0,_is_high_connectivity={ 0 | 1 }
16421#@cli : Compute minimal path between two points on selected potential maps.
16422#@cli : Default value: 'is_high_connectivity=0'.
16423#@cli : $ image.jpg +gradient_norm fill[-1] 1/(1+i) minimal_path[-1] 0,0,0,100%,100%,0 pointcloud[-1] 0 *[-1] 280 \
16424# to_rgb[-1] ri[-1] [-2],0 or
16425minimal_path : check "$1>=0 && $2>=0 && $3>=0" skip ${7=0}
16426  e[^-1] "Compute minimal path between points ($1,$2,$3) and ($4,$5,$6) for potential map$?, with "\
16427         ${arg\ 1+$7,low,high}" connectivity."
16428  repeat $! l[$>] nm={0,n}
16429    - {im} + {iM/100}
16430    100%,100% = 1,${4-6} distance. 1,[0],{if($7,4,3)} k.
16431    x={round(if(${is_percent\ $1},$1*(w-1),$1))}
16432    y={round(if(${is_percent\ $2},$2*(h-1),$2))}
16433    z={round(if(${is_percent\ $3},$3*(d-1),$3))}
16434    ($x;$y;$z)
16435    do
16436      p={0,i($x,$y,$z)}
16437      if $p&1 x-=1
16438      elif $p&2 x+=1
16439      fi
16440      if $p&4 y-=1
16441      elif $p&8 y+=1
16442      fi
16443      if $p&16 z-=1
16444      elif $p&32 z+=1
16445      fi
16446      ($x;$y;$z)
16447    while $p
16448    rm[0,-1] a x
16449  nm $nm endl done
16450
16451#@cli mse : : (+)
16452#@cli : Compute MSE (Mean-Squared Error) matrix between selected images.
16453#@cli : $ image.jpg +noise 30 +noise[0] 35 +noise[0] 38 cut. 0,255 mse
16454
16455#@cli patches : patch_width>0,patch_height>0,patch_depth>0,x0,y0,z0,_x1,_y1,_z1,...,_xN,_yN,_zN
16456#@cli : Extract N+1 patches from selected images, centered at specified locations.
16457#@cli : $ image.jpg +patches 64,64,1,153,124,0,184,240,0,217,126,0,275,38,0
16458patches : check "isint($1) && $1>0 && isint($2) && $2>0 && isint($3) && $3>0"
16459  e[^-1] "Extract $1x$2x$3 patches from image$?, at locations (${4--1})."
16460  (${4--1}) r. 3,{w/3},1,1,-1 permute. yzcx N={w}
16461  H={int(sqrt(w))} W={round(w/$H,1,1)} r. {$W*$H},1,1,3,0 r. $W,$H,1,3,-1
16462  r. {w*$1},{h*$2},{d*$3}
16463  $1,$2,$3,1,x-{int($1/2)} +f. y-{int($2/2)} +f. z-{int($3/2)} a[-3--1] c ri. ..,0,2 +[-2,-1]
16464  repeat $!-1 warp[$>] .,0,0,0 done rm.
16465  repeat $! l[$<] s y,$H s x,$W k[0-{$N-1}] endl done
16466
16467#@cli matchpatch : [patch_image],patch_width>=1,_patch_height>=1,_patch_depth>=1,_nb_iterations>=0,\
16468# _nb_randoms>=0,_patch_penalization,_output_score={ 0 | 1 },_[guide] : (+)
16469#@cli : Estimate correspondence map between selected images and specified patch image, using
16470#@cli : a patch-matching algorithm.
16471#@cli : Each pixel of the returned correspondence map gives the location (p,q) of the closest patch in
16472#@cli : the specified patch image. If 'output_score=1', the third channel also gives the corresponding
16473#@cli : matching score for each patch as well.
16474#@cli : If 'patch_penalization' is >=0, SSD is penalized with patch occurrences.
16475#@cli : If 'patch_penalization' is <0, SSD is inf-penalized when distance between patches are less \
16476# than '-patch_penalization'.
16477#@cli : Default values: 'patch_height=patch_width', 'patch_depth=1', 'nb_iterations=5', 'nb_randoms=5', \
16478# 'patch_penalization=0', 'output_score=0' and 'guide=(undefined)'.
16479#@cli : $ image.jpg sample colorful +matchpatch[0] [1],3 +warp[-2] [-1],0
16480
16481#@cli plot2value
16482#@cli : Retrieve values from selected 2D graph plots.
16483#@cli : $ 400,300,1,1,'if(y>300*abs(cos(x/10+2*u)),1,0)' +plot2value +display_graph[-1] 400,300
16484plot2value :
16485  e[^-1] "Retrieve values from 2D graph plot$?."
16486  repeat $! l[$>]
16487    s c >= 50%
16488    repeat $! l[$>] (1,{w}) ri[1] [0],3 * histogram {w},1,{w} endl done
16489    a c
16490  endl done
16491
16492#@cli pointcloud : _type = { -X=-X-opacity | 0=binary | 1=cumulative | 2=label | 3=retrieve coordinates },\
16493# _width,_height>0,_depth>0
16494#@cli : Render a set of point coordinates, as a point cloud in a 1D/2D or 3D binary image
16495#@cli : (or do the reverse, i.e. retrieve coordinates of non-zero points from a rendered point cloud).
16496#@cli : Input point coordinates can be a NxMx1x1, Nx1x1xM or 1xNx1xM image, where 'N' is the number of points,
16497#@cli : and M the point coordinates.
16498#@cli : If 'M'>3, the 3-to-M components sets the (M-3)-dimensional color at each point.
16499#@cli : Parameters 'width','height' and 'depth' are related to the size of the final image :
16500#@cli :   - If set to 0, the size is automatically set along the specified axis.
16501#@cli :   - If set to N>0, the size along the specified axis is N.
16502#@cli :   - If set to N<0, the size along the specified axis is at most N.
16503#@cli : Points with coordinates that are negative or higher than specified ('width','height','depth')
16504#@cli : are not plotted.
16505#@cli : Default values: 'type=0' and 'max_width=max_height=max_depth=0'.
16506#@cli : $ 3000,2 rand 0,400 +pointcloud 0 dilate[-1] 3
16507#@cli : $ 3000,2 rand 0,400 {w} {w},3 rand[-1] 0,255 append y +pointcloud 0 dilate[-1] 3
16508pointcloud : check "${1=0}<=3 && ${2=0}>=0 && ${3=0}>=0 && ${4=0}>=0"
16509  e[^-1] "Convert image$? to point clouds, in "${arg\ 2+($1>=0)*$1-($1<0),{-$1}-opacity,binary,cumulative,labeling}\
16510         " mode, with dimensions $2x$3x$4."
16511  repeat $! l[$>] nm={0,n}
16512    if $1!=3 # Render point cloud image from set of point coordinates
16513
16514      # Force input data to be of size Nx1x1xM.
16515      if "d>1 || (w>1 && h>1 && s>1)"
16516         error "Command '$0': Invalid input image "{w}x{h}x{d}x{s}". Should be NxMx1x1, Nx1x1xM or 1xNx1xM."
16517      fi
16518      if "w>1 && h>1 && s==1" r 100%,1,1,{h},-1  # NxMx1x1 -> Nx1x1xM
16519      elif "w==1 && h>1 && s>1" r {h},1,1,{s},-1 # 1xNx1xM -> Nx1x1xM
16520      fi
16521
16522      # Retrieve coordinates and color info.
16523      if s<3 channels 0,2 fi
16524      if s<4 100%,1,1,1,1 a[-2,-1] c fi
16525      sh. 0   round. siz_x={!$2?iM+1:$2}
16526      sh.. 1  round. siz_y={!$3?iM+1:$3}
16527      sh... 2 round. siz_z={!$4?iM+1:$4}
16528      rm[-3--1]
16529
16530      # Draw point cloud.
16531      $siz_x,$siz_y,$siz_z,{$1!=2?s-3:1}
16532      if $1<0 # -X-opacity
16533        f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = (1+$1)*I(#-1,P) - $1*C; V"
16534      elif $1==0 # Binary
16535        f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = C; V"
16536      elif $1==1 # Cumulative
16537        f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) += C; V"
16538      else # Label
16539        f.. ">begin(l = 0); V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = ++l; V"
16540      fi
16541
16542    else # Retrieve set of point coordinates from rendered point cloud image
16543      16,1,1,{s+3}
16544      f.. ">
16545        begin(N = 0; zero = vectors(0));
16546        I!=zero?I[#-1,N++] = [ x,y,z,I ];
16547        N>=w(#-1)?resize(#-1,1.5*w(#-1),1,1,s#-1,0);
16548        end(resize(#-1,N,1,1,s#-1,0));
16549        I"
16550    fi
16551  k. nm $nm endl done
16552
16553#@cli psnr : _max_value
16554#@cli : Compute PSNR (Peak Signal-to-Noise Ratio) matrix between selected images.
16555#@cli : Default value: 'max_value=255'.
16556#@cli : $ image.jpg +noise 30 +noise[0] 35 +noise[0] 38 cut[-1] 0,255 psnr 255 replace_inf 0
16557psnr : skip "${1=}"
16558  if isnum("$1")
16559    e[0--3] "Compute the "$!x$!" matrix of PSNR values, from image$? with maximum value $1."
16560    mse log10 - {log10(($1)^2)}
16561  else
16562    e[0--3] "Compute the "$!x$!" matrix of PSNR values, from image$?."
16563    noarg
16564    if $!
16565      $! repeat $!-1 =. {$>,iM},$> done
16566      mse[^-1] sqr. log10 -.. 'max(i[#-1,x],i[#-1,y])' rm.
16567    fi
16568  fi
16569  * -10 nm [PSNR]
16570
16571#@cli segment_watershed : _threshold>=0
16572#@cli : Apply watershed segmentation on selected images.
16573#@cli : Default values: 'threshold=2'.
16574#@cli : $ image.jpg segment_watershed 2
16575segment_watershed : check "${1=2}>=0"
16576  e[^-1] "Apply watershed segmentation on image$?, with edge threshold $1."
16577  repeat $! l[$>]
16578    min={im}
16579    + {1+$min} +gradient_norm
16580    +f. "i<$1 && i<j(1) && i<j(-1) && i<j(0,1) && i<j(0,-1) && (d<=1?1:i<j(0,0,1) && i<j(0,0,-1))"
16581    *[-3,-1] *. -1 watershed.. . rm.
16582    - {1+$min}
16583  endl done
16584
16585#@cli shape2bump : _resolution>=0,0<=_weight_avg_max_avg<=1,_dilation,_smoothness>=0
16586#@cli : Estimate bumpmap from binary shape in selected images.
16587#@cli : Default value: 'resolution=256', 'weight_avg_max=0.75', 'dilation=0' and 'smoothness=100'.
16588shape2bump : check "isint(${1=256}) && $1>=0 && ${2=0.75}>=0 && $2<=1 && isnum(${3=0}) && ${4=100}>=0"
16589  e[^-1] "Estimate bumpmap from binary shape in image$?, using "\
16590         ${"if $1 u \"resolution $1\" else u \"full resolution\" fi"}", avg/max weight $2, dilation $3
16591          and smoothness $4."
16592  repeat $!
16593    +l[$>]
16594      norm > 0
16595      siz={[w,h]}
16596
16597      # Generate skeleton.
16598      distance 0 + $3
16599      +f. "const boundary = 1;
16600           (i>j(-1)&&i>j(1)) || (i>j(0,-1)&&i>j(0,1)) || (i>j(-1,-1)&&i>j(1,1)) || (i>j(-1,1)&&i>j(1,-1))"
16601
16602      # Downsize for faster computation.
16603      is_resized=0
16604      if $1" && "max(w,h)>$1 rr2d $1,$1,0,2 gt. 0 thinning. 1 *.. {$1/max($siz)} is_resized=1 fi
16605
16606      # Generate z-map.
16607      +f. 0 .x2
16608      f[1] "*if (i,
16609        r = i(#0,x,y);
16610        ir = floor(r);
16611        r2 = r^2;
16612        for (q = -ir, q<=ir, ++q,
16613          Y = y + q;
16614          for (p = -ir, p<=ir, ++p,
16615            X = x + p;
16616            dist = norm(p,q);
16617            if (dist<r,
16618              elev = sqrt(r2 - dist^2);
16619              i(#2,X,Y) = max(i(#2,X,Y),elev);
16620              if ($2<1, i(#3,X,Y) += elev; ++i(#4,X,Y));
16621            );
16622          )
16623        );
16624      ); i"
16625
16626      if $2<1 M={-3,iM} max. 1 /[-2,-1] n. 0,$M j. ..,0,0,0,0,$2
16627      else rm[-2,-1]
16628      fi
16629      k.
16630      if $is_resized *. {max([$siz]/[w,h])} r $siz,1,1,5 c 0,100% fi
16631    endl
16632    if $4 M={iM} (1^0^1) r. [0],[0] !=[0] 0 *. [0] smooth.. .,$4,0.1,0 rm. b. 0.5 n. 0,$M fi # Smooth bumpmap
16633    k.
16634  done
16635
16636#@cli skeleton : _boundary_conditions={ 0=dirichlet | 1=neumann }
16637#@cli : Compute skeleton of binary shapes using distance transform and constrained thinning.
16638#@cli : Default value: 'boundary_conditions=1'.
16639#@cli : $ shape_cupid 320 +skeleton 0
16640skeleton : check "!isnum(${1=1}) || ($1>=0 && $1<=1)"
16641  if isnum($1) bc=$1 else bc=1 noarg fi
16642  e[^-1] "Compute skeleton of binary image$? with "${"arg 1+"$bc",dirichlet,neumann"}" boundary conditions."
16643  repeat $! l[$>] s c repeat $! l[$>]
16644                    # [0] = 2D binary shape
16645    1,16,1,2        # [1] = List of boundary pixels
16646    1,16,1,2        # [2] = List of boundary pixels (next iteration)
16647
16648    # [3] = Pixels on median axis.
16649    +distance[0] 0
16650    f. "const boundary = 1;
16651        (i>j(-1)&&i>j(1)) || (i>j(0,-1)&&i>j(0,1)) || (i>j(-1,-1)&&i>j(1,1)) || (i>j(-1,1)&&i>j(1,-1))"
16652
16653    # Extract boundary pixels.
16654    f[0] ">"${-math_lib}"const boundary = "$bc";
16655          i && (!j(-1) || !j(1) || !j(0,-1) || !j(0,1))?dar_insert(#1,[x,y]); i;"
16656
16657    # Run thinning algorithm.
16658    eval ${-math_lib}"
16659      const boundary = "$bc";
16660
16661      # Lookup tables for detecting the simple points.
16662      is_removable = [ 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,
16663                       1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
16664                       0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,1,
16665                       1,0,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,0,0,0,0,
16666                       0,0,0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,1,1,0,0,1,1,0,
16667                       1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,0 ];
16668      dotm = [ 128,64,32,16,0,8,4,2,1 ];
16669      is_removed = 1;
16670
16671      # Start thinning iterations.
16672      while (is_removed,
16673        is_removed = 0;
16674        N = dar_size(#1);
16675        repeat (N,n,
16676          xc = i(#1,0,n,0,0);
16677          yc = i(#1,0,n,0,1);
16678          icc = i(#0,xc,yc);
16679          (icc && !i(#3,xc,yc))?(
16680            xp = xc - 1; yp = yc - 1;
16681            xn = xc + 1; yn = yc + 1;
16682            ref(crop(#0,xp,yp,3,3),V);
16683            val = dot(dotm,V>0);
16684            is_removable[val]?(
16685              i(#0,xc,yc) = 0;
16686              is_removed = 1;
16687              V[3]==1?(dar_insert(#2,[xp,yc]); i(#0,xp,yc) = 2);
16688              V[5]==1?(dar_insert(#2,[xn,yc]); i(#0,xn,yc) = 2);
16689              V[1]==1?(dar_insert(#2,[xc,yp]); i(#0,xc,yp) = 2);
16690              V[7]==1?(dar_insert(#2,[xc,yn]); i(#0,xc,yn) = 2);
16691            ):(dar_insert(#2,[xc,yc]); i(#0,xc,yc) = 2);
16692          )
16693        );
16694        resize(#1,1,h(#2),1,2,0);
16695        copy(i(#1),i(#2),2*h(#2));
16696        i[#2,h(#2)-1] = 0;
16697      );"
16698    k[0] > 0 thinning
16699  endl done a c endl done
16700
16701skeleton_v236 : check ${1=0}>=0
16702  e[^-1] "Compute skeleton of binary image$?."
16703  distance 0 b $1 sharpen 1e10 >= 100%
16704  repeat $! +erode[$>] 2 -[$>,-1] done
16705
16706#@cli slic : size>0,_regularity>=0,_nb_iterations>0
16707#@cli : Segment selected 2D images with superpixels, using the SLIC algorithm (Simple Linear Iterative Clustering).
16708#@cli : Scalar images of increasingly labeled pixels are returned.
16709#@cli : Reference paper: Achanta, R., Shaji, A., Smith, K., Lucchi, A., Fua, P., & Susstrunk, S. (2010). \
16710# SLIC Superpixels (No. EPFL-REPORT-149300).
16711#@cli : Default values: 'size=16', 'regularity=10' and 'nb_iterations=10'.
16712#@cli : $ image.jpg +srgb2lab slic[-1] 16 +blend shapeaverage f[-2] "j(1,0)==i && j(0,1)==i" *[-1] [-2]
16713slic : check "${1=16}>0 && ${2=10}>=0 && ${3=10}>0"
16714  e[^-1] "Segment image$? using SLIC superpixels, with size $1, regularity $2 and $3 iterations."
16715  S,m,nb_iter=${1-3}
16716  repeat $! l[$>] slices 50%
16717
16718    # Initialize superpixel centers Ck.
16719    {[max(1,round(w/$S)),max(1,round(h/$S))]},1,2,"round(([x,y]+=0.5)*="$S")"
16720
16721    # Perturb the Ck towards low gradient positions.
16722    if $S>=3
16723      +b[0] 0.7 g. xy,1 a[-2,-1] c norm. # Gradient norm with forward differences
16724      f.. "
16725        const n = round("$S"/3);
16726        const n1 = int(n/2);
16727        pos = argmin(crop(#-1,i0 - n1,i1 - n1,n,n,1));
16728        dxy = [pos%n,int(pos/n)] - n1;
16729        [ cut(i0 + dxy[0],0,w#0-1), cut(i1 + dxy[1],0,h#0-1) ]"
16730      rm.
16731    fi
16732    r. {wh},1,1,2,-1
16733    100%,1,1,{0,s},"I(#0,I#1)" a[-2,-1] c # Add superpixels colors
16734    [0],[0],1,2 eval.. "I(#-1,i0,i1) = [ x + 1,1 ]; I" s. c distance. 1 *. -1 watershed.. . rm. channels. 0,1
16735
16736    # Start iteration loop.
16737    repeat $nb_iter
16738
16739      # Assign best superpixel to each pixel.
16740      sh[2] 1 f. inf rm.
16741      eval[1] "
16742        const m = "$m";
16743        const S = "$S";
16744        k = x;
16745        xk = i0;
16746        yk = i1;
16747        Ik = (I)[2,s#0];
16748        x0 = max(xk - S,0);
16749        x1 = min(xk + S,w#0 - 1);
16750        y0 = max(yk - S,0);
16751        y1 = min(yk + S,h#0 - 1);
16752        for (y = y0, y<=y1, ++y,
16753          for (x = x0, x<=x1, ++x,
16754            delta_c = norm(I(#0,x,y) - Ik);
16755            delta_s = norm(x - xk, y - yk);
16756            delta = delta_c + m/S*delta_s;
16757            if (delta<i(#-1,x,y,0,1),
16758              I(#-1,x,y) = [ k,delta ];
16759            );
16760          )
16761        );
16762      I"
16763
16764      # Update superpixels.
16765      f[1] 0 channels[1] 0,{-2,s}
16766      eval[2] "I[#1,i0]+=[ x,y,I#0,1 ];I"
16767      s[1] c,-{1,s-1} max[2] 1 /[1,2]
16768
16769    done
16770
16771    # Remove small and isolated regions (surrounded by pixels all having the same label).
16772    k[2] channels 0 label. 0,0
16773    +area 0,0 <. {$S^2/8}
16774    {0,iM+1},1,1,1,x
16775    eval[0] ">
16776      const boundary = 1;
16777      if (i[#-1,i]>=0,
16778        N = [ j(-1,0),j(0,-1),j(1,0),j(0,1) ];
16779        repeat (size(N),k,
16780          if (N[k]!=i,
16781            i[#-1,i] = i[#-1,i]==i || i[#-1,i]==N[k]?N[k]:-1
16782          );
16783        );
16784      );
16785    i"
16786    f. "i<0?0:1" +map[0] . rm.. or[-2,-1]
16787
16788    +[0] 1 100%,100% j[0] .,0,0,0,0,1,.. rm.
16789    distance. 0 *. -1 watershed.. . rm.
16790    label. 0,0
16791  endl done
16792
16793#@cli ssd_patch : [patch],_use_fourier={ 0 | 1 },_boundary_conditions={ 0=dirichlet | 1=neumann }
16794#@cli : Compute fields of SSD between selected images and specified patch.
16795#@cli : Argument 'boundary_conditions' is valid only when 'use_fourier=0'.
16796#@cli : Default value: 'use_fourier=0' and 'boundary_conditions=0'.
16797#@cli : $ image.jpg +crop 20%,20%,35%,35% +ssd_patch[0] [1],0,0
16798ssd_patch : check ${is_image_arg\ $1} skip ${2=0},${3=0}
16799  e[^-1] "Compute field of SSD between image$? and patch $1 using "${arg\ 1+!$2,fourier,spatial}" mode."
16800  repeat $! pass$1 0 l[$>,-1]
16801    r 100%,100%,100%,${-max_s} s c
16802    repeat $!/2 l[$>,{-1-$<}]
16803      +sqr[1] val={is} rm. # Sum J(p,q)^2
16804      +sqr[0] +f[1] 1
16805      if $2
16806        convolve_fft.. . rm. # Sum I(x+p,y+q)^2
16807        mirror[1] xyz convolve_fft[0] [1] rm[1]  # Sum I(x+p,y+q).J(p,q)
16808      else
16809        correlate.. .,$3 rm. # Sum I(x+p,y+q)^2
16810        correlate[0] [1],$3 rm[1] # Sum I(x+p,y+q).J(p,q)
16811      fi
16812      *[0] -2 +[0,1] + $val
16813    endl done +
16814  endl done
16815
16816#@cli thinning : _boundary_conditions={ 0=dirichlet | 1=neumann }
16817#@cli : Compute skeleton of binary shapes using morphological thinning
16818#@cli : (beware, this is a quite slow iterative process)
16819#@cli : Default value: 'boundary_conditions=1'.
16820#@cli : $ shape_cupid 320 +thinning
16821thinning : check "!isnum(${1=1}) || ($1>=0 && $1<=1)"
16822  if isnum($1) bc=$1 else bc=1 noarg fi
16823  e[^-1] "Apply morphological thinning to binary image$? with "\
16824             ${"arg 1+"$bc",dirichlet,neumann"}" boundary conditions."
16825  repeat $! l[$>] s c repeat $! l[$>]
16826                    # [0] = 2D binary shape (current iteration)
16827    1,16,1,2        # [1] = List of boundary pixels (current iteration)
16828    1,16,1,2        # [2] = List of pixels to remove (current iteration)
16829    1,16,1,2        # [3] = List of boundary pixels (for next iteration)
16830
16831    # Extract boundary pixels.
16832    f[0] ">"${-math_lib}"const boundary = "$bc";
16833          i && (!j(-1) || !j(1) || !j(0,-1) || !j(0,1))?dar_insert(#1,[x,y]); i;"
16834
16835    # Run thinning algorithm.
16836    eval ${-math_lib}"
16837      const boundary = "$bc";
16838
16839      # Lookup tables for detecting the 8 3x3 hit&miss.
16840      hm_and = [ 231,189,231,189,122,91,94,218 ];
16841      hm_eq = [ 7,148,224,41,18,80,72,10 ];
16842
16843      # Start thinning iterations.
16844      dotm = [ 128,64,32,16,0,8,4,2,1 ];
16845      is_removed = vector8(0);
16846      ind = 1;
16847      nind = 3;
16848      it = 0;
16849      do (
16850        N = dar_size(#ind);
16851        it8 = it%8;
16852        is_removed[it8] = 0;
16853
16854        # Find removable contour points.
16855        repeat (N,n,
16856          xc = i(#ind,0,n,0,0);
16857          yc = i(#ind,0,n,0,1);
16858          icc = i(#0,xc,yc);
16859          icc?(
16860            xp = xc - 1; yp = yc - 1;
16861            xn = xc + 1; yn = yc + 1;
16862            ref(crop(#0,xp,yp,3,3),V);
16863            val = dot(dotm,V>0);
16864            (val & hm_and[it8])==hm_eq[it8]?(
16865              dar_insert(#2,[xc,yc]);
16866              is_removed[it8] = 1;
16867              V[3]==1?(dar_insert(#nind,[xp,yc]); i(#0,xp,yc) = 2);
16868              V[5]==1?(dar_insert(#nind,[xn,yc]); i(#0,xn,yc) = 2);
16869              V[1]==1?(dar_insert(#nind,[xc,yp]); i(#0,xc,yp) = 2);
16870              V[7]==1?(dar_insert(#nind,[xc,yn]); i(#0,xc,yn) = 2);
16871            ):(dar_insert(#nind,[xc,yc]); i(#0,xc,yc) = 2);
16872          );
16873        );
16874
16875        # Re-assign value '1' to contour points.
16876        N = dar_size(#nind);
16877        repeat (N,n, i(#0,i[#nind,n],i[#nind,n + h(#nind)]) = 1);
16878        _tmp = ind; ind = nind; nind = _tmp;
16879        i[#nind,h(#nind)-1] = 0;
16880
16881        # Remove matching contour points.
16882        N = dar_size(#2);
16883        repeat (N,n, i(#0,I[#2,n]) = 0);
16884        i[#2,h(#2)-1] = 0;
16885        ++it;
16886        _(while), max(is_removed)
16887      );"
16888    k[0]
16889  endl done a c endl done
16890
16891#@cli tones : N>0
16892#@cli : Get N tones masks from selected images.
16893#@cli : $ image.jpg +tones 3
16894tones : check $1>0
16895  e[^-1] "Get $1 tones masks from image$?."
16896  norm n 0,{$1-1} round 1 repeat $! l[$<]
16897    repeat $1-1 +==[0] {1+$>} done ==[0] 0
16898  endl done
16899
16900#@cli topographic_map : _nb_levels>0,_smoothness
16901#@cli : Render selected images as topographic maps.
16902#@cli : Default values: 'nb_levels=16' and 'smoothness=2'.
16903#@cli : $ image.jpg topographic_map 10
16904topographic_map : check "isint(${1=16}) && $1>0" skip ${2=2}
16905  e[^-1] "Render topographic maps from image$?, with $1 levels and smoothness $2."
16906  repeat $! l[$>]
16907    +b $2 isophotes. $1 compose_channels. + ==. 0 blend shapeaverage0
16908  endl done
16909
16910#@cli tsp : _precision>=0
16911#@cli : Try to solve the 'travelling salesman' problem, using a combination of greedy search and 2-opt algorithms.
16912#@cli : Selected images must have dimensions Nx1x1xC to represent N cities each with C-dimensional coordinates.
16913#@cli : This command re-order the selected data along the x-axis so that the point sequence becomes a shortest path.
16914#@cli : Default values: 'precision=256'.
16915#@cli : $ 256,1,1,2 rand 0,512 tsp , 512,512,1,3 repeat w#0 circle[-1] {0,I[$>]},2,1,255,255,255 \
16916# line[-1] {0,boundary=2;[I[$>],I[$>+1]]},1,255,128,0 done keep[-1]
16917tsp : check "${1=256}>=0"
16918  e[^-1] "Try to solve the 'travelling salesman' problem for pointcloud$?, with precision $1."
16919  repeat $! l[$>] n={n}
16920    if h>1" || "d>1 error[0--4] "Selected image '"{n}"' has invalid dimensions ("{[w,h,d,s]}")." fi
16921
16922    # Find initial estimate, using greedy nearest neighbor algorithm.
16923    eval "
16924      is_used = vectorw(0);
16925      next = vectorw(-1);
16926      n_initial = n_current = round(u(0,w-1));
16927      do (
16928        is_used[n_current] = 1;
16929        P_current = I[n_current];
16930        n_next = -1; dmin = inf;
16931        repeat (w,n,
16932          if (!is_used[n],
16933            d = norm(I[n] - P_current);
16934            if (d<dmin, dmin = d; n_next = n);
16935          );
16936        );
16937        if (n_next<0, next[n_current] = n_initial; break());
16938        next[n_current] = n_next;
16939        n_current = n_next;
16940        _(while), 1
16941      );
16942      resize(#-1,w,1,1,s+1,0);
16943      copy(i(0,0,0,s),next,w)"
16944    100%,1,1,{s-1},">begin(ind = 0); val = I[#0,ind]; ind = val[s]; val" rm..
16945
16946    # Improving initial estimate iteratively by 2-opt algorithm.
16947    eval "
16948      is_improved = 1;
16949      while (is_improved,
16950        is_improved = 0;
16951        nb_try = $1*w;
16952        repeat (nb_try,try,
16953          r = round(max(8,0.5*w*(try/nb_try)^0.25));
16954          i = round(u(0,w-1));
16955          ni = (i+1)%w;
16956          pi = (i-1)%w;
16957          do (j = (i + round(u(-r,r)))%w; _(while), j==i || j==ni || j==pi);
16958          nj = (j+1)%w;
16959          P_i = I[i];
16960          P_ni = I[ni];
16961          P_j = I[j];
16962          P_nj = I[nj];
16963          dist_ini = norm(P_ni - P_i);
16964          dist_jnj = norm(P_nj - P_j);
16965          dist_ij = norm(P_j - P_i);
16966          dist_ninj = norm(P_nj - P_ni);
16967          if (dist_ij + dist_ninj<dist_ini + dist_jnj,
16968            mi = (min(i,j) + 1)%w;
16969            mj = max(i,j);
16970            oi = min(mi,mj);
16971            oj = max(mi,mj);
16972            delta = oj - oi + 1;
16973            repeat (s,c, copy(i(oi,0,0,c),i(oj,0,0,c),delta,1,-1));  # Reverse path between nodes.
16974            is_improved = 1;
16975          );
16976        );
16977      )"
16978    nm $n
16979  endl done
16980
16981#@cli variance_patch : _patch_size>=1
16982#@cli : Compute variance of each images patch centered at (x,y), in selected images.
16983#@cli : Default value: 'patch_size=16'
16984#@cli : $ image.jpg +variance_patch
16985variance_patch : check "isint(${1=16}) && $1>=1"
16986  e[^-1] "Compute variance of image patches in image$?, with patch size $1."
16987  $1,$1,1,1,1 normalize_sum.
16988  repeat $!-1 l[$>,-1]
16989    +sqr[0] convolve[0,2] [1]
16990    sqr[0] rv[0,2] -[0,2] max[0] 0
16991  endl done rm.
16992
16993#---------------------------------
16994#
16995#@cli :: Image Drawing
16996#
16997#---------------------------------
16998
16999#@cli arrow : x0[%],y0[%],x1[%],y1[%],_thickness[%]>=0,_head_length[%]>=0,_head_thickness[%]>=0,_opacity,\
17000# _pattern,_color1,...
17001#@cli : Draw specified arrow on selected images.
17002#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
17003#@cli : even if a color is specified. If a pattern is specified, the arrow is
17004#@cli : drawn outlined instead of filled.
17005#@cli : Default values: 'thickness=1%', 'head_length=10%', 'head_thickness=3%', 'opacity=1', 'pattern=(undefined)' \
17006# and 'color1=0'.
17007#@cli : $ 400,400,1,3 repeat 100 arrow 50%,50%,{u(100)}%,{u(100)}%,3,20,10,0.3,${-rgb} done
17008arrow : check "${5=1%}>=0 && ${6=10%}>=0 && ${7=3%}" skip ${8=1}
17009  e[^-1] "Draw arrow in image$?, from ($1,$2) to ($3,$4), with thickness $5, head length $6,
17010          head_thickness $7 and opacity $8."
17011  repeat $! l[$>]
17012    polygon. 7,{"
17013      x0 = "${"is_percent $1"}"?(w-1)*$1:$1;
17014      y0 = "${"is_percent $2"}"?(h-1)*$2:$2;
17015      x1 = "${"is_percent $3"}"?(w-1)*$3:$3;
17016      y1 = "${"is_percent $4"}"?(h-1)*$4:$4;
17017      p0 = [x0,y0];
17018      dp = [x1,y1]-=p0;
17019      l = norm2(dp);                       # arrow length
17020      t = "${"is_percent $5"}"?l*$5:$5;   # thickness
17021      hl = "${"is_percent $6"}"?l*$6:$6;  # head length
17022      ht = "${"is_percent $7"}"?l*$7:$7;  # head thickness
17023      lmhl = l - hl;
17024      X = mul([0,-t,lmhl,-t,lmhl,-ht,l,0,lmhl,ht,lmhl,t,0,t],rot(-atan2(dp[1],dp[0])),2);
17025      X+=[p0,p0,p0,p0,p0,p0,p0]"},${8--1}
17026  endl done
17027
17028#@cli axes : x0,x1,y0,y1,_font_height>=0,_opacity,_pattern,_color1,...
17029#@cli : Draw xy-axes on selected images.
17030#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
17031#@cli : even if a color is specified.
17032#@cli : To draw only one x-axis at row Y, set both 'y0' and 'y1' to Y.
17033#@cli : To draw only one y-axis at column X, set both 'x0' and 'x1' to X.
17034#@cli : Default values: 'font_height=14', 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
17035#@cli : $ 400,400,1,3,255 axes -1,1,1,-1
17036axes : check "isint(${5=14}) && $5>=0 && ${6=1}>=0" skip ${7=0},${8=0}
17037  if ${"is_pattern \"$7\""}
17038    e[0--3] "Draw xy-axes on image$?, with x-range ($1,$2), y-range ($3,$4), font height $5, opacity $6,
17039             pattern $7 and color (${8--1})."
17040    pattern=$7 color=${8--1}
17041  else
17042    e[0--3] "Draw xy-axes on image$?, with x-range ($1,$2), y-range ($3,$4), font height $5, opacity $6
17043             and color (${7--1})."
17044    pattern=0xFFFFFFFF color=${7--1}
17045  fi
17046  if !$5" || "!$6 return fi
17047  mx={min($1,$2)} Mx={max($1,$2)}
17048  my={min($3,$4)} My={max($3,$4)}
17049
17050  # Start drawing axes on selected images.
17051  repeat $! l[$>]
17052    w1={0,w-1} h1={0,h-1}
17053
17054    # Determine number of axes tick marks.
17055    if $1!=$2 u=${"_axes[] $1,$2,{0.3*w/$5}"} offx={arg(1,$u)} deltax={arg(2,$u)} fi
17056    if $3!=$4 u=${"_axes[] $3,$4,{0.3*h/$5}"} offy={arg(1,$u)} deltay={arg(2,$u)} fi
17057
17058    # Draw x-axis.
17059    is_0x=0
17060    if $3==$4 y0=$3 else y0={v=-($my)*$h1/($My-$my);if($4>=$3,v,$h1-v)} fi
17061    sty={if($y0>$h1-$5,-1,1)}
17062
17063    if $1!=$2" && "$y0>=0" && "$y0<=$h1
17064      line 0,$y0,$w1,$y0,$6,$pattern,$color
17065      4,4,1,1,x<=y +mirror. y rows. 1,3 a[-2,-1] y .,.,1,[0] fc. $color
17066      if $2>=$1 j[0] .,{$w1-3},{$y0-3},0,0,$6,..
17067      else mirror.. x j[0] .,0,{$y0-3},0,0,$6,..
17068      fi
17069      rm[-2,-1]
17070
17071      i=0 do
17072        val={_$offx+$i*$deltax} i+=1
17073        if $val>=$mx" && "$val<=$Mx
17074          x={v=($val-$mx)*$w1/($Mx-$mx);if($2>=$1,v,$w1-v)}
17075          line $x,{$y0-1},$x,{$y0+1},$6,$pattern,$color
17076          if $val
17077            0 t. $val,0,0,$5,1,1 100%,100%,1,[0] fc. $color
17078            j[0] .,{max(0,min($w1-w,$x-w/2))},{if($sty>0,$y0+3,$y0-h-3)},0,0,$6,.. rm[-2,-1]
17079          else is_0x=1
17080          fi
17081        fi
17082      while $val<$Mx
17083    fi
17084
17085    # Draw y-axis.
17086    is_0y=0
17087    if $1==$2 x0=$1 else x0={v=-($mx)*$w1/($Mx-$mx);if($2>=$1,v,$w1-v)} fi
17088    stx={if($x0>$w1-$5,-1,1)}
17089
17090    if $3!=$4" && "$x0>=0" && "$x0<=$w1
17091      line $x0,0,$x0,$h1,$6,$pattern,$color
17092      4,4,1,1,x>=y +mirror. x z. 1,3 a[-2,-1] x .,.,1,[0] fc. $color
17093      if $4>=$3 j[0] .,{$x0-3},{$h1-3},0,0,$6,..
17094      else mirror.. y j[0] .,{$x0-3},0,0,0,$6,..
17095      fi
17096      rm[-2,-1]
17097
17098      i=0 do
17099        val={_$offy+$i*$deltay} i+=1
17100        if $val>=$my" && "$val<=$My
17101          y={v=($val-$my)*$h1/($My-$my);if($4>=$3,v,$h1-v)}
17102          line {$x0-1},$y,{$x0+1},$y,$6,$pattern,$color
17103          if $val
17104            0 t. $val,0,0,$5,1,1 100%,100%,1,[0] fc. $color
17105            j[0] .,{if($stx>0,$x0+6,$x0-w-6)},{max(0,min($h1-h,$y-h/2))},0,0,$6,.. rm[-2,-1]
17106          else is_0y=1
17107          fi
17108        fi
17109      while $val<$My
17110    fi
17111
17112    # Draw origin, if necessary.
17113    if $is_0x" || "$is_0y
17114      0 t. 0,0,0,$5,1,1 100%,100%,1,[0] fc. $color
17115      j[0] .,{if($stx>0,$x0+6,$x0-w-6)},{if($sty>0,$y0+3,$y0-h-3)},0,0,$6,.. rm[-2,-1]
17116    fi
17117
17118  endl done
17119
17120# Return optimal (offset0,scale) to display input (min,max) values.
17121# $1 = min value, $2 = max value, $3 = max number of tags(>=1).
17122_axes :
17123  n={max(1,round($3))}
17124  d={abs($2-$1)/($n-1)}
17125  s={10^round(log10($d))}
17126  m={round(min($1,$2),$s,-1)}
17127  M={round(max($1,$2),$s,1)}
17128  do N={1+round(($M-$m)/$s,1,1)} s={2*$s} while $N>$n
17129  u $m,{$s/2}
17130
17131#@cli ball : _size>0, _R,_G,_B,0<=_specular_light<=8,0<=_specular_size<=8,_shadow>=0
17132#@cli : Input a 2D RGBA colored ball sprite.
17133#@cli : Default values: 'size=64', 'R=255', 'G=R', 'B=R', 'specular_light=0.8', 'specular_size=1' and 'shading=1.5'.
17134#@cli : $ repeat 9 ball {1.5^($>+2)},${-rgb} done append x
17135ball : check "${1=64}>0 && ${5=0.8}>=0 && $5<=8 && ${6=1}>=0 && $6<=8 && ${7=1.5}>=0" skip ${2=255},${3=$2},${4=$3}
17136  e[^-1] "Input $1x$1 ball with color (${2-4}), specular light $5, specular size $6 and shadow factor $7."
17137  l[]
17138  {2*$1},{2*$1} = 1,65%,30% distance 1 * -1
17139  +n 0,1 ^[1] $7 *[1] 1.4 +*[1] $3 +*[1] $4 *[1] $2 a[^0] c
17140  >=[0] {100-10*$6}% b[0] {3*$6}% n[0] 0,{$5*255} rv + c 0,255
17141  100%,100% circle[1] 50%,50%,34%,1,1 *[0] [1] *. 255 a c
17142  r $1,$1,1,4,2
17143  endl
17144
17145# chromeball64x64 : Output a 64x64 GreyA chrome ball sprite.
17146# ${1-3} = R,G,B
17147chromeball64x64 :
17148  base642img[] \
17149"MiB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgNjQgMSAyICMzMzY0CnicxZl5VJNnvsfT0mM7ntPp9N7paefe6ZzOmWlnemfGmTpzer0dO1W"\
17150"RnQCBQPZ9TwhhDQkYwyKQAAlbEjZBthgRRBahFamAggp1KS7QIgKyk7DIpoCQd54XtComyPLH/f7xvn/k/Xyf3/N7n+X35EUgLOqN9z/DHcqtaW69cq"\
17151"EmK8z7s3csP2ZZO/7ILGzpHhwaHBzoHxgA9/vt55M8fvPaxuh37NOu9AyNjBpNY+PjE+OmkYGuOzfb73XURu1589X0zx2PXu8ZMo0Dbnh41DQxMT5mH"\
17152"O7vam/v7O5sUH35Cgebvyiaf+g3jRuHB/oGjeMPpqamHkyMDvb3g3iG+7tvnjm8a71e/Af+5PXOYdPo4MCwaWr24cO5ubnZ2ekHY6PDgwNDRtNQT9u3"\
17153"mRirqXztt5Laa10jo0N9fcaZ+UWghflHwGBudnpywjTUNzjcd/faN3kBH1vGX98VWdd6b2B4oKt3fH7JbF5+vDC/ADQPm0xPTRr779/vuNZUdUzyT0t"\
17154"9eON/5Webb3f8eOdqW9/Eo2XI/Gh6auYxbAMMHsIO48OD/TcvnCvNltjbvIz/I/RMXVPTlaa6xrauwbG55eU5wAObpZmJialpoJnpSZCZ63WV+rRgxz"\
17155"fW9n0Xr/RsZVX1mdOV9a237o3OLT+amJw3Q9DS3IRxdNQ4NmYcn4YN7l8+U5ybIDq4pgsf4fPrjhcWFhScqL54vWt0Zn5++sHcotkMLc1OjptGR4yjw"\
17156"6MPZqbBu+yoLyvURfjueQF/z1V98YRGo8vWV128PTTzeGlpeWkZtA5B5sVHczNTD4CmZ+Zmp8b6Oy7XFB9NDOV+8hz+1j7Jt9+kKVIyC0433DItmiHz"\
17157"CvtEZvPSit/j+bnpsYGbF6tL8tIiRfRn4+C1PzGKWwuVCRm5xWevDS5CVmRemp0Y6m1rqCrOT1eKBS4/peA/XZKu1mkVKVn5ZQ0/TJmt8dDSeO8PNy6"\
17158"fK9fnZqllIu4fnuA2u4VnO0rU8WmZheUN9x5ZxaHFwatN9bVnSgtzstKOBAkIO1b595007W25CQkpmUUwb7V981T3pXM1VWXFBUcztcpQIWfX6rj9G6"\
17159"nqXotGmZCUDvjO2cWFhWXL/OTdK9/WlJ8yFORkalXhIt5qAL/4l/hSz8UUhVKlKyiv7xibmRibsxzDo77vL56tKDXk52TqUuQiAetTmP/USXm1pzFZo"\
17160"VADvuHOkGlocGzJIr881tlaV1kC+KwMXZS/gI0Er+DNL7zUV+9d1CgUqvTC8gsdIyO9vabHljMwP3S7sbr0eN7R7KN5sf4CLu2XIHv7MXFX7rZmg/jT"\
17161"CysutBuNvT2j81Yy+OBea22ZIe9YblFpPOAZuxGIXQd9ws/fbTMoFIm6/NPfXu8Z6e8bsZIAEMCtxqoSvf54WbUK5pEIm332nsJTP7RXqRSJaTkl3zS"\
17162"13e3pG5mxnABoebKrpe5MWXllXXUc4JnEn73tZo+kpH9/u+FofGJSpr6ytvlGR49VHloytrfW19bVXy6RifgcJuXXv8E6OKIP1d9oOa1JUGtyT5yuvX"\
17163"yza8gqDz0e6/zuUvOVCxkhfjwOk/znv1Id7d0Z+ZcvNeiTEsEELK6qb7kzMG2Vh5ZMP95ovXIqOsCXx2aQ93zFdbZz8ZKX19bX5CSqtbmG01+f/+6u0"\
17164"eokBKNo4PZ3X6cG+/G5LDrZ1t3fzdbBjZqgLz97KkOdmlVUUl7deKN7fNHqNFgauX0+O9zfl8dh0chIfCjazs4ZxY07WlxWlKXRZBcYSmsab9wzPlxc"\
17165"smzxeKhFHx0oFHDZTBoJxZARHA46ItG86IxCg/5Yhi47/3hpdX1L293eQdP0/Mse5ocdlUlhAQKQPTqV6MmNYSDt7J3d0dzIjAKDoSA3J6/wRGlV9df"\
17166"nGi5dvdU1MDa9ZjbO99bnxAGey2bQyHiUUCVEO9k5unp4sw9r8/XHiwoLi/T6E8XFJWUV1bUXWm/c6TbNLS4/DWN5prvpZDrMc1gMKgmHZGskZHdHey"\
17167"ekhxctVJVTdBzIcBy20RtOlFR8Xdf43Z2eQePk9KPHy0vzk73f158u0MaGBwhA78lErAMxK5qHQdo7OiM90ARfeVoewE+A5oEMer3hZFnVuebrt37s6"\
17168"ukb6Ou80VRbebIgKzkmPIDPolNJeMy/XHJVEgba2cEJGHhhqP6RqcdAwydLTpVXlJWcMBiKSyrONjRdam48X1dzSl+QBy8d8VHSAB6LRiHhvP/+xVHN"\
17169"kQCKm5PjioEPjuYXrtTmFukNJWWnSk8CA8PJU2WlhsLcrAxtilqVmJAQHxd9ONSfx6BRyVj0xx+mZKkihBgXJydnFzcPT28snswSSaMTkjOOFR4HPSg"\
17170"qKsjNTE2Ii4mOlB+WyQ7LwfWQNNiPS6fRKViv99+JyNaqZCwvV8C7url7on2weBKFwRUGS+VH4hRKpTI2SiYVBwXCCgoODgkJDg4KFAk4DCabhkHtQH"\
17171"DS0zPigyhebq6urkg3d5SXNwZHIJKpNPAAl8fj8/krFz5fIPAV+MISCAQ8LpvN51N99iMQX4J9QxslJPt4uiNh3tMLhIDDE0kUKpVGpzOYLBaLvSIOE"\
17172"HxnsZhMFs8vwI/s80cE4r9CFGk6VbiAjPP2RHl4oFBeXmhvDHAgEElkMmxCg22eiQ7E5PmFSPwIaHgPJUmOJCVGifl08Dp90F5eIABvHwwWi3tqQQEm"\
17173"K6I9EZ3FFYllMi7uILz+/40TEBkXJfXns8GIIOJxGB9v72cGRBKRBExgARtgRaMzuQJ/SUS0mIb9aGXzxzOE4TIJWE/4YErCrxW4AAEcTyAQSLBWcAq"\
17174"VQgX54Pj6B4VHxcoFBORqFfQ5nswPDPL35cLpFfBXXOg0Gtzac4JRFofL9w2UhMuiFTEhVPyTQvAtNAbDEAj5bI6vn5/IPyAwQOQH+/B4XM5q5tnwKw"\
17175"Ao+DU4TB4dHaOIk7II7k+LsD97uLmTWWwGnSMQ+geGhIaKxWCYgMESFBTgD8tPKBSKwA8SqVQep1QqYqOlXCLuw6f1h42DowMSR6WQaWy+UBQUKpWAB"\
17176"8ElFHYSiyVhEnCXyCIjo6JiVMlqRbRczCUTv3pWP/3C1fagiw/INoXJEwiD4KclUglMAwupTA4GvTwyVhGfqErVpSnB7GGTSd4/e64A+/jg/v0O7j44"\
17177"YMDhCQNDACgGAx0oRCyVRUYdiYlVqlN1uvRMTZxUxKURiPj/fqEA3PPVflsndx8Chc7h8EXB0jAgqXjFAPBH4hLUKdrMLF1aYmQwj0HCk6l/ebH+tLH"\
17178"be8DBHUOiMUCeRSHhh+UR0dERMqBDkXHxquRUjS5dl5oYLRawqAQsjbMXsUav79t7wBlNoIM3xfUNORyjUCYmJ6mTU5ISkmBUk5qckhgVyqdRiBgMU2"\
17179"T7cgX/+sEvv3JGE+nwdBNKY1O06ZnpWm16SoIqJS01Me5IpCyYSyHgfbywfMna6nlV/2e31xlLptEoZDpfHJWoyy3ILyjIzdKoosOCgvxYVCyYFyhPi"\
17180"lj+hSUa6BOXvUgKk4rxROMYAWFH1FpNSnJ8ZFiwL5OC9/ZCodAoT4JYEfuJFRyBeNfDwZnEpHq7ubhj6Twhn8nkMYk+aA83Dx8Sm0OlsMK12ohPreJA"\
17181"n5FcPMg0vJu9vbMXDuPh5unuam9r6+BJF/n5R6Tk5aei3loPRyB22vliUHgqztV23wEHuwOObo62SAyJzpfEZxflpBI/WJ+G9ZZdmIxL9EG5OhzYb4c"\
17182"kEJnCUHmMUp2uVXj/6tX0iv6Hmp6nUYaHHkrUKmLjlOq0rOwU6u4dG6RX9ME+ukydW1FZXl6kCafs+91mWKCdu7Hqijvj8JY9dadajf3w1cgzvUutWV"\
17183"hbdnQl/XOD9J9y5iwXTp2itzdAF1s+PKxoTPoKh7eTXwr8RZlo6+H7e9anYTX82hptE7FO6M80ibKM7yzfCA1kllvC32veIA5U+PL5/722jeMWDHY2b"\
17184"gaHoGNr+JLN4RAU/gIeulkcMiOfwz9/xaixJNOzcfDmzc3jEHTmJ/7QVnAI8n6CfzC7Nb7vyZ9pmVvDIUi4OnK2kLwnAaysiJFbxSEInsw2A1vnLwF+"\
17185"39ZxCALLcvp2eAkCsY3wIagJ8fvt4NDCTuy2eGifent8YMX2eN2mlq2XdXZwe/w2m4d6/r/5DW1Z6/DbDKClZXt8RfH2eHXE9nim2/b43e9tC596HbG"\
17186"lveepKhGIbSWACgq2beAL74IF+PrW+TJ4/2BtnbeH+R19W8VbVvfPgK3yzk/Kh+6t4c1P6wfUlvCF3T8VIKVb4ROe1U+/7N88fu3549CXm64hJv+AeF"\
17187"6CTeILTmsK0NhN4WYmYq02Y2DmvIQjEHLrH37WBk+1gCMQRCtfHdZq0s4ijkD8tXMj+P2PrODg+JXx6u3kzLpH8C+urU93e69HA9ng2q3TfbwNfIFGO"\
17188"Fda7sUF0kZP8L8S1K05Ay80hP52g/CqdnzOTa1o6QFqqdb57dlp7bl/A5UFagkxIDQ1IDEgMSAjNTIKeJxz941i0M/Iz03VTykpTs4ozS3Qd0ktzi7J"\
17189"L9BPzigCiscXF2SkFqXqFeSlMwAAcSYQbQ=="
17190  s. c +apply_gamma.. 0.05 b. 3 n. 0,150 n... 0,1 i[-4] 100%,100%,1,3 fc[-4] ${1-3} *[-4,-3] +[-3,-1]
17191  c.. 0,255 a[-2,-1] c
17192
17193#@cli chessboard : size1>0,_size2>0,_offset1,_offset2,_angle,_opacity,_color1,...,_color2,...
17194#@cli : Draw chessboard on selected images.
17195#@cli : Default values: 'size2=size1', 'offset1=offset2=0', 'angle=0', 'opacity=1', 'color1=0' and 'color2=255'.
17196#@cli : $ image.jpg chessboard 32,32,0,0,25,0.3,255,128,0,0,128,255
17197chessboard : check "$1>0 && ${2=$1}>0" skip ${3=0},${4=0},${5=0},${6=1},${7=0},${8=255}
17198  e[^-1] "Draw chessboard on image$?, with sizes ($1,$2), offsets ($3,$4), angle $5 deg., opacity $6 and
17199          colors (${7--1})."
17200  i[0] (${7--1}) r[0] {{0,w}/2},1,1,2,-1 permute[0] cyzx
17201  repeat $!-1
17202    w={w} h={h} theta={$5*pi/180}
17203    ($3,{$3+$w-1};$3,{$3+$w-1}^$4,$4;{$4+$h-1},{$4+$h-1}) r. $w,$h,1,2,3
17204    r. {$w*$h},2,1,1,-1
17205    i.. ({cos($theta)},{-sin($theta)};{sin($theta)},{cos($theta)}) m*[-2,-1]
17206    r. $w,$h,1,2,-1
17207    %. {$1+$2} >=. $1 s. c xor[-2,-1] map. [0] r. 100%,100%,1,..
17208    j.. .,0,0,0,0,$6 rm.
17209  mv. 1 done rm[0]
17210
17211#@cli cie1931
17212#@cli : Draw CIE-1931 chromaticity diagram on selected images.
17213#@cli : $ 500,400,1,3 cie1931
17214cie1931 :
17215  e[^-1] "Draw CIE-1931 chromaticity diagram on image$?."
17216
17217  # Generate convex hull of visible colors, as a 3D object.
17218  (67.5;73.5;109.5;103.5;51.5;100.5;37;36)  # Header
17219  (280,420,0;171,829,0;158,820,0;153,816,0;147,811,0;140,804,0;132,794,0;121,776,0;106,747,0;88,701,0;\  # Vertices.
17220  65,633,0;42,539,0;20,421,0;5,295,0;0,179,0;4,115,0;10,83,0;16,61,0;25,38,0;35,21,0;47,10,0;58,3,0;\
17221  71,0,0;92,1,0;111,7,0;151,28,0;189,52,0;226,79,0;262,109,0;298,141,0;334,175,0;370,209,0;405,244,0;\
17222  441,279,0;475,313,0;509,347,0;731,568,0)
17223  xM=731 yM=829
17224  2,{h-1},1,1,3,0 1,{h},1,1,'y' ++. 1 %. {h} +[-2,-1] 1 a[-3--1] x  # Primitives.
17225  3,{h},1,1,160 1,{h},1,1,1  # Colors + opacities.
17226  y[-4--2] a[-5--1] y mv. 0
17227
17228  # Generate RGB triangle of displayable colors.
17229  xR=636 yR=504 xG=297 yG=234 xB=147 yB=774
17230  512,512,1,3 triangle_shade. 0,0,{w-1},0,0,{h-1},""255,0,0,""0,255,0,""0,0,255 rgb2srgb.
17231  +compose_channels. max +. 1e-8 /[-2,-1] *. 255
17232  i.. (67.5;73.5;109.5;103.5;51.5;100.5;3;1;$xR;$yR;-0.01;$xG;$yG;-0.01;$xB;$yB;-0.01;\
17233       9;0;1;2;0;0;511;0;0;511;-128;512;512;3)
17234  y. (1) a[-3--1] y mv. 1
17235
17236  # Draw chroma diagram.
17237  repeat $!-2
17238    to_rgb. fc. 255,255,255 grid. 10%,10%,0,0,0.3,0xCCCCCCCC,1,0
17239    100%,100%,1,3
17240    +*3d[0,1] {(w-8)/$xM},{(h-32)/$yM}
17241    j3d... .,2,30,0,1,2
17242    +!=... 0 distance. 1 *. -1 watershed[-4] . rm. /... 1.5
17243    j3d... .,2,30,0,1,2
17244    p3d. 1 p3d. 2 col3d. 128 j3d... .,2,30,0,1,1 rm.
17245    {-2,w},{-2,h} j3d. ..,2,30,0,1,2 rm..
17246    +erode. 4 -. .. ==. 0 *[-3,-1]
17247    a[-2,-1] c blend[-2,-1] alpha
17248    100%,100%,1,1,255 axes. 0,0.75,0.85,0,14,1 +erode. 3 negate. to_rgb..
17249    j... ..,0,0,0,0,1,.,400 rm[-2,-1]
17250  mv. 2 done rm[0,1]
17251
17252#@cli circle : x[%],y[%],R[%],_opacity,_pattern,_color1,...
17253#@cli : Draw specified colored circle on selected images.
17254#@cli : A radius of '100%' stands for 'sqrt(width^2+height^2)'.
17255#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
17256#@cli : even if a color is specified. If a pattern is specified, the circle is
17257#@cli : drawn outlined instead of filled.
17258#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
17259#@cli : $ image.jpg repeat 300 circle {u(100)}%,{u(100)}%,{u(30)},0.3,${-rgb} done circle 50%,50%,100,0.7,255
17260circle : skip ${4=1},${5=0},${6=0}
17261  if ${"is_pattern \"$5\""}
17262    e[0--3] "Draw outlined circle at ($1,$2) with radius $3 on image$?, with opacity $4, pattern $5 and
17263             color (${6--1})."
17264  else
17265    e[0--3] "Draw filled circle at ($1,$2) with radius $3 on image$?, with opacity $4 and color (${5--1})."
17266  fi
17267  ellipse $1,$2,$3,$3,0,${4--1}
17268
17269#@cli close_binary : 0<=_endpoint_rate<=100,_endpoint_connectivity>=0,_spline_distmax>=0,_segment_distmax>=0,\
17270# 0<=_spline_anglemax<=180,_spline_roundness>=0,_area_min>=0,_allow_self_intersection={ 0 | 1 }
17271#@cli : Automatically close open shapes in binary images (defining white strokes on black background).
17272#@cli : Default values: 'endpoint_rate=75', 'endpoint_connectivity=2', 'spline_distmax=80', 'segment_distmax=20', \
17273# 'spline_anglemax=90', 'spline_roundness=1','area_min=100', 'allow_self_intersection=1'.
17274close_binary :
17275  check "${1=75}>=0 && $1<=100 && ${2=2}>=0 && ${3=80}>=0 && ${4=20}>=0 && ${5=90}>=0 && $5<=180 && ${6=1}>=0 &&
17276         ${7=100}>=0 && isnum(${8=1})"
17277  e[^-1] "Close open shapes in binary image$?, with endpoint rate $1, endpoint connectivity $2, spline max distance $3,
17278          segment max distance $4, spline max angle $5, spline roundness $6, area min $7 and self intersections "\
17279          ${"arg 1+!$8,allowed,\"not allowed\""}"."
17280
17281  # Set algorithm parameters.
17282  endpoint_threshold={100-$1}       # in [0,100]
17283  endpoint_connectivity={round($2)}
17284  spline_distmax=$3                 # in px
17285  segment_distmax=$4                # in px
17286  spline_anglemax=$5                # in deg
17287  spline_roundness=$6
17288  area_min=$7                       # in px
17289  allow_self_intersections={!!$8}   # 0 or 1
17290
17291  # Define useful functions to navigate through edgels.
17292  _edgel_lib="
17293    begin(
17294      pn = [ 0,1,1,1,-1,0,-1,1,0,-1,-1,-1,1,0,1,-1 ];
17295      pp = [ 0,-1,1,-1,1,0,1,1,0,1,-1,1,-1,0,-1,-1 ];
17296    );
17297
17298    next(img,p) = ( # Used when shape is made of '!=0' (in 8-connexity)
17299      p0 = p#[0,2] + pn[4*p#[2],2];
17300      p1 = p#[0,2] + pn[4*p#[2] + 2,2];
17301      case = !!i(#img,p0[0],p0[1],0,0) + 2*!!i(#img,p1[0],p1[1],0,0);
17302      !case?[ p[0],p[1],(p[2]+1)%4 ]:case==1?[ p0[0],p0[1],p[2] ]:[ p1[0],p1[1],(p[2]-1)%4 ];
17303    );
17304
17305    previous(img,p) = ( # Used when shape is made of '!=0' (in 8-connexity)
17306      p0 = p#[0,2] + pp[4*p#[2],2];
17307      p1 = p#[0,2] + pp[4*p#[2] + 2,2];
17308      case = !!i(#img,p0[0],p0[1],0,0) + 2*!!i(#img,p1[0],p1[1],0,0);
17309      !case?[ p[0],p[1],(p[2]-1)%4 ]:case==1?[ p0[0],p0[1],p[2] ]:[ p1[0],p1[1],(p[2]+1)%4 ];
17310    );
17311
17312    next2(img,p) = ( # Used when shape is made of '{ 0 | 4 }' (in 4-connexity)
17313      p0 = p#[0,2] + pn[4*p#[2],2];
17314      p1 = p#[0,2] + pn[4*p#[2] + 2,2];
17315      case = !(i(#img,p0[0],p0[1],0,0)&3) + 2*!(i(#img,p1[0],p1[1],0,0)&3);
17316      case==1?[ p0[0],p0[1],p[2] ]:case==3?[ p1[0],p1[1],(p[2]-1)%4 ]:[ p[0],p[1],(p[2]+1)%4 ];
17317    );
17318
17319    next3(img,p) = ( # Used when shape is made of '{ 1 | 2 }' (in 4-connexity)
17320      p0 = p#[0,2] + pn[4*p#[2],2];
17321      p1 = p#[0,2] + pn[4*p#[2] + 2,2];
17322      val1 = i(#img,p0[0],p0[1],0,0);
17323      val2 = i(#img,p1[0],p1[1],0,0);
17324      case = (val1==1 || val1==2) + 2*(val2==1 || val2==2);
17325      case==1?[ p0[0],p0[1],p[2] ]:case==3?[ p1[0],p1[1],(p[2]-1)%4 ]:[ p[0],p[1],(p[2]+1)%4 ];
17326    );
17327
17328    vec(p) = (
17329      ang = i(p[0],p[1],0,p[2])*pi/180;
17330      [ cos(ang), sin(ang) ];
17331    );"
17332
17333  repeat $! l[$>] nm={n}
17334
17335    # Ensure input is a 2D binary image.
17336    slices 50% channels 0 > 0
17337    nm. strokes
17338
17339    # Extract primary edgel normals.
17340    100%,100%,1,4,"
17341      ref(crop(#"$strokes",x - 1,y - 1,0,0,3,3,1,1,1),N);
17342      N[4]?[ N[5]?-2:0, N[7]?-2:90, N[3]?-2:180, N[1]?-2:270 ]:[-2,-2,-2,-2];
17343    " nm. e_normals
17344
17345    # Smooth edgel normals.
17346    f[e_normals] $_edgel_lib"
17347      const boundary = 1;
17348      i<-1?-2:(
17349        ppos = npos = pos = [ x,y,c ];
17350        u = vec(pos);
17351        for (t = 1, t<=5, ++t,
17352          ppos = previous(#"$strokes",ppos);
17353          npos = next(#"$strokes",npos);
17354          if (ppos==npos, break());
17355          w = exp(-t^2/30);
17356          u+=w*vec(ppos);
17357          u+=w*vec(npos);
17358        );
17359        ang = (atan2(u[1],u[0])*180/pi)%360;
17360      )"
17361
17362    # Estimate edgel curvatures.
17363    +f[e_normals] $_edgel_lib"
17364      const boundary = 1;
17365      i<-1?-2:(
17366        pos = [ x,y,c ];
17367        ppos = previous(#"$strokes",pos);
17368        npos = next(#"$strokes",pos);
17369        pu = vec(ppos);
17370        nu = vec(npos);
17371        du = (nu - pu)/2;
17372        cr = cross([vec(pos),0],[du,0]);
17373        norm(du)*sign(cr[2]);
17374      )"
17375    nm. e_curvatures
17376
17377    # Smooth edgel curvatures.
17378    +f[e_curvatures] $_edgel_lib"
17379      const boundary = 1;
17380      i<-1?-2:(
17381        ppos = npos = pos = [ x,y,c ];
17382        val = i; sumw = 1;
17383        for (t = 1, t<=5, ++t,
17384          ppos = previous(#"$strokes",ppos);
17385          npos = next(#"$strokes",npos);
17386          if (ppos==npos, break());
17387          w = exp(-t^2/30);
17388          val+=w*i(ppos[0],ppos[1],0,ppos[2]);
17389          val+=w*i(npos[0],npos[1],0,npos[2]);
17390          sumw+=2*w;
17391        );
17392        val/sumw;
17393      )"
17394    nm. e_smooth_curvatures
17395
17396    # Estimate stroke radius.
17397    +distance[strokes] 0
17398    f. "
17399      i<1-0.01||i>1+0.01?0:(
17400        p = x; q = y; d = 1;
17401        for (is_better = 1, is_better && d<32,
17402          next_p = p;
17403          next_q = q;
17404          is_better = 0;
17405          pp = p - 1;
17406          np = p + 1;
17407          pq = q - 1;
17408          nq = q + 1;
17409          (nd = i(pp,pq))>d?(d = nd; next_p = pp; next_q = pq; is_better = 1);
17410          (nd = i(p, pq))>d?(d = nd; next_p = p;  next_q = pq; is_better = 1);
17411          (nd = i(np,pq))>d?(d = nd; next_p = np; next_q = pq; is_better = 1);
17412          (nd = i(pp,q))>d?(d = nd; next_p = pp; next_q = q; is_better = 1);
17413          (nd = i(np,q))>d?(d = nd; next_p = np; next_q = q; is_better = 1);
17414          (nd = i(pp,nq))>d?(d = nd; next_p = pp; next_q = nq; is_better = 1);
17415          (nd = i(p, nq))>d?(d = nd; next_p = p;  next_q = nq; is_better = 1);
17416          (nd = i(np,nq))>d?(d = nd; next_p = np; next_q = nq; is_better = 1);
17417          p = next_p;
17418          q = next_q;
17419        );
17420        d)"
17421    nm. stroke_radii
17422
17423    # Detect end points.
17424    compose_channels[e_smooth_curvatures] max nm[e_smooth_curvatures] smooth_curvatures
17425    +compose_channels[e_curvatures] max nm. curvatures
17426
17427    f. "i(#"$smooth_curvatures")>=("$endpoint_threshold"%)/(max(1,i(#"$stroke_radii"))) ||
17428        i>=max(0.25,"$endpoint_threshold"%)"
17429    label_fg. 0,1 nm. keypoints
17430    if iM>0
17431      {iM},1,1,3,-1 nm. keycoords
17432      f[keypoints] "> # Keep only a single point on connected regions of high curvatures.
17433        ret = 0;
17434        if (i,
17435          j = i - 1;
17436          old_max = I[#"$keycoords",j];
17437          kappa = i(#"$smooth_curvatures");
17438          if (kappa>old_max[2],
17439            I[#"$keycoords",j] = [ x,y,kappa ];
17440            i(#"$keypoints",old_max[0],old_max[1]) = 0;
17441            ret = 1;
17442          );
17443        ); ret"
17444      channels[keycoords] 0,3 # Channels 2,3 = number of connexions and normal angle for each keypoint.
17445    fi
17446    rm[smooth_curvatures,stroke_radii,keypoints]
17447
17448    # Estimate point normals.
17449    if !narg($keycoords)
17450      rm[e_normals,e_curvatures] [strokes] nm. new_strokes
17451    else
17452      f[keycoords] "
17453        P = (I)[0,2];
17454        angles = I(#"$e_normals",P);
17455        U = [ 0,0 ];
17456        repeat (size(angles),k,
17457          if (angles[k]>=0,
17458            w = max(1e-8,i(#"$e_curvatures",P[0],P[1],0,k))^2;
17459            ang = angles[k]*pi/180;
17460            U += w*[ cos(ang),sin(ang) ];
17461          );
17462        );
17463        [ P,0,(atan2(U[1],U[0])*180/pi)%360 ]"
17464      rm[e_normals,e_curvatures]
17465
17466      # Detect pairs of keypoints preserving the spline constraints.
17467      256,1,1,3 nm. keypairs
17468      f[keycoords] ">
17469        begin(ind = 0);
17470        for (nx = x + 1, nx<w, ++nx,
17471          const cosmin = cos("$spline_anglemax"*pi/180);
17472          Ic = I[x];
17473          In = I[nx];
17474          S = Ic[0,2];
17475          T = In[0,2];
17476          angS = Ic[3]*pi/180;
17477          angT = In[3]*pi/180;
17478          NS = [ cos(angS),sin(angS) ];
17479          NT = [ cos(angT),sin(angT) ];
17480          ST = T - S;
17481          dist = norm(ST);
17482          cosN = dot(NS,-NT);
17483          angN = acos(cosN)*180/pi;
17484          quality = max(0,1 - dist/"$spline_distmax") * max(0,dot(NS,ST) - dot(NT,ST))/dist * max(0,cosN - cosmin);
17485          if (quality>0,
17486            if (ind>=w(#"$keypairs"), resize(#"$keypairs",2*ind,1,1,3,0));
17487            I[#"$keypairs",ind++] = [ quality,x,nx ];
17488          );
17489        );
17490        end(resize(#"$keypairs",ind,1,1,3,0));
17491        I"
17492      if {keypairs,w} sort[keypairs] -,x
17493      else rm[keypairs]
17494      fi
17495
17496      # Find and connect splines.
17497      [strokes] nm. new_strokes
17498      ind_strokes={$allow_self_intersections?$strokes:$new_strokes}
17499
17500      if narg($keypairs)
17501        f[keypairs] ">"$_edgel_lib"
17502          for_spline(code) = for (t = 0, t<=1, t+=dt,
17503            t3 = t*(t2 = t*t);
17504            P = round(mul([t3,t2,t,1],C,2));
17505            code#;
17506            dP = abs(mul([3*t2,2*t,1,0],C,2)) + 1e-8;
17507            dt = min(dtmin,0.75/max(dP));
17508          );
17509
17510          Ic = I;
17511          indS = Ic[1];
17512          indT = Ic[2];
17513          if (i(#"$keycoords",indS,0,0,2)<"$endpoint_connectivity" &&
17514              i(#"$keycoords",indT,0,0,2)<"$endpoint_connectivity",
17515            S = I(#"$keycoords",indS)[0,2];
17516            T = I(#"$keycoords",indT)[0,2];
17517            angS = i(#"$keycoords",indS,0,0,3)*pi/180;
17518            angT = i(#"$keycoords",indT,0,0,3)*pi/180;
17519            ST = T - S;
17520            dist = "$spline_roundness"*norm(ST);
17521            NS = [ cos(angS),sin(angS) ];
17522            NT = [ cos(angT),sin(angT) ];
17523            tmax = max(abs(ST));
17524            is_cond = 1;
17525            C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ S,T,dist*NS,-dist*NT ],2);
17526            dt = dtmin = 1/max(abs(T - S));
17527            current_val = 1;
17528            nb_switches = 0;
17529            for_spline(
17530              if (i(#"$ind_strokes",P,0,0)!=current_val, ++nb_switches; current_val=!current_val);
17531              if (nb_switches>2, is_cond = 0; break())
17532            );
17533            if (is_cond,
17534
17535              # Check that the new spline did not create small closed regions.
17536              const area_max_threshold = "$area_min";
17537              const area_min_threshold = 5;
17538              const max_edgels = 2*(area_max_threshold + 1);
17539
17540              if (area_max_threshold<=0,
17541                for_spline(i(#"$new_strokes",P)=1);
17542              , #else
17543                for_spline(i(#"$new_strokes",P)|=2);
17544                for_spline(
17545                  if (i(#"$new_strokes",P[0] + 1,P[1])==0,
17546                    edgels = area = 0; Q0 = Q = [ P[0] + 1,P[1],2 ];
17547                    do (
17548                      i(#"$new_strokes",Q[0],Q[1]) = 4;
17549                      area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
17550                      Q = next2(#"$new_strokes",Q),
17551                    Q!=Q0 && ++edgels<=max_edgels);
17552                    if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
17553                      is_cond = 0; break()
17554                    );
17555                  );
17556                  if (i(#"$new_strokes",P[0],P[1] + 1)==0,
17557                    edgels = area = 0; Q0 = Q = [ P[0],P[1] + 1,3 ];
17558                    do (
17559                      i(#"$new_strokes",Q[0],Q[1]) = 4;
17560                      area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
17561                      Q = next2(#"$new_strokes",Q),
17562                    Q!=Q0 && ++edgels<=max_edgels);
17563                    if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
17564                      is_cond = 0; break()
17565                    );
17566                  );
17567                  if (i(#"$new_strokes",P[0] - 1,P[1])==0,
17568                    edgels = area = 0; Q0 = Q = [ P[0] - 1,P[1],0 ];
17569                    do (
17570                      i(#"$new_strokes",Q[0],Q[1]) = 4;
17571                      area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
17572                      Q = next2(#"$new_strokes",Q),
17573                    Q!=Q0 && ++edgels<=max_edgels);
17574                    if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
17575                      is_cond = 0; break()
17576                    );
17577                  );
17578                  if (i(#"$new_strokes",P[0],P[1] - 1)==0,
17579                    edgels = area = 0; Q0 = Q = [ P[0],P[1] - 1,1 ];
17580                    do (
17581                      i(#"$new_strokes",Q[0],Q[1]) = 4;
17582                      area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
17583                      Q = next2(#"$new_strokes",Q),
17584                    Q!=Q0 && ++edgels<=max_edgels);
17585                    if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
17586                      is_cond = 0; break()
17587                    );
17588                  );
17589                0);
17590
17591                # Set or unset spline, and clean temp points used for local area estimations.
17592                for_spline(
17593                  if (i(#"$new_strokes",P[0] + 1,P[1])==4,
17594                    edgels = 0; Q0 = Q = [ P[0] + 1,P[1],2 ];
17595                    do (i(#"$new_strokes",Q[0],Q[1]) = 0; Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
17596                  );
17597                  if (i(#"$new_strokes",P[0],P[1] + 1)==4,
17598                    edgels = 0; Q0 = Q = [ P[0],P[1] + 1,3 ];
17599                    do (i(#"$new_strokes",Q[0],Q[1]) = 0; Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
17600                  );
17601                  if (i(#"$new_strokes",P[0] - 1,P[1])==4,
17602                    edgels = 0; Q0 = Q = [ P[0] - 1,P[1],0 ];
17603                    do (i(#"$new_strokes",Q[0],Q[1]) = 0; Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
17604                  );
17605                  if (i(#"$new_strokes",P[0],P[1] - 1)==4,
17606                    edgels = 0; Q0 = Q = [ P[0],P[1] - 1,1 ];
17607                    do (i(#"$new_strokes",Q[0],Q[1]) = 0; Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
17608                  );
17609                0);
17610                for_spline(i(#"$new_strokes",P) = is_cond?1:(i(#"$new_strokes",P)&1));
17611                if (is_cond,
17612                  ++i(#"$keycoords",indS,0,0,2);
17613                  ++i(#"$keycoords",indT,0,0,2);
17614                );
17615              );
17616            );
17617          ); I"
17618        rm[keypairs]
17619      fi
17620
17621      # Find and connect segments.
17622      ==[new_strokes] 0 # Negate values to consider boundary pixels (0) as strokes.
17623
17624      f[keycoords] ">"$_edgel_lib"
17625        for_segment(code) = for (t = 0, t<=tmax, ++t,
17626          P = round(S + t*ST);
17627          code#;
17628        );
17629
17630        if (i(#"$keycoords",x,0,0,2)<"$endpoint_connectivity",
17631          S = I(#"$keycoords",x)[0,2];
17632          angS = i(#"$keycoords",x,0,0,3)*pi/180;
17633          NS = [ cos(angS),sin(angS) ];
17634          ST = round(NS*"$segment_distmax");
17635          tmax = max(abs(ST));
17636          ST/=tmax;
17637          is_cond = 0;
17638          current_val = 0;
17639          nb_switches = 0;
17640          for_segment(
17641            if (i(#"$new_strokes",P,0,0)!=current_val, ++nb_switches; current_val=1 - current_val);
17642            if (nb_switches==2,is_cond = 1; break(););
17643          );
17644          tmax = t;
17645          if (is_cond,
17646
17647            # Check that the new segment did not create small closed regions.
17648            const area_max_threshold = "$area_min";
17649            const area_min_threshold = 5;
17650            const max_edgels = 2*(area_max_threshold + 1);
17651            if (area_max_threshold<=0,
17652              for_segment(i(#"$new_strokes",P)=0);
17653            , #else
17654              for_segment(i(#"$new_strokes",P)|=4);
17655              for_segment(
17656                if (i(#"$new_strokes",P[0] + 1,P[1])==1,
17657                  edgels = area = 0; Q0 = Q = [ P[0] + 1,P[1],2 ];
17658                  do (
17659                    i(#"$new_strokes",Q[0],Q[1]) = 2;
17660                    area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
17661                    Q = next3(#"$new_strokes",Q),
17662                  Q!=Q0 && ++edgels<=max_edgels);
17663                  if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
17664                    is_cond = 0; break()
17665                  );
17666                );
17667                if (i(#"$new_strokes",P[0],P[1] + 1)==1,
17668                  edgels = area = 0; Q0 = Q = [ P[0],P[1] + 1,3 ];
17669                  do (
17670                    i(#"$new_strokes",Q[0],Q[1]) = 2;
17671                    area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
17672                    Q = next3(#"$new_strokes",Q),
17673                  Q!=Q0 && ++edgels<=max_edgels);
17674                  if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
17675                    is_cond = 0; break()
17676                  );
17677                );
17678                if (i(#"$new_strokes",P[0] - 1,P[1])==1,
17679                  edgels = area = 0; Q0 = Q = [ P[0] - 1,P[1],0 ];
17680                  do (
17681                    i(#"$new_strokes",Q[0],Q[1]) = 2;
17682                    area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
17683                    Q = next3(#"$new_strokes",Q),
17684                  Q!=Q0 && ++edgels<=max_edgels);
17685                  if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
17686                    is_cond = 0; break()
17687                  );
17688                );
17689                if (i(#"$new_strokes",P[0],P[1] - 1)==1,
17690                  edgels = area = 0; Q0 = Q = [ P[0],P[1] - 1,1 ];
17691                  do (
17692                    i(#"$new_strokes",Q[0],Q[1]) = 2;
17693                    area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
17694                    Q = next3(#"$new_strokes",Q),
17695                  Q!=Q0 && ++edgels<=max_edgels);
17696                  if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
17697                    is_cond = 0; break()
17698                  );
17699                );
17700              0);
17701
17702              # Set or unset segment, and clean temp points used for local area estimations.
17703              for_segment(
17704                if (i(#"$new_strokes",P[0] + 1,P[1])==2,
17705                  edgels = 0; Q0 = Q = [ P[0] + 1,P[1],2 ];
17706                  do (i(#"$new_strokes",Q[0],Q[1]) = 1; Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
17707                );
17708                if (i(#"$new_strokes",P[0],P[1] + 1)==2,
17709                  edgels = 0; Q0 = Q = [ P[0],P[1] + 1,3 ];
17710                  do (i(#"$new_strokes",Q[0],Q[1]) = 1; Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
17711                );
17712                if (i(#"$new_strokes",P[0] - 1,P[1])==2,
17713                  edgels = 0; Q0 = Q = [ P[0] - 1,P[1],0 ];
17714                  do (i(#"$new_strokes",Q[0],Q[1]) = 1; Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
17715                );
17716                if (i(#"$new_strokes",P[0],P[1] - 1)==2,
17717                  edgels = 0; Q0 = Q = [ P[0],P[1] - 1,1 ];
17718                  do (i(#"$new_strokes",Q[0],Q[1]) = 1; Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
17719                );
17720              0);
17721              for_segment(i(#"$new_strokes",P) = is_cond?0:i(#"$new_strokes",P)&3);
17722              if (is_cond, ++i(#"$keycoords",x,0,0,2));
17723            );
17724          );
17725        ); I"
17726      ==[new_strokes] 0
17727      if !0$_keep_keycoords rm[keycoords] fi # Small hack used by the plug-in preview.
17728    fi
17729    rm[strokes]
17730    nm[new_strokes] $nm
17731  endl done
17732
17733#@cli ellipse : x[%],y[%],R[%],r[%],_angle,_opacity,_pattern,_color1,... : (+)
17734#@cli : Draw specified colored ellipse on selected images.
17735#@cli : A radius of '100%' stands for 'sqrt(width^2+height^2)'.
17736#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
17737#@cli : even if a color is specified. If a pattern is specified, the ellipse is
17738#@cli : drawn outlined instead of filled.
17739#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
17740#@cli : $ image.jpg repeat 300 ellipse {u(100)}%,{u(100)}%,{u(30)},{u(30)},{u(180)},0.3,${-rgb} done \
17741# ellipse 50%,50%,100,100,0,0.7,255
17742
17743#@cli flood : x[%],_y[%],_z[%],_tolerance>=0,_is_high_connectivity={ 0 | 1 },_opacity,_color1,... : (+)
17744#@cli : Flood-fill selected images using specified value and tolerance.
17745#@cli : Default values: 'y=z=0', 'tolerance=0', 'is_high_connectivity=0', 'opacity=1' and 'color1=0'.
17746#@cli : $ image.jpg repeat 1000 flood {u(100)}%,{u(100)}%,0,20,0,1,${-rgb} done
17747
17748#@cli gaussian : _sigma1[%],_sigma2[%],_angle
17749#@cli : Draw a centered gaussian on selected images, with specified standard deviations and orientation.
17750#@cli : Default values: 'sigma1=3', 'sigma2=sigma1' and 'angle=0'.
17751#@cli : $ 400,400 gaussian 100,30,45
17752#@cli : $$ https://gmic.eu/oldtutorial/_gaussian
17753gaussian : skip ${1=3},${2=$1},${3=0}
17754  e[^-1] "Draw centered gaussian on image$? with standard deviations ($1,$2) and angle $3 deg."
17755  u={cos($3*pi/180)}
17756  v={sin($3*pi/180)}
17757  dmax={max(w,h)}
17758  if isnum($1) l1=$1 else l1={${1}10000*$dmax/100} fi
17759  if isnum($2) l2=$2 else l2={${2}10000*$dmax/100} fi
17760  l1={1/(2*max(1/3,$l1)^2)}
17761  l2={1/(2*max(1/3,$l2)^2)}
17762  A={$l1*$u*$u+$l2*$v*$v}
17763  B={($l1-$l2)*$u*$v}
17764  C={$l1*$v*$v+$l2*$u*$u}
17765  repeat $! l[$>] nm={0,n}
17766    w={w} h={h} ds={d},{s} rm
17767    $w,$h,1,1,'X=x-{($w-1)/2};Y=y-{($h-1)/2};$A*X*X+2*$B*X*Y+$C*Y*Y'
17768    * -1 exp r $w,$h,$ds
17769  nm $nm endl done
17770
17771#@cli graph : [function_image],_plot_type,_vertex_type,_ymin,_ymax,_opacity,_pattern,_color1,... : \
17772# 'formula',_resolution>=0,_plot_type,_vertex_type,_xmin,xmax,_ymin,_ymax,_opacity,_pattern,_color1,... : (+)
17773#@cli : Draw specified function graph on selected images.
17774#@cli : 'plot_type' can be { 0=none | 1=lines | 2=splines | 3=bar }.
17775#@cli : 'vertex_type' can be { 0=none | 1=points | 2,3=crosses | 4,5=circles | 6,7=squares }.
17776#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
17777#@cli : even if a color is specified.
17778#@cli : Default values: 'plot_type=1', 'vertex_type=1', 'ymin=ymax=0 (auto)', 'opacity=1', 'pattern=(undefined)'
17779#@cli : and 'color1=0'.
17780#@cli : $ image.jpg +rows 50% blur[-1] 3 split[-1] c div[0] 1.5 graph[0] [1],2,0,0,0,1,255,0,0 \
17781# graph[0] [2],2,0,0,0,1,0,255,0 graph[0] [3],2,0,0,0,1,0,0,255 keep[0]
17782
17783#@cli grid : size_x[%]>=0,size_y[%]>=0,_offset_x[%],_offset_y[%],_opacity,_pattern,_color1,...
17784#@cli : Draw xy-grid on selected images.
17785#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
17786#@cli : even if a color is specified.
17787#@cli : Default values: 'offset_x=offset_y=0', 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
17788#@cli : $ image.jpg grid 10%,10%,0,0,0.5,255
17789#@cli : $ 400,400,1,3,255 grid 10%,10%,0,0,0.3,0xCCCCCCCC,128,32,16
17790grid : check "$1>=0 && $2>=0" skip ${3=0},${4=0},${5=1},${6=0},${7=$6}
17791  if ${"is_pattern \"$6\""}
17792    e[0--3] "Draw xy-grid on image$?, with sizes ($1,$2), offsets ($3,$4), opacity $5, pattern $6 and color (${7--1})."
17793    pattern=$6 color=${7--1}
17794  else
17795    e[0--3] "Draw xy-grid on image$?, with sizes ($1,$2), offsets ($3,$4), opacity $5, and color (${6--1})."
17796    pattern=0xFFFFFFFF color=${6--1}
17797  fi
17798  eval "
17799    is_percent(str) = (unref(_is_pct); _is_pct=['#str']; _is_pct[size(_is_pct) - 1]==_'%');
17800    repeat (l,k,
17801
17802      # Horizontal lines.
17803      size = is_percent($1)?max(1,w#k*$1):$1;
17804      size>=1?(
17805        off = (is_percent($3)?size*$3:$3)%size;
17806        for (x = off, x<w#k, x+=size, polygon(#k,-2,x,0,x,h - 1,$5,"$pattern","$color"));
17807      );
17808
17809      # Vertical lines.
17810      size = is_percent($2)?max(1,h#k*$2):$2;
17811      size>=1?(
17812        off = (is_percent($4)?size*$4:$4)%size;
17813        for (y = off, y<h#k, y+=size, polygon(#k,-2,0,y,w - 1,y,$5,"$pattern","$color"));
17814      )
17815    )"
17816
17817#@cli j : eq. to 'image'. : (+)
17818
17819#@cli image : [sprite],_x[%|~],_y[%|~],_z[%|~],_c[%|~],_opacity,_[opacity_mask],_max_opacity_mask : (+)
17820#@cli : Draw specified sprite image on selected images.
17821#@cli : (eq. to 'j').\n
17822#@cli : If one of the x,y,z or c argument ends with a '~', its value is expected to be
17823#@cli : a centering ratio (in [0,1]) rather than a position.
17824#@cli : Usual centering ratio are { 0=left-justified | 0.5=centered | 1=right-justified }.
17825#@cli : Default values: 'x=y=z=c=0', 'opacity=1', 'opacity_mask=(undefined)' and 'max_opacity_mask=1'.
17826#@cli : $ image.jpg +crop 40%,40%,60%,60% resize[-1] 200%,200%,1,3,5 frame[-1] 2,2,0 image[0] [-1],30%,30% keep[0]
17827
17828#@cli line : x0[%],y0[%],x1[%],y1[%],_opacity,_pattern,_color1,... : (+)
17829#@cli : Draw specified colored line on selected images.
17830#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
17831#@cli : even if a color is specified.
17832#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
17833#@cli : $ image.jpg repeat 500 line 50%,50%,{u(w)},{u(h)},0.5,${-rgb} done line 0,0,100%,100%,1,0xCCCCCCCC,255 \
17834# line 100%,0,0,100%,1,0xCCCCCCCC,255
17835
17836#@cli linethick : x0[%],y0[%],x1[%],y1[%],_thickness,_opacity,_color1
17837#@cli : Draw specified colored thick line on selected images.
17838#@cli : Default values: 'thickness=2', 'opacity=1' and 'color1=0'.
17839#@cli : $ 400,400,1,3 repeat 100 linethick {u([w,h,w,h,5])},0.5,${-rgb} done
17840linethick : check "${5=2}>=0 && isnum(${6=1}) && isnum(${7=0})"
17841  e[^-1] "Draw thick line ($1,$2) - ($3,$4) on image$?, with thickness $5, opacity $6 and color (${7--1})."
17842  if !$5 line ${1-4},${6--1}
17843  else repeat $! l[$>]
17844    x0={${"is_percent $1"}?(w-1)*$1:$1}
17845    y0={${"is_percent $2"}?(h-1)*$2:$2}
17846    x1={${"is_percent $3"}?(w-1)*$3:$3}
17847    y1={${"is_percent $4"}?(h-1)*$4:$4}
17848    coords={"
17849      const th = "$5";
17850      P0 = [ "$x0","$y0" ];
17851      P1 = [ "$x1","$y1" ];
17852      dP = P1 - P0;
17853      n = [ -dP[1],dP[0] ]/max(1e-8,norm(dP))*th/2;
17854      round([ P0 - n, P0 + n, P1 + n, P1 - n ]);
17855    "}
17856    polygon 4,$coords,${6--1}
17857  endl done fi
17858
17859#@cli mandelbrot : z0r,z0i,z1r,z1i,_iteration_max>=0,_is_julia={ 0 | 1 },_c0r,_c0i,_opacity : (+)
17860#@cli : Draw mandelbrot/julia fractal on selected images.
17861#@cli : Default values: 'iteration_max=100', 'is_julia=0', 'c0r=c0i=0' and 'opacity=1'.
17862#@cli : $ 400,400 mandelbrot -2.5,-2,2,2,1024 map 0 +blur 2 elevation3d[-1] -0.2
17863
17864#@cli marble : _image_weight,_pattern_weight,_angle,_amplitude,_sharpness>=0,_anisotropy>=0,_alpha,_sigma,\
17865# _cut_low>=0,_cut_high>=0
17866#@cli : Render marble like pattern on selected images.
17867#@cli : Default values: 'image_weight=0.2', 'pattern_weight=0.1', 'angle=45', 'amplitude=0', 'sharpness=0.4' \
17868# and 'anisotropy=0.8',
17869#@cli : 'alpha=0.6', 'sigma=1.1' and 'cut_low=cut_high=0'.
17870#@cli : $ image.jpg +marble ,
17871marble : skip ${1=0.2},${2=0.1},${3=45},${4=0},${5=0.4},${6=0.8},${7=0.6},${8=1.1},${9=0%},${10=100%}
17872  e[^-1] "Render marble like pattern on image$?, with image weight $1, pattern weight $2, angle $3 deg.,
17873          amplitude $4, sharpness $5, anisotropy $6, alpha $7, sigma $8, and cut ($9,$10)."
17874  sx={$2*sin($3*pi/180)} sy={$2*cos($3*pi/180)} f sin(x*$sx+y*$sy+i*$1)
17875  if $4 smooth $4,$5,$6,$7,$8 fi
17876  c $9,$10 n 0,255
17877
17878#@cli maze : _width>0,_height>0,_cell_size>0
17879#@cli : Input maze with specified size.
17880#@cli : $ maze 30,20 negate normalize 0,255
17881maze : check "isint(${1=15}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=24}) && $3>0"
17882  e[^-1] "Input $1x$2 maze."
17883  ({round(u($1-1))},{round(u($2-1))})  # Starting cell.
17884  $1,$2,1,1,15 +f. 0 a[-2,-1] c   # Starting maze data.
17885  _generate_maze $1,$2
17886  _render_maze. $3 nm. [maze]
17887
17888_generate_maze :
17889
17890  # Start opening walls.
17891  do
17892    x={-2,@-2} y={-2,@-1} # Coords of the current cell.
17893    =. 1,$x,$y,0,1       # Mark current cell as visited.
17894
17895    # Check for neighboring cells that are candidate for opening wall, and select one random.
17896    is_candidate=0
17897    up=-1 if i($x,$y)&8" && "$y>0" && "!i($x,$y-1,0,1) up=$x,{$y-1},8 is_candidate=1 fi          # Up.
17898    down=-1 if i($x,$y)&4" && "$y<$2-1" && "!i($x,$y+1,0,1) down=$x,{$y+1},4 is_candidate=1 fi   # Down.
17899    left=-1 if i($x,$y)&2" && "$x>0" && "!i($x-1,$y,0,1) left={$x-1},$y,2 is_candidate=1 fi      # Left.
17900    right=-1 if i($x,$y)&1" && "$x<$1-1" && "!i($x+1,$y,0,1) right={$x+1},$y,1 is_candidate=1 fi # Right.
17901    if $is_candidate
17902      ($up,$down,$left,$right) y. discard. -1 r. 3,{h/3},1,1,-1 shift. 0,{round(u(4))},0,0,2 rows. 0,0 mv. -2
17903    fi
17904
17905    # Remove wall between the current and chosen neighboring cells.
17906    if $is_candidate
17907      if {-2,@-1}==8   =. {i($x,$y)&7},$x,$y =. {i($x,$y-1)&11},$x,{$y-1}   # Remove up wall.
17908      elif {-2,@-1}==4 =. {i($x,$y)&11},$x,$y =. {i($x,$y+1)&7},$x,{$y+1}   # Remove down wall.
17909      elif {-2,@-1}==2 =. {i($x,$y)&13},$x,$y =. {i($x-1,$y)&14},{$x-1},$y  # Remove left wall.
17910      else             =. {i($x,$y)&14},$x,$y =. {i($x+1,$y)&13},{$x+1},$y  # Remove right wall.
17911      fi
17912      z.. 0,1 a[-3,-2] y  # Add neighboring cell to stack of cells to explore.
17913    else # No candidate : remove current cell from cells to explore.
17914      if h#-2==1 break fi
17915      rows.. 0,{{-2,h}-2}
17916    fi
17917  while 1
17918  rm.. channels. 0
17919
17920_render_maze :
17921  # Create the 16 configurations of walls.
17922  i[0] $1,$1 i[1] [0]x15
17923  line[8-15] 0,0,100%,0,1,1
17924  line[4-7,12-15] 0,100%,100%,100%,1,1
17925  line[2-3,6-7,10-11,14-15] 0,0,0,100%,1,1
17926  line[1-15:2] 100%,0,100%,100%,1,1
17927  # Map the wall data with them.
17928  a[0-15] x r. {w*$1},{h*$1} *. $1 channels. 0,1
17929  $1,$1,1,1,x $1,$1,1,1,y a[-2,-1] c r. ..,..,1,2,0,2 +[-2,-1]
17930  warp.. .,0,0,0 rm.
17931
17932#@cli maze_mask : _cellsize>0
17933#@cli : Input maze according to size and shape of selected mask images.
17934#@cli : Mask may contain disconnected shapes.
17935#@cli : $ 0 text "G'MIC",0,0,53,1,1 dilate 3 autocrop 0 frame 1,1,0 maze_mask 8 dilate 3 negate mul 255
17936maze_mask : check "isint(${1=24}) && $1>0"
17937  e[^-1] "Input masked maze from image$? with cell size $1."
17938  compose_channels + >= 50% repeat $! l[$>]
17939    do
17940      +rand[0] 0,1 *. [0] ({[xM,yM]}) rm..  # Select one starting point in the mask.
17941      +flood[0] {^},0,0,0,1,2 >=. 2 +negate. *.. 15 a[-2,-1] c
17942      flood[0] {-2,^},0,0,0,1,0
17943      _generate_maze {w},{h}
17944    while iM#0
17945    rm[0] + _render_maze. $1 nm. [maze]
17946  endl done
17947
17948#@cli newton_fractal : z0r,z0i,z1r,z1i,_angle,0<=_descent_method<=2,_iteration_max>=0,_convergence_precision>0,\
17949# _expr_p(z),_expr_dp(z),_expr_d2p(z)
17950#@cli : Draw newton fractal on selected images, for complex numbers in range (z0r,z0i) - (z1r,z1i).
17951#@cli : Resulting images have 3 channels whose meaning is [ last_zr, last_zi, nb_iter_used_for_convergence ].
17952#@cli : 'descent_method' can be { 0=secant | 1=newton | 2=householder }.
17953#@cli : Default values: 'angle=0', 'descent_method=1', 'iteration_max=200', 'convergence_precision=0.01', \
17954# 'expr_p(z)=z^^3-1', 'expr_dp(z)=3*z^^2' and 'expr_d2z(z)=6*z'.
17955#@cli : $ 400,400 newton_fractal -1.5,-1.5,1.5,1.5,0,2,200,0.01,"z^^6 + z^^3 - 1","6*z^^5 + 3*z^^2","30*z^^4 + 6*z" \
17956# f "[ atan2(i1,i0)*90+20,1,cut(i2/30,0.2,0.7) ]" hsl2rgb
17957newton_fractal : check "isin(${6=1},0,1,2) && ${7=200}>=0 && ${8=0.01}>0"
17958                 skip "${4=0},${9=z^^3-1},${10=3*z^^2},${11=6*z}"
17959  m0,m1,m2=secant,newton,householder
17960  e[^-1] "Draw newton fractal on image$?, for complex range ($1,$2)-($3,$4), with angle $5, $7 max "${m$6}" "\
17961   "iterations, precision $8, and expressions 'p(z)=$9', 'dp(z)=$10' and 'd2p(z)=$11'."
17962  channels 0,2
17963  f "*
17964    begin(
17965      const dx = abs($3 - $1);
17966      const dy = abs($4 - $2);
17967      const angle = $5;
17968      const method = $6;
17969      const itermax = $7;
17970      const precision = $8;
17971
17972      zc = [ $1 + $3, $2 + $4 ]/2;
17973      R = rot(-angle°);
17974    );
17975
17976    p(z) = ($9);
17977    dp(z) = ($10);
17978    d2p(z) = ($11);
17979
17980    zn = [ $1 + x*dx/(w-1), $2 + y*dy/(h-1) ];
17981    angle?(zn = (R*(zn-=zc)+=zc));
17982
17983    !method?(znm1 = zn + [ precision,0 ]);
17984    repeat (itermax,iter,
17985      pzn = p(zn);
17986      method==0?(
17987        znp1 = zn - pzn**(zn - znm1)//(pzn - p(znm1)); # secant
17988        znm1 = zn;
17989      ):method==1?(
17990        dpzn = dp(zn);
17991        znp1 = zn - pzn//dpzn; # newton
17992      ):( # householder
17993        dpzn = dp(zn);
17994        d2pzn = d2p(zn);
17995        hn = (pzn**d2pzn)//(2*dpzn^^2);
17996        znp1 = zn - pzn//dpzn**([1,0] + hn);
17997      );
17998      norm(znp1 - zn)<precision?break();
17999      zn = znp1;
18000    );
18001    cabs(zn)<2?[ zn,iter ]:[ 0,0,iter ]"
18002
18003#@cli j3d : eq. to 'object3d'. : (+)
18004
18005#@cli object3d : [object3d],_x[%],_y[%],_z,_opacity,_rendering_mode,_is_double_sided={ 0 | 1 },\
18006# _is_zbuffer={ 0 | 1 },_focale,_light_x,_light_y,_light_z,_specular_lightness,_specular_shininess : (+)
18007#@cli : Draw specified 3D object on selected images.
18008#@cli : (eq. to 'j3d').\n
18009#@cli : 'rendering_mode' can be { 0=dots | 1=wireframe | 2=flat | 3=flat-shaded | 4=gouraud-shaded | 5=phong-shaded }.
18010#@cli : Default values: 'x=y=z=0', 'opacity=1' and 'is_zbuffer=1'. All other arguments take their default values
18011#@cli : from the 3D environment variables.
18012#@cli : $ image.jpg torus3d 100,10 cone3d 30,-120 add3d[-2,-1] rotate3d. 1,1,0,60 object3d[0] [-1],50%,50% keep[0]
18013
18014#@cli pack_sprites : _nb_scales>=0,0<=_min_scale<=100,_allow_rotation={ 0=0 deg. | 1=180 deg. | 2=90 deg. | 3=any },\
18015# _spacing,_precision>=0,max_iterations>=0
18016#@cli : Try to randomly pack as many sprites as possible onto the 'empty' areas of an image.
18017#@cli : Sprites can be eventually rotated and scaled during the packing process.
18018#@cli : First selected image is the canvas that will be filled with the sprites.
18019#@cli : Its last channel must be a binary mask whose zero values represent potential locations for drawing the sprites.
18020#@cli : All other selected images represent the sprites considered for packing.
18021#@cli : Their last channel must be a binary mask that represents the sprite shape (i.e. a 8-connected component).
18022#@cli : The order of sprite packing follows the order of specified sprites in the image list.
18023#@cli : Sprite packing is done on random locations and iteratively with decreasing scales.
18024#@cli : 'nb_scales' sets the number of decreasing scales considered for all specified sprites to be packed.
18025#@cli : 'min_scale' (in %) sets the minimal size considered for packing (specified as a percentage of the
18026#@cli : original sprite size).
18027#@cli : 'spacing' can be positive or negative.
18028#@cli : 'precision' tells about the desired number of failed trials before ending the filling process.
18029#@cli : Default values: 'nb_scales=5', 'min_scale=25', 'allow_rotation=3', 'spacing=1', 'precision=7' \
18030# and 'max_iterations=256'.
18031#@cli : $ 512,512,1,3,"min(255,y*c/2)" 100%,100% circle 50%,50%,100,1,255 append c image.jpg resize2dy[-1] 24 \
18032# to_rgba pack_sprites 3,25
18033pack_sprites : check "isint(${1=5}) && $1>=0 && ${2=25}>=0 && $2<=100 && isint(${3=3}) && $3>=0 && $3<=3 &&
18034                      isint(${4=1}) && isint(${5=7}) && $5>=0 && isint(${6=256}) && $6>=0"
18035  e[^-1] "Randomly pack image$? with $1 scales, minimum scale $2%, "${arg\ 1+$3,no,180\"\ \"deg.,90\"\ \"deg.,any}\
18036         " rotation, spacing $4, precision $5 and $6 maximum iterations."
18037  N={$!-1} is_first_time=1
18038  repeat $! r[$>] 100%,100%,1,{$>,max(2,s)} done   # Ensure all images have a binary shape mask.
18039
18040  # Start iterations over scales.
18041  repeat $1
18042    rprogress {$>*100/$1}
18043    nb_attempts=0
18044
18045    # Generate all sprites for current scale.
18046    ratio={if($1>1,$2+(100-$2)*$</($1-1),100)}%
18047    repeat $N +l[{1+$>}]
18048      w={w*$ratio} h={h*$ratio}
18049      if $w<1||$h<1 rm
18050      else r $w,$h,1,100%,2 sh. 100% !=. 0 area{1+$>}={is} rm.
18051      fi
18052    endl done
18053
18054    # Pack rescaled sprites together.
18055    l[0,{$N+1}--1] repeat $6
18056
18057      # Compute reference sprite.
18058      ind={1+($>%$N)} area=${area$ind}
18059      if $3==0 [$ind]
18060      elif $3==1 +rotate[$ind] {round(u)*180}
18061      elif $3==2 +rotate[$ind] {round(u(3))*90}
18062      else +rotate[$ind] {u*360} sh. 100% !=. 0 area={is} rm.
18063      fi
18064
18065      # Get binary map of possible locations.
18066      +channels[0] 100% ==. 0
18067      if $4>1 erode. {2*$4-1}
18068      elif $4<1 dilate. {-2*$4+3}
18069      fi
18070
18071      # Generate random skeleton-oriented point cloud.
18072      +rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0
18073      if $is_first_time noise. 0.1,2 ==. 1 fi
18074      distance. 0 noise. 1,1
18075      max_patch. {$ind,round(1.5*max(w,h))}
18076      *. .. pointcloud3d.
18077
18078      # Subdivide point cloud if multiple sprites.
18079      if $N>1 l.
18080        s3d /[1] $N round[1] max[1] 1 n={1,@0}
18081        r[2] 3,{{2,h}/3},1,1,-1
18082        i[2] 1,{2,h} rand[2] 0,1 a[2,3] x sort[2] +,y z[2] 1,3 r[2] 3,$n,1,1 y[2]
18083        r[3] 1,{2*$n},1,1,0 r[4] 1,{3*$n},1,1,0 r[5] 1,$n,1,1,0 a y
18084      endl fi
18085
18086      # Create 3D cloud of sprites.
18087      n={@7}
18088      if $n
18089        s3d. rm[-2,-1]
18090        if $3==0 # No rotation allowed.
18091          [-6] i.. (-128;{w};{h};{s})
18092          if $n>1 4,{$n-1},1,1,-128,0,0,0 fi
18093          +channels.. 100% i.. (-128;{w};{h};{s})
18094          if $n>1 ... fi
18095        elif $3==1 # 180 deg. rotation allowed.
18096          +rotate[-6] {round(u(1))*180} i.. (-128;{w};{h};{s})
18097          if $n>1 +rotate. 180 i.. (-128;{w};{h};{s}) fi
18098          if $n>2 4,{$n-2},1,1,-128,0,0,0 1,100% rand. 0,1 round. 1 j.. .,1 rm. fi
18099          +channels[-4] 100% i.. (-128;{w};{h};{s})
18100          if $n>1 +channels[-4] 100% i.. (-128;{w};{h};{s}) fi
18101          if $n>2 [-5] fi
18102        else # 90 deg. rotation (or more) allowed.
18103          +rotate[-6] {round(u(3))*90} i.. (-128;{w};{h};{s})
18104          if $n>1 +rotate. 90 i.. (-128;{w};{h};{s}) fi
18105          if $n>2 +rotate. 90 i.. (-128;{w};{h};{s}) fi
18106          if $n>3 +rotate. 90 i.. (-128;{w};{h};{s}) fi
18107          if $n>4 4,{$n-4},1,1,-128,0,0,0 1,100% rand. 0,3 round. 1 j.. .,1 rm. fi
18108          +channels[-8] 100% i.. (-128;{w};{h};{s})
18109          if $n>1 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi
18110          if $n>2 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi
18111          if $n>3 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi
18112          if $n>4 [-9] fi
18113        fi
18114        y[{$N+3}--1] a[{$N+3}--1] y
18115      fi
18116      rm... # Delete reference sprite.
18117
18118      # Draw cloud and detect non-intersecting sprites.
18119      [0] sh. 100% f. 1 -. [-4]
18120      j3d.. ...,0,0,0,1,2,0,0 rm[-3,-1]
18121      sh. 100% area_fg. 0,1 ==. $area
18122      *. ... rm... sh.. 0,{-2,s-2} *. .. rm.
18123
18124      # Draw selected sprites on rendering image.
18125      if iM j[0] ..,0,0,0,0,1,. rm[-2,-1]
18126      else
18127        rm[-2,-1]
18128        nb_attempts+=1
18129        if $nb_attempts>$5 break else continue fi
18130      fi
18131
18132    done k[0] endl
18133
18134  done k[0]
18135
18136#@cli piechart : label_height>=0,label_R,label_G,label_B,"label1",value1,R1,G1,B1,...,"labelN",valueN,RN,GN,BN
18137#@cli : Draw pie chart on selected (RGB) images.
18138#@cli : $ image.jpg piechart 25,0,0,0,"Red",55,255,0,0,"Green",40,0,255,0,"Blue",30,128,128,255,"Other",5,128,128,128
18139piechart : check $1>=0
18140  e[^-1] "Draw pie chart on image$?, with label height $1 and color ($2,$3,$4)."
18141  $=arg repeat $! l[$>]
18142    ellipse 50%,50%,{w/2-1},{h/2-1},0,1,1
18143    ellipse 50%,50%,{w/2-1},{h/2-1},0,1,0xFFFFFFFF
18144    (${6--1:5}) normalize_sum.
18145    theta=0
18146    if w>1 repeat w
18147      xe={0.5*{-2,w}*(1+cos($theta))}
18148      ye={0.5*{-2,h}*(1+sin($theta))}
18149      line.. 50%,50%,$xe,$ye
18150      theta-={2*pi*i($>)}
18151    done fi
18152    theta=0
18153    repeat w if i($>)
18154      ntheta={$theta-2*pi*i($>)}
18155      xc={0.5*{-2,w}*(1+0.5*cos(0.5*($ntheta+$theta)))}
18156      yc={0.5*{-2,h}*(1+0.5*sin(0.5*($ntheta+$theta)))}
18157      xf={0.5*{-2,w}*(1+0.8*cos(0.5*($ntheta+$theta)))}
18158      yf={0.5*{-2,h}*(1+0.8*sin(0.5*($ntheta+$theta)))}
18159      flood.. $xf,$yf,0,0,0,1,${arg{7+5*$>}},${arg{8+5*$>}},${arg{9+5*$>}}
18160      if abs($ntheta-$theta)>0.1
18161        0 t. ${arg{5+5*$>}},0,0,$1,1,1
18162        ($2^$3^$4) r. ..,..,1,3 *. ..
18163        j[-4] .,{$xc-w/2},{$yc-h/2},0,0,1,..
18164        rm[-2,-1]
18165      fi
18166      theta=$ntheta
18167    fi done
18168    rm.
18169  endl done
18170
18171#@cli plasma : _alpha,_beta,_scale>=0 : (+)
18172#@cli : Draw a random colored plasma fractal on selected images.
18173#@cli : This command implements the so-called 'Diamond-Square' algorithm.
18174#@cli : Default values: 'alpha=1', 'beta=1' and 'scale=8'.
18175#@cli : $ 400,400,1,3 plasma
18176#@cli : $$ https://gmic.eu/oldtutorial/_plasma
18177
18178#@cli point : x[%],y[%],_z[%],_opacity,_color1,... : (+)
18179#@cli : Set specified colored pixel on selected images.
18180#@cli : Default values: 'z=0', 'opacity=1' and 'color1=0'.
18181#@cli : $ image.jpg repeat 10000 point {u(100)}%,{u(100)}%,0,1,${-rgb} done
18182
18183#@cli polka_dots : diameter>=0,_density,_offset1,_offset2,_angle,_aliasing,_shading,_opacity,_color,...
18184#@cli : Draw dots pattern on selected images.
18185#@cli : Default values: 'density=20', 'offset1=offset2=50', 'angle=0', 'aliasing=10', 'shading=1', 'opacity=1' \
18186# and 'color=255'.
18187#@cli : $ image.jpg polka_dots 10,15,0,0,20,10,1,0.5,0,128,255
18188polka_dots : check $1>=0 skip ${2=20},${3=50},${4=50},${5=0},${6=10},${7=1},${8=1},${9=255}
18189  e[^-1] "Draw polka dots on image$?, with diameter $1, density $2, angle $3 deg., shift ($4,$5), aliasing $6 and
18190          shading $7."
18191  theta={$5*pi/180} ct={cos($theta)} st={sin($theta)} mid1={$1/2} mid2={$2/2}
18192  i[0] (${9--1}) y[0] c
18193  repeat $!-1
18194    WH={max(w,h)}
18195    100%,100%,100%,1,"xn = 100*x/"$WH"-$3; yn = 100*y/"$WH"-$4; \
18196                      xr = xn*"$ct"-yn*"$st"; yr = xn*"$st"+yn*"$ct"; \
18197                      xc = xr%$2-"$mid2"; yc = yr%$2-"$mid2"; \
18198                      "$mid1"-sqrt(xc*xc+yc*yc)"
18199    *. $6 c. 0,$7 n. 0,$8 (${9--1}) y. c r. ..,..,..
18200    j... .,0,0,0,0,1,.. rm[-2,-1]
18201  mv. 1 done rm[0]
18202
18203#@cli polygon : N>=1,x1[%],y1[%],...,xN[%],yN[%],_opacity,_pattern,_color1,... : (+)
18204#@cli : Draw specified colored N-vertices polygon on selected images.
18205#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
18206#@cli : even if a color is specified. If a pattern is specified, the polygon is
18207#@cli : drawn outlined instead of filled.
18208#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
18209#@cli : $ image.jpg polygon 4,20%,20%,80%,30%,80%,70%,20%,80%,0.3,0,255,0 \
18210# polygon 4,20%,20%,80%,30%,80%,70%,20%,80%,1,0xCCCCCCCC,255
18211#@cli : $ image.jpg 2,16,1,1,'u(if(x,{h},{w}))' polygon[-2] {h},{^},0.6,255,0,255 remove[-1]
18212
18213#@cli quiver : [function_image],_sampling[%]>0,_factor>=0,_is_arrow={ 0 | 1 },_opacity,_color1,...
18214#@cli : Draw specified 2D vector/orientation field on selected images.
18215#@cli : Default values: 'sampling=5%', 'factor=1', 'is_arrow=1', 'opacity=1', 'pattern=(undefined)'
18216#@cli : and 'color1=0'.
18217#@cli : $ 100,100,1,2,'if(c==0,x-w/2,y-h/2)' 500,500,1,3,255 quiver[-1] [-2],10
18218#@cli : $ image.jpg +resize2dy 600 luminance[0] gradient[0] mul[1] -1 reverse[0,1] append[0,1] c \
18219# blur[0] 8 orientation[0] quiver[1] [0],20,1,1,0.8,255
18220quiver : check ${"is_image_arg $1"}" && ${2=5%}>0 && ${3=1}>=0 && isbool(${4=1})" skip "${5=1},${6=0}"
18221  e[^-1] "Draw 2D vector field $1 on image$?, with sampling $2, factor $3, arrows "${"arg 1+$4,disabled,enabled"}",
18222          opacity $5 and color (${6--1})."
18223  pass$1 repeat $!-1 l[$>,-1]
18224    eval ${-math_lib}"
18225      s_sampling = ['$2'];
18226      sampling = s_sampling[size(s_sampling) - 1 ]==_'%'?min(w#0,h#0)*$2:$2;
18227      vmax = max(abs(im),abs(iM));
18228      vmax = vmax?vmax:1;
18229      fact = $3*sampling/vmax;
18230      for (y = sampling/2, y<h#0, y+=sampling,
18231        for (x = sampling/2, x<w#0, x+=sampling,
18232          X = round(x*w/w#0); Y = round(y*h/h#0);
18233          u = i(X,Y,0,0)*fact; v = i(X,Y,0,1)*fact;
18234          if ($4,
18235            arrow(#0,[x,y],[x + u,y + v],45,sampling/4,$5,[${6--1}]),
18236            polygon(#0,2,[x - 0.5*u,y - 0.5*v],[x + 0.5*u,y + 0.5*v],$5,[${6--1}]);
18237          );
18238        );
18239      );
18240    "
18241  endl done rm.
18242
18243#@cli rectangle : x0[%],y0[%],x1[%],y1[%],_opacity,_pattern,_color1,...
18244#@cli : Draw specified colored rectangle on selected images.
18245#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
18246#@cli : even if a color is specified. If a pattern is specified, the rectangle is
18247#@cli : drawn outlined instead of filled.
18248#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
18249#@cli : $ image.jpg repeat 30 rectangle {u(100)}%,{u(100)}%,{u(100)}%,{u(100)}%,0.3,${-rgb} done
18250rectangle : skip ${5=1},${6=0},${7=$6}
18251  if ${"is_pattern \"$5\""}
18252    e[0--3] "Draw outlined rectangle from ($1,$2) to ($3,$4) on image$?, with opacity $5 and color (${7--1})."
18253  else
18254    e[0--3] "Draw filled rectangle from ($1,$2) to ($3,$4) on image$?, with opacity $5 and color (${6--1})."
18255  fi
18256  polygon 4,$1,$2,$3,$2,$3,$4,$1,$4,${5--1}
18257
18258#@cli rorschach : 'smoothness[%]>=0','mirroring={ 0=none | 1=x | 2=y | 3=xy }
18259#@cli : Render rorschach-like inkblots on selected images.
18260#@cli : Default values: 'smoothness=5%' and 'mirroring=1'.
18261#@cli : $ 400,400 rorschach 3%
18262rorschach : check "${1=5%}>=0 && isint(${2=1}) && $2>=0 && $2<=3"
18263  e[^-1] "Render rorschach-like inkblots on image$?, with smoothness $1 and "${arg\ 1+$2,no,x,y,xy}"-mirroring."
18264  if $2==0 # No mirroring.
18265   rand -1,1 b $1 >= 0
18266  elif $2==1 # X-mirroring.
18267    repeat $! l[$>]
18268      w={w}
18269      columns 0,{w/2-1} rand -1,1 b $1 >= 0
18270      +mirror x if $w%2 columns. 1,100% fi a x
18271    endl done
18272  elif $2==2 # Y-mirroring.
18273    repeat $! l[$>]
18274      h={h}
18275      rows 0,{h/2-1} rand -1,1 b $1 >= 0
18276      +mirror y if $h%2 rows. 1,100% fi a y
18277    endl done
18278  elif $2==3 # XY-mirroring.
18279    repeat $! l[$>]
18280      w={w} h={h}
18281      z 0,0,{w/2-1},{h/2-1} rand -1,1 b $1 >= 0
18282      +mirror x if $w%2 columns. 1,100% fi a x
18283      +mirror y if $h%2 rows. 1,100% fi a y
18284    endl done
18285  fi
18286
18287#@cli sierpinski : recursion_level>=0
18288#@cli : Draw Sierpinski triangle on selected images.
18289#@cli : Default value: 'recursion_level=7'.
18290#@cli : $ image.jpg sierpinski 7
18291sierpinski : check ${1=7}>=0 skip ${2=50},${3=0},${4=0},${5=100},${6=100},${7=100}
18292  e[^-1] "Draw Sierpinski triangle of degree $1 on image$?."
18293  _sierpinski ${2-7},$1
18294
18295_sierpinski :
18296  if $7<=0 polygon 3,$1%,$2%,$3%,$4%,$5%,$6%,1,255 return fi
18297  _sierpinski $1,$2,{($1+$3)/2},{($2+$4)/2},{($1+$5)/2},{($2+$6)/2},{$7-1}
18298  _sierpinski {($1+$3)/2},{($2+$4)/2},$3,$4,{($3+$5)/2},{($4+$6)/2},{$7-1}
18299  _sierpinski {($1+$5)/2},{($2+$6)/2},$5,$6,{($3+$5)/2},{($4+$6)/2},{$7-1}
18300
18301#@cli spiralbw : width>0,_height>0,_is_2dcoords={ 0 | 1 }
18302#@cli : Input a 2D rectangular spiral image with specified size.
18303#@cli : Default values: 'height=width' and 'is_2dcoords=0'.
18304#@cli : $ spiralbw 16
18305#@cli : $ image.jpg spiralbw {[w,h]},1 +warp[0] [1],0 +warp[2] [1],2
18306spiralbw : check "$1>=1 && ${2=$1}>=1 && isbool(${3=0})"
18307  e[^-1] "Input 2D rectangular spiral image of size $1x$2."
18308  main="alpha = min(x,y,w - 1 - x,h - 1 - y);
18309        t0 = alpha*2*(w + h) - 4*alpha^2;
18310        X = x - alpha;
18311        Y = y - alpha;
18312        W = w - 2*alpha;
18313        H = h - 2*alpha;
18314        t = t0 + (Y==0?X:
18315                  X==W - 1?W - 1 + Y:
18316                  Y==H - 1?2*W + H - 3 - X:
18317                  2*(W + H - 2) - Y);"
18318  if $3 $1,$2,1,2,$main"[ t%w, int(t/w) ]" else $1,$2,1,1,$main fi
18319
18320#@cli spline : x0[%],y0[%],u0[%],v0[%],x1[%],y1[%],u1[%],v1[%],_opacity,_color1,...
18321#@cli : Draw specified colored spline curve on selected images (cubic hermite spline).
18322#@cli : Default values: 'opacity=1' and 'color1=0'.
18323#@cli : $ image.jpg repeat 30 spline {u(100)}%,{u(100)}%,{u(-600,600)},{u(-600,600)},{u(100)}%,{u(100)}%,\
18324# {u(-600,600)},{u(-600,600)},0.6,255 done
18325spline : skip ${9=1},${10=0}
18326  e[^-1] "Draw spline from ($1,$2) [$3,$4] to ($5,$6) [$7,$8] on image$?, with opacity $9 and color (${10--1})."
18327  repeat $! l[$>]
18328    x0={if(${"is_percent $1"},$1*(w-1),$1)}
18329    y0={if(${"is_percent $2"},$2*(h-1),$2)}
18330    u0={if(${"is_percent $3"},$3*(w-1),$3)}
18331    v0={if(${"is_percent $4"},$4*(h-1),$4)}
18332    x1={if(${"is_percent $5"},$5*(w-1),$5)}
18333    y1={if(${"is_percent $6"},$6*(h-1),$6)}
18334    u1={if(${"is_percent $7"},$7*(w-1),$7)}
18335    v1={if(${"is_percent $8"},$8*(h-1),$8)}
18336    eval ${-math_lib}"spline(#0,["$x0","$y0"],["$u0","$v0"],["$x1","$y1"],["$u1","$v1"],$9,[${10--1}])"
18337  endl done
18338
18339#@cli tetraedron_shade : x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3,R0,G0,B0,...,R1,G1,B1,...,R2,G2,B2,...,R3,G3,B3,...
18340#@cli : Draw tetraedron with interpolated colors on selected (volumetric) images.
18341tetraedron_shade :
18342  e[^-1] "Draw tetraderon ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)-($10,$11,$12) with interpolated colors in image$?."
18343
18344  # Find bounding box.
18345  xm={round(min($1,$4,$7,$10),1,-1)} xM={round(max($1,$4,$7,$10),1,1)}
18346  ym={round(min($2,$5,$8,$11),1,-1)} yM={round(max($2,$5,$8,$11),1,1)}
18347  zm={round(min($3,$6,$9,$12),1,-1)} zM={round(max($3,$6,$9,$12),1,1)}
18348
18349  # Find color mapping coefficients for each vertex.
18350  l[] (${1-3},1;${4-6},1;${7-9},1;${10-12},1) (${13--1}) r. {w/4},4,1,1,-1 s. x solve[^0] [0] rm[0] a c endl
18351
18352  # Draw tetraedron on selected images.
18353  f[^-1] "*
18354  begin(
18355    x0 = $1; y0 = $2; z0 = $3;
18356    x1 = $4; y1 = $5; z1 = $6;
18357    x2 = $7; y2 = $8; z2 = $9;
18358    x3 = $10; y3 = $11; z3 = $12;
18359    u01 = x1 - x0; v01 = y1 - y0; w01 = z1 - z0;
18360    u02 = x2 - x0; v02 = y2 - y0; w02 = z2 - z0;
18361    u03 = x3 - x0; v03 = y3 - y0; w03 = z3 - z0;
18362    u12 = x2 - x1; v12 = y2 - y1; w12 = z2 - z1;
18363    u13 = x3 - x1; v13 = y3 - y1; w13 = z3 - z1;
18364    u23 = x3 - x2; v23 = y3 - y2; w23 = z3 - z2;
18365    nx012 = v01*w02 - w01*v02; ny012 = w01*u02 - u01*w02; nz012 = u01*v02 - v01*u02;
18366    if (nx012*u03 + ny012*v03 + nz012*w03<0, nx012*=-1; ny012*=-1; nz012*=-1);
18367    nx013 = v01*w03 - w01*v03; ny013 = w01*u03 - u01*w03; nz013 = u01*v03 - v01*u03;
18368    if (nx013*u02 + ny013*v02 + nz013*w02<0, nx013*=-1; ny013*=-1; nz013*=-1);
18369    nx023 = v02*w03 - w02*v03; ny023 = w02*u03 - u02*w03; nz023 = u02*v03 - v02*u03;
18370    if (nx023*u01 + ny023*v01 + nz023*w01<0, nx023*=-1; ny023*=-1; nz023*=-1);
18371    nx123 = v12*w13 - w12*v13; ny123 = w12*u13 - u12*w13; nz123 = u12*v13 - v12*u13;
18372    if (-nx123*u01 - ny123*v01 - nz123*w01<0, nx123*=-1; ny123*=-1; nz123*=-1);
18373  );
18374  if (x<"$xm" || x>"$xM" || y<"$ym" || y>"$yM" || z<"$zm" || z>"$zM",i,
18375    dx0 = x - x0; dy0 = y - y0; dz0 = z - z0;
18376    dx1 = x - x1; dy1 = y - y1; dz1 = z - z1;
18377    is_in = dx0*nx012 + dy0*ny012 + dz0*nz012>=0 &&
18378    dx0*nx013 + dy0*ny013 + dz0*nz013>=0 &&
18379    dx0*nx023 + dy0*ny023 + dz0*nz023>=0 &&
18380    dx1*nx123 + dy1*ny123 + dz1*nz123>=0;
18381    is_in? i(#-1,0,0,0)*x + i(#-1,0,1,0)*y + i(#-1,0,2,0)*z + i(#-1,0,3,0) :i
18382  )
18383"
18384  rm.
18385
18386#@cli t : eq. to 'text'. : (+)
18387
18388#@cli text : text,_x[%|~],_y[%|~],_font_height[%]>=0,_opacity,_color1,... : (+)
18389#@cli : Draw specified colored text string on selected images.
18390#@cli : (eq. to 't').\n
18391#@cli : If one of the x or y argument ends with a '~', its value is expected to be
18392#@cli : a centering ratio (in [0,1]) rather than a position.
18393#@cli : Usual centering ratio are { 0=left-justified | 0.5=centered | 1=right-justified }.
18394#@cli : Sizes '13' and '128' are special and correspond to binary fonts (no-antialiasing).
18395#@cli : Any other font size is rendered with anti-aliasing.
18396#@cli : Specifying an empty target image resizes it to new dimensions such that the image contains
18397#@cli : the entire text string.
18398#@cli : Default values: 'x=y=0.01~', 'font_height=16', 'opacity=1' and 'color1=0'.
18399#@cli : $ image.jpg resize2dy 600 y=0 repeat 30 text {2*$>}" : This is a nice text, isn't it ?",10,$y,{2*$>},0.9,255 \
18400# y+={2*$>} done
18401#@cli : $ 0 text "G'MIC",0,0,23,1,255
18402
18403#@cli to : eq. to 'text_outline'.
18404to : skip "${1=}",${2=0.01~},${3=0.01~} check "${4=7.5%}>0 && ${5=2}>=0 && isnum(${6=1}) && isnum(${7=255}) && "\
18405                                      "isnum(${8=$7}) && isnum(${9=$7}) && isnum(${10=255})"
18406  _text_outline $"*"
18407
18408#@cli text_outline : text,_x[%|~],_y[%|~],_font_height[%]>0,_outline>=0,_opacity,_color1,...
18409#@cli : Draw specified colored and outlined text string on selected images.
18410#@cli : If one of the x or y argument ends with a '~', its value is expected to be
18411#@cli : a centering ratio (in [0,1]) rather than a position.
18412#@cli : Usual centering ratio are { 0=left-justified | 0.5=centered | 1=right-justified }.
18413#@cli : Default values: 'x=y=0.01~', 'font_height=7.5%', 'outline=2', 'opacity=1', 'color1=color2=color3=255' \
18414# and 'color4=255'.
18415#@cli : $ image.jpg text_outline "Hi there!",10,10,63,3
18416text_outline : skip "${1=}",${2=0.01~},${3=0.01~}
18417  check "${4=7.5%}>0 && ${5=2}>=0 && isnum(${6=1}) && isnum(${7=255}) && "\
18418        "isnum(${8=$7}) && isnum(${9=$7}) && isnum(${10=255})"
18419  _text_outline $"*"
18420
18421_text_outline : skip "${1=}"
18422  e[0--3] "Draw outlined text '$1' at position ($2,$3) on image$?, with font height $4, outline $5, opacity $6 and
18423           color ${7--1}."
18424  if ['"$1"']==0 return fi
18425  sepx,sepy={"sx=['$2']; sy=['$3']; [sx[size(sx)-1], sy[size(sy)-1]]"}
18426  is_fontpercent=${"is_percent $4"}
18427  xpos={`s=['"$2"'];$sepx==_'~'||$sepx==_'%'?s[0,size(s)-1]:s`}
18428  ypos={`s=['"$3"'];$sepy==_'~'||$sepy==_'%'?s[0,size(s)-1]:s`}
18429  repeat $! l[$>]
18430    0 t. "$1",0,0,{-2,$is_fontpercent?h*$4:$4},1,1 expand_xy. {1+$5},0
18431    +dilate. {2*$5+1}
18432    i[-3] (${7--1}) r... {s#0},1,1,1,0,2 y... c r... .,.,1,100%
18433    if $5 *[-3,-2] else rm.. fi
18434    if w#0
18435      j... ..,{[($sepx==_'~'?(w#0-1-w):$sepx==_'%'?(w#0-1)%:1)*$xpos,\
18436                ($sepy==_'~'?(h#0-1-h):$sepy==_'%'?(h#0-1)%:1)*$ypos]},0,0,$6,.
18437      k[0]
18438    else k[1]
18439    fi
18440  endl done
18441
18442#@cli triangle_shade : x0,y0,x1,y1,x2,y2,R0,G0,B0,...,R1,G1,B1,...,R2,G2,B2,...
18443#@cli : Draw triangle with interpolated colors on selected images.
18444#@cli : $ image.jpg triangle_shade 20,20,400,100,120,200,255,0,0,0,255,0,0,0,255
18445triangle_shade :
18446  e[^-1] "Draw triangle ($1,$2)-($3,$4)-($5,$6) with interpolated colors on image$?."
18447  # Find color mapping coefficients for each vertex.
18448  l[] ($1,$2,1;$3,$4,1;$5,$6,1) (${7--1}) r. {w/3},3,1,1,-1 s. x solve[^0] [0] rm[0] a c endl
18449
18450  # Pre-compute coefs to test point inside triangle.
18451  invarea={(-$4*$5+$2*(-$3+$5)+$1*($2-$6)+$3*$6)^-1}
18452  s1={$2*$5-$1*$6} s2={$6-$2} s3={$1-$5}
18453  t1={$1*$4-$2*$3} t2={$2-$4} t3={$3-$1}
18454
18455  # Begin drawing on each selected image.
18456  repeat $!-1 l[$>,-1] repeat s#0
18457    a={i(0,0,0,$>)} b={i(0,1,0,$>)} c={i(0,2,0,$>)}
18458    sh[0] $>
18459    f. "s = "$invarea"*("$s1" + "$s2"*x + "$s3"*y);
18460        t = "$invarea"*("$t1" + "$t2"*x + "$t3"*y);
18461        s>=0 && t>=0 && t+s<=1 ? "$a"*x+"$b"*y+"$c":i"
18462    rm.
18463  done endl done
18464  rm.
18465
18466#@cli truchet : _scale>0,_radius>=0,_pattern_type={ 0=straight | 1=curved }
18467#@cli : Fill selected images with random truchet patterns.
18468#@cli : Default values: 'scale=32', 'radius=5' and 'pattern_type=1'.
18469#@cli : $ 400,300 truchet ,
18470truchet : check "isint(${1=32}) && $1>0 && ${2=3}>=0" skip ${3=1}
18471  e[^-1] "Render "${arg\ 1+!$3,curved,straight}" truchet patterns in image$?, with scale $1 and radius $2."
18472  repeat $! l[$>] nm={0,n}
18473    w={w} h={h} s={s} rm
18474    $1,$1 = 1,0,0 = 1,100%,100% distance 1,{1+$3} M={int(iM/2)} # Generate truchet pattern and its mirrored version.
18475    ir {$M-$2/2-($1%2)},{$M+$2/2} +mirror y a x
18476    {round($w/$1,1,1)},{round($h/$1,1,1)} rand. 0,1 >=. 50% r. {w*$1},{h*$1} *. $1
18477    channels. 0,1 (0,{$1-1}) r. $1,$1,1,1,3 +transpose. a[-2,-1] c ri. ..,0,2 +[-2,-1]
18478    warp.. . rm. >= 50% r $w,$h,1,1,0 r 100%,100%,1,$s
18479  nm $nm endl done
18480
18481#@cli turbulence : _radius>0,_octaves={1,2,3...,12},_alpha>0,_difference={-10,10},_mode={0,1,2,3}
18482#@cli : Render fractal noise or turbulence on selected images.
18483#@cli : Default values: 'radius=32', 'octaves=6', 'alpha=3', 'difference=0' and 'mode=0'.
18484#@cli : $ 400,400,1,3 turbulence 16
18485#@cli : $$ https://gmic.eu/oldtutorial/_turbulence
18486turbulence : check "${1=32}>0 && ${2=6}>0" skip ${3=3},${4=0},${5=0}
18487  e[^-1] "Render fractal noise or turbulence on image$?, with radius $1, octaves $2, damping per octave $3,
18488          difference $4 and mode $5."
18489  repeat $! l[$>] nm={0,n}
18490    if $4 . fi
18491    f. 0 +noise. 10,0 b. $1,0
18492    if $5==0||$5==1 -. {ia} abs.
18493    elif $5==3||$5==4 ^. 2
18494    elif $5==5 ^. 3
18495    fi
18496    repeat $2-1
18497      +noise.. 10,0 b. {$1/2^$>},0
18498      if $5==0 -. {ia} abs.
18499      elif $5==4 ^. 2
18500      elif $5==5 ^. 3
18501      fi
18502      *.. $3 +[-2--1]
18503    done
18504    n. 0,255
18505    rm..
18506    if $4 *. $4 mv.. 2 - n. 0,255 fi
18507  nm $nm endl done
18508
18509#@cli yinyang
18510#@cli : Draw a yin-yang symbol on selected images.
18511#@cli : $ 400,400 yinyang
18512yinyang :
18513  e[^-1] "Draw yin-yang symbol on image$?."
18514  f 0 repeat $! l[$>]
18515    s={s} channels 0
18516    r={round(0.95*min(w,h)/4)}
18517    +line 50%,0,50%,50%,1,2 ellipse. 50%,{h/2-$r},$r,$r,0,1,2
18518    line. 50%,50%,50%,100%,1,1 ellipse. 50%,{h/2+$r},$r,$r,0,1,1
18519    flood. {w/2-$r},50%,0,0,0,1,2
18520    flood. {w/2+$r},50%,0,0,0,1,1
18521    ellipse.. 50%,50%,{2*$r},{2*$r},0,1,1
18522    *
18523    ellipse. 50%,{h/2-$r},{$r/3},{$r/3},0,1,1
18524    ellipse. 50%,{h/2+$r},{$r/3},{$r/3},0,1,2
18525    r 100%,100%,1,$s
18526  endl done
18527
18528#---------------------------------
18529#
18530#@cli :: Matrix Computation
18531#
18532#---------------------------------
18533
18534#@cli dijkstra : starting_node>=0,ending_node>=0 : (+)
18535#@cli : Compute minimal distances and paths from specified adjacency matrices by the Dijkstra algorithm.
18536
18537#@cli eigen : (+)
18538#@cli : Compute the eigenvalues and eigenvectors of selected symmetric matrices or matrix fields.
18539#@cli : If one selected image has 3 or 6 channels, it is regarded as a field of 2x2 or 3x3 symmetric matrices,
18540#@cli : whose eigen elements are computed at each point of the field.
18541#@cli : $ (1,0,0;0,2,0;0,0,3) +eigen
18542#@cli : $ image.jpg structuretensors blur 2 eigen split[0] c
18543#@cli : $$ https://gmic.eu/oldtutorial/_eigen
18544
18545#@cli invert : solver={ 0=SVD | 1=LU } : (+)
18546#@cli : Compute the inverse of the selected matrices.
18547#@cli : SVD solver is slower but less numerically instable than LU.
18548#@cli : Default value: 'solver=1'.
18549#@cli : $ (0,1,0;0,0,1;1,0,0) +invert
18550
18551#@cli orthogonalize : _mode = { 0=orthogonalize | 1=orthonormalize }
18552#@cli : Orthogonalize or orthonormalize selected matrices, using Modified Gram-Schmidt process.
18553#@cli : Default value: 'mode=0'.
18554orthogonalize :
18555  if isbool($1) mode=$1 else mode=0 noarg fi
18556  u0,u1,v0,v1=Orthogonalize,Orthonormalize,x,ce
18557  e[^-1] ${u$mode}" matri"${v{$!!=1}}"$?, using Modified Gram-Schmidt process."
18558  repeat $! l[$>]
18559    eval ">
18560      proj(u,v) = (dot(u,v)/dot(u,u)*u);
18561      for (p = 1, p<w, ++p,
18562        ref(crop(p,0,1,h),v);
18563        repeat (p,q,
18564          ref(crop(q,0,1,h),u);
18565          v-=proj(u,v);
18566        );
18567        draw(v,p,0,0,0,1,h);
18568      )"
18569    if $mode
18570      eval. "*!y?(
18571        ref(crop(x,0,1,h),v);
18572        n = norm(v);
18573        v/=n?norm(v):1;
18574        draw(v,x,0,0,0,1,h);
18575      )"
18576    fi
18577  endl done
18578
18579#@cli meigen : m>=1
18580#@cli : Compute an approximation of the 'm' largest eigenvalues and eigenvectors of selected symmetric matrices,
18581#@cli : using the Arnoldi iteration method (https://en.wikipedia.org/wiki/Arnoldi_iteration).
18582#@cli : A larger 'm' goes with better numerical precision.
18583#@cli : $ (1,0,0;0,2,0;0,0,3) +meigen 3
18584meigen : check "isint($1) && $1>0"
18585  if $!!=1 s="ce" else s="x" fi
18586  e[^-1] "Compute $1 largest eigen-values of matri"$s"$?."
18587  repeat $! l[$>] nm={n}
18588    if w!=h" || "d!=1" || "s!=1 v 1 error[0--5] "Command 'meigen': Image '"$nm"' is not a square matrix." fi
18589    eval ${-math_lib}" store(meig(crop(),$1,h),'val',1,min($1,h))" $val k. nm $nm
18590  endl done
18591
18592#@cli mproj : [dictionary],_method,_max_iter={ 0=auto | >0 },_max_residual>=0 : (+)
18593#@cli : Find best matching projection of selected matrices onto the span of an over-complete
18594#@cli : dictionary D, using the orthogonal projection or Matching Pursuit algorithm.
18595#@cli : Selected images are 2D-matrices in which each column represent a signal to project.
18596#@cli : '[dictionary]' is a matrix in which each column is an element of the dictionary D.
18597#@cli : 'method' tells what projection algorithm must be applied. It can be:
18598#@cli : \   - 0 = orthogonal projection (least-squares solution using LU-based solver).
18599#@cli : \   - 1 = matching pursuit.
18600#@cli : \   - 2 = matching pursuit, with a single orthogonal projection step at the end.
18601#@cli : \   - >=3 = orthogonal matching pursuit where an orthogonal projection step is performed
18602#@cli : \           every 'method-2' iterations.
18603#@cli : 'max_iter' sets the max number of iterations processed for each signal.
18604#@cli : If set to '0' (default), 'max_iter' is equal to the number of columns in D.
18605#@cli : (only meaningful for matching pursuit and its variants).
18606#@cli : 'max_residual' gives a stopping criterion on signal reconstruction accuracy.
18607#@cli : (only meaningful for matching pursuit and its variants).
18608#@cli : For each selected image, the result is returned as a matrix W
18609#@cli : whose columns correspond to the weights associated to each column of D,
18610#@cli : such that the matrix product D*W is an approximation of the input matrix.
18611#@cli : Default values: 'method=0', 'max_iter=0' and 'max_residual=1e-6'.
18612
18613#@cli solve : [image] : (+)
18614#@cli : Solve linear system AX = B for selected B-matrices and specified A-matrix.
18615#@cli : If the system is under- or over-determined, the least squares solution is returned
18616#@cli : (using SVD-based solver).
18617#@cli : $ (0,1,0;1,0,0;0,0,1) (1;2;3) +solve[-1] [-2]
18618
18619#@cli svd : (+)
18620#@cli : Compute SVD decomposition of selected matrices.
18621#@cli : $ 10,10,1,1,'if(x==y,x+u(-0.2,0.2),0)' +svd
18622
18623#@cli transpose
18624#@cli : Transpose selected matrices.
18625#@cli : $ image.jpg +transpose
18626transpose :
18627  e[^-1] "Transpose image$?."
18628  permute yxzc
18629
18630#@cli trisolve : [image] : (+)
18631#@cli : Solve tridiagonal system AX = B for selected B-vectors and specified tridiagonal A-matrix.
18632#@cli : Tridiagonal matrix must be stored as a 3 column vector, where 2nd column contains the
18633#@cli : diagonal coefficients, while 1st and 3rd columns contain the left and right coefficients.
18634#@cli : $ (0,0,1;1,0,0;0,1,0) (1;2;3) +trisolve[-1] [-2]
18635
18636#---------------------------------
18637#
18638#@cli :: 3D Meshes
18639#
18640#---------------------------------
18641
18642#@cli +3d : eq. to 'add3d'. : (+)
18643
18644#@cli add3d : tx,_ty,_tz : [object3d] : (no arg) : (+)
18645#@cli : Shift selected 3D objects with specified displacement vector, or merge them with specified
18646#@cli : 3D object, or merge all selected 3D objects together.
18647#@cli : (eq. to '+3d').
18648#@cli : Default values: 'ty=tz=0'.
18649#@cli : $ sphere3d 10 repeat 5 +add3d[-1] 10,{u(-10,10)},0 color3d[-1] ${-rgb} done add3d
18650#@cli : $ repeat 20 torus3d 15,2 color3d[-1] ${-rgb} mul3d[-1] 0.5,1 if $>%2 rotate3d[-1] 0,1,0,90 fi add3d[-1] 70 \
18651# add3d rotate3d[-1] 0,0,1,18 done double3d 0
18652
18653#@cli animate3d : _width>0,_height>0,_angle_dx,_angle_dy,_angle_dz,_zoom_factor>=0,_filename
18654#@cli : Animate selected 3D objects in a window.
18655#@cli : If argument 'filename' is provided, each frame of the animation is saved as a numbered filename.
18656#@cli : Default values: 'width=640', 'height=480', 'angle_dx=0', 'angle_dy=1', 'angle_dz=0', 'zoom_factor=1' \
18657# and 'filename=(undefined)'.
18658animate3d : skip ${1=640},${2=480},${3=0},${4=1},${5=0},"${7=""}" check ${6=1}>=0
18659  e[^-1] "Animate 3D object$?, in a $1x$2 window with angle velocities ($3,$4,$5)."
18660  is_multi={$!>1} repeat $! +l[$>]
18661    n3d *3d {$6*min($1,$2)/1.5} c3d
18662    ax=0 ay=0 az=0 frame=0 vfact=1
18663    do
18664       +r3d 1,0,0,$ax r3d. 0,1,0,$ay r3d. 0,0,1,$az
18665       ax+=$3 ay+=$4 az+=$5
18666       $1,$2,1,3,-1 j3d. ..,50%,50%,0,1
18667       if narg("$7")
18668         to_rgba. replace_color. 0,0,-1,-1,-1,255,64,64,64,0
18669         if $is_multi filename=${filename\ "$7",$>,$frame} else filename=${filename\ "$7",$frame} fi
18670         o. $filename frame+=1
18671       else
18672         replace. -1,64
18673       fi
18674       w. {$vfact*w},{$vfact*h},0,0,-1,-1,{0,n} wait 20 k[0]
18675       if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,-D} vfact=1.5 fi # Increase window size.
18676       if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,-C} vfact={1/1.5} fi # Decrease window size.
18677       if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,-R} vfact=1 fi # Decrease window size.
18678    while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0
18679  endl done
18680
18681#@cli apply_camera3d : pos_x,pos_y,pos_z,target_x,target_y,target_z,up_x,up_y,up_z
18682#@cli : Apply 3D camera matrix to selected 3D objects.
18683#@cli : Default values: 'target_x=0', 'target_y=0', 'target_z=0', 'up_x=0', 'up_y=-1' and 'up_z=0'.
18684apply_camera3d : skip ${4=0},${5=0},${6=0},${7=0},${8=-1},${9=0}
18685  e[^-1] "Apply 3D camera matrix to 3D object$?, with camera position ($1,$2,$3), target position ($4,$5,$6) and
18686          up-vector ($7,$8,$9)."
18687  ({$4-$1}^{$5-$2}^{$6-$3})  # f.
18688  ($7^$8^$9)                 # up.
18689  orientation[-2,-1]         # f/|f| and up/|up|.
18690  _cross3d {-2,^},{^}        # s = f x up
18691  _cross3d {^},{-3,^}        # u = s x f
18692  rm... y[-3--1] x mv[-2,-1] -3
18693  a[-3--1] y z. 0,3  # Rotation matrix R.
18694  -3d[^-1] $1,$2,$3 pose3d[^-1] {^} rm. -3d 0,0,800
18695
18696_cross3d :
18697  ({$2*$6-$3*$5}^{$3*$4-$1*$6}^{$1*$5-$2*$4}) orientation. y.
18698
18699#@cli apply_matrix3d : a11,a12,a13,...,a31,a32,a33
18700#@cli : Apply specified 3D rotation matrix to selected 3D objects.
18701#@cli : $ torus3d 10,1 +apply_matrix3d {mul(rot(1,0,1,-15°),[1,0,0,0,2,0,0,0,8],3)} double3d 0
18702apply_matrix3d :
18703  e[^-1] "Apply 3x3 matrix (${1-3};${4-6};${7-9}) to 3D object$?."
18704  repeat $! l[$>]
18705    nbp={i[6]} sh 8,{8+3*$nbp-1},0,0 r. 3,$nbp,1,1,-1 3,3,1,1,$* transpose. m*[-2,-1] rm.
18706  endl done
18707
18708#@cli array3d : size_x>=1,_size_y>=1,_size_z>=1,_offset_x[%],_offset_y[%],_offset_y[%]
18709#@cli : Duplicate a 3D object along the X,Y and Z axes.
18710#@cli : Default values: 'size_y=1', 'size_z=1' and 'offset_x=offset_y=offset_z=100%'.
18711#@cli : $ torus3d 10,1 +array3d 5,5,5,110%,110%,300%
18712array3d : check "isint($1) && $1>0 && isint(${2=1}) && $2>0 && isint(${3=1}) && $3>0"
18713  skip ${4=100%},${5=100%},${6=100%}
18714  e[^-1] "Duplicate 3D object$? along X,Y,Z axes with factors ($1,$2,$3) and offsets ($4,$5,$6)."
18715  repeat $! l[$>]
18716
18717    # Retrieve object dimensions.
18718    +rows 8,{8+3*i[6]} r. 3,{h/3},1,1,-1 s. x,3
18719    dx={-3,if(${is_percent\ $4},$4*(iM-im),$4)}
18720    dy={-2,if(${is_percent\ $5},$5*(iM-im),$5)}
18721    dz={if(${is_percent\ $6},$6*(iM-im),$6)}
18722    rm[-3--1]
18723
18724    # Duplicate along X.
18725    off=0 repeat int(log2($1))
18726      ++3d. {2^$>*$dx} +3d. ..
18727      if !($1&(2^$>)) rm.. else +3d.. $off off+={2^$>*$dx} fi
18728    done +3d. $off +3d
18729
18730    # Duplicate along Y.
18731    off=0 repeat int(log2($2))
18732      ++3d. 0,{2^$>*$dy} +3d. ..
18733      if !($2&(2^$>)) rm.. else +3d.. 0,$off off+={2^$>*$dy} fi
18734    done +3d. 0,$off +3d
18735
18736    # Duplicate along Z.
18737    off=0 repeat int(log2($3))
18738      ++3d. 0,0,{2^$>*$dz} +3d. ..
18739      if !($3&(2^$>)) rm.. else +3d.. 0,0,$off off+={2^$>*$dz} fi
18740    done +3d. 0,0,$off +3d
18741  endl done
18742
18743#@cli arrow3d : x0,y0,z0,x1,y1,z1,_radius[%]>=0,_head_length[%]>=0,_head_radius[%]>=0
18744#@cli : Input 3D arrow with specified starting and ending 3D points.
18745#@cli : Default values: 'radius=5%', 'head_length=25%' and 'head_radius=15%'.
18746#@cli : $ repeat 10 a={$>*2*pi/10} arrow3d 0,0,0,{cos($a)},{sin($a)},-0.5 done +3d
18747arrow3d : check "${7=5%}>=0 && ${8=25%}>=0 && ${9=15%}>=0"
18748  e[^-1] "Input 3D arrow, from (${1-3}) to (${4-6}), with radius $7, head length $8 and head radius $9."
18749
18750  # Create 3D object.
18751  L={sqrt(($4-$1)^2+($5-$2)^2+($6-$3)^2)}
18752  R={if(${is_percent\ $7},$7*$L,$7)}
18753  l={if(${is_percent\ $8},$8*$L,$8)}
18754  r={if(${is_percent\ $9},$9*$L,$9)}
18755  L-=$l cylinder3d $R,$L cone3d $r,$l +3d. 0,0,$L +3d[-2,-1]
18756
18757  # Compute rotation matrix for arrow orientation.
18758  ({$4-$1}^{$5-$2}^{$6-$3}) (0.01^-0.02^0.03) orientation[-2,-1]
18759  _cross3d {-2,^},{^} _cross3d {^},{-3,^} rm... y[-3--1] x mv[-2,-1] -3
18760  a[-3--1] y
18761
18762  # Rotate and translate the arrow at specified coordinates.
18763  s3d.. r[-5] 3,{-5,h/3},1,1,-1 m*[-5,-1]
18764  y[-4] a[-6--1] y +3d. ${1-3} rv3d.
18765
18766#@cli axes3d : _size_x,_size_y,_size_z,_font_size>0,_label_x,_label_y,_label_z,_is_origin={ 0=no | 1=yes }
18767#@cli : Input 3D axes with specified sizes along the x,y and z orientations.
18768#@cli : Default values: 'size_x=size_y=size_z=1', 'font_size=23', 'label_x=X', 'label_y=Y', 'label_z=Z' and \
18769# 'is_origin=1'
18770#@cli : $ axes3d ,
18771axes3d : check "${4=23}>0 && isbool(${8=1})" skip ${1=1},${2=$1},${3=$2},"${5=X},${6=Y},${7=Z}"
18772  e[^-1] "Input 3D axes with sizes ($1,$2,$3)."
18773  l[]
18774  m={max(abs($1),abs($2),abs($3))/40} m2={2*$m} m3={1.2*$m2}
18775  if $1 line3d 0,0,0,$1,0,0 fi
18776  if $2 line3d 0,0,0,0,$2,0 fi
18777  if $3 line3d 0,0,0,0,0,$3 fi
18778  if $1
18779    cone3d $m,{2*$m},16 r3d. 0,1,0,90 +3d. {$1-$m2},0,0
18780    _axes3d "$5",$4 +3d. {$1+$m3},0,0
18781  fi
18782  if $2
18783    cone3d $m,{2*$m},16 r3d. 1,0,0,-90 +3d. 0,{$2-$m2},0
18784    _axes3d "$6",$4 +3d. 0,{$2+$m3},0
18785  fi
18786  if $3
18787    cone3d $m,{2*$m},16 +3d. 0,0,{$3-$m2}
18788    _axes3d "$7",$4 +3d. 0,0,{$3+$m3}
18789  fi
18790  if $8 _axes3d "O",$4 -3d. $m3,$m3,$m3 fi
18791  +3d nm [3d\ axes]
18792  endl
18793
18794_axes3d :
18795  0 t. "$1",2,0,$2,1,1 +dilate. 3 *.. 255 r.. 100%,100%,1,3
18796  i... (67.5;73.5;109.5;103.5;51.5;100.5;1;1;0;0;0;1;0;-128;{w};{h};3)
18797  i.. (-128;{w};{h};1) y[-3,-1] a[-4--1] y
18798
18799#@cli boundingbox3d
18800#@cli : Replace selected 3D objects by their 3D bounding boxes.
18801#@cli : $ torus3d 100,30 +boundingbox3d +3d[-1] [-2]
18802boundingbox3d :
18803  e[^-1] "Replace 3D object$? by their 3D bounding boxes."
18804  repeat $! l[$>]
18805    nbv={f2ui(i[6])} rows 8,{8+3*$nbv} r. 3,$nbv,1,1,-1 s. x
18806    xc,yc,zc,sx,sy,sz={"[ (im#0 + iM#0)/2, (im#1 + iM#1)/2, (im#2 + iM#2)/2, iM#0 - im#0,iM#1 - im#1, iM#2 - im#2 ]"}
18807    rm box3d $sx,$sy,$sz c3d +3d $xc,$yc,$zc p3d. 1
18808  endl done
18809
18810#@cli box3d : _size_x,_size_y,_size_z
18811#@cli : Input 3D box at (0,0,0), with specified geometry.
18812#@cli : Default values: 'size_x=1' and 'size_z=size_y=size_x'.
18813#@cli : $ box3d 100,40,30 +primitives3d 1 color3d[-2] ${-rgb}
18814box3d : skip ${1=1},${2=$1},${3=$2}
18815  e[^-1] "Input 3D box, with size ($1,$2,$3)."
18816  1,86,1,1,\
18817  67.5,73.5,109.5,103.5,51.5,100.5,8,6,\
18818  0,0,0,$1,0,0,$1,$2,0,0,$2,0,\
18819  0,0,$3,$1,0,$3,$1,$2,$3,0,$2,$3,\
18820  4,0,3,2,1,4,4,5,6,7,4,0,1,5,4,4,3,7,6,2,4,0,4,7,3,4,1,2,6,5,\
18821  200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,\
18822  1,1,1,1,1,1
18823  nm. [3D\ box]
18824
18825#@cli c3d : eq. to 'center3d'.
18826c3d :
18827  _center3d
18828
18829#@cli center3d
18830#@cli : Center selected 3D objects at (0,0,0).
18831#@cli : (eq. to 'c3d').
18832#@cli : $ repeat 100 circle3d {u(100)},{u(100)},{u(100)},2 done add3d color3d[-1] 255,0,0 +center3d \
18833# color3d[-1] 0,255,0 add3d
18834center3d :
18835  _$0
18836
18837_center3d :
18838  e[0--3] "Center 3D object$?."
18839  check3d 0 repeat $! l[$>]
18840    if i[6]
18841      s3d r[2] 3,{2,h/3},1,1,-1 s[2] x
18842      -[2] {2,(iM+im)/2} -[3] {3,(iM+im)/2} -[4] {4,(iM+im)/2}
18843      a[2-4] x y[2] a y
18844    fi
18845  endl done
18846
18847#@cli circle3d : _x0,_y0,_z0,_radius>=0
18848#@cli : Input 3D circle at specified coordinates.
18849#@cli : Default values: 'x0=y0=z0=0' and 'radius=1'.
18850#@cli : $ repeat 500 a={$>*pi/250} circle3d {cos(3*$a)},{sin(2*$a)},0,{$a/50} color3d[-1] ${-rgb},0.4 done add3d
18851circle3d : skip ${1=0},${2=0},${3=0},${4=1}
18852  e[^-1] "Input 3D circle at position ($1,$2,$3) with radius $4."
18853  r={$4/sqrt(3)}
18854  1,24,1,1,\
18855  67.5,73.5,109.5,103.5,51.5,100.5,2,1,\
18856  {$1-$r},{$2-$r},{$3-$r},\
18857  {$1+$r},{$2+$r},{$3+$r},\
18858  5,0,1,0,0,0,200,200,200,1
18859  nm. [3D\ circle]
18860
18861#@cli circles3d : _radius>=0,_is_wireframe={ 0 | 1 }
18862#@cli : Convert specified 3D objects to sets of 3D circles with specified radius.
18863#@cli : Default values: 'radius=1' and 'is_wireframe=1'.
18864#@cli : $ image.jpg luminance resize2dy 40 threshold 50% * 255 pointcloud3d color3d[-1] 255,255,255 circles3d 0.7
18865circles3d : check "${1=1}>=0 && isbool(${2=0})"
18866  e[^-1] "Convert 3D object$? to sets of 3D "${arg\ 1+$2,filled,wireframe}" circles with radius $1."
18867  p3d 0 repeat $! l[$>]
18868    -3d {$1/2},0,0 ++3d $1,0,0 +3d[1] [0]
18869    s3d # Two 3d objects decomposed here!
18870    rows[7] 0 j[1] [7] # Number of points
18871    rv[2,8] # Points
18872    rv[3,9] l[3] r 2,{h/2},1,1,-1 z 1,1 s y,2 i[0] 1,100%,1,1,5 1,100%,1,1,$2 2,100% a x y endl # Primitives
18873    k[0-5] a y
18874  endl done
18875
18876#@cli col3d : eq. to 'color3d'. : (+)
18877
18878#@cli color3d : R,_G,_B,_opacity : (no arg) : (+)
18879#@cli : Set color and opacity of selected 3D objects.
18880#@cli : (eq. to 'col3d').
18881#@cli : Default value: 'B=G=R' and 'opacity=(undefined)'.
18882#@cli : $ torus3d 100,10 double3d 0 repeat 7 +rotate3d[-1] 1,0,0,20 color3d[-1] ${-rgb} done add3d
18883
18884#@cli colorcube3d
18885#@cli : Input 3D color cube.
18886#@cli : $ colorcube3d mode3d 2 +primitives3d 1
18887colorcube3d :
18888  e[^-1] "Input 3D RGB-color cube."
18889  (67.5;73.5;109.5;103.5;51.5;100.5;8;6)
18890  (0;0;0;\
18891   255;0;0;\
18892   255;255;0;\
18893   0;255;0;\
18894   0;0;255;\
18895   255;0;255;\
18896   255;255;255;\
18897   0;255;255)
18898  (12;0;3;2;1;0;0;0;63;63;63;63;0;\
18899   12;1;2;6;5;0;0;0;63;63;63;63;0;\
18900   12;0;4;7;3;0;0;63;0;63;63;0;63;\
18901   12;4;5;6;7;0;0;63;0;63;63;0;63;\
18902   12;0;1;5;4;0;0;63;0;63;63;0;63;\
18903   12;3;7;6;2;0;0;0;63;63;63;63;0)
18904  (0,255;0,255^0,0;255,255^0,0;0,0)
18905  (255,255;255,255^0,0;255,255^0,255;0,255)
18906  (0,0;0,0^0,0;255,255^0,255;0,255)
18907  (0,255;0,255^0,0;255,255^255,255;255,255)
18908  (0,255;0,255^0,0;0,0^0,0;255,255)
18909  (0,255;0,255^255,255;255,255^0,0;255,255)
18910  r[-6--1] 64,64,1,3,3 round[-6--1] y[-6--1] i[-7--2] (-128;64;64;3)
18911  (1;1;1;1;1;1)
18912  a[-16--1] y nm. [3D\ colorcube]
18913
18914#@cli cone3d : _radius,_height,_nb_subdivisions>0
18915#@cli : Input 3D cone at (0,0,0), with specified geometry.
18916#@cli : Default value: 'radius=1','height=1' and 'nb_subdivisions=24'.
18917#@cli : $ cone3d 10,40 +primitives3d 1 color3d[-2] ${-rgb}
18918cone3d : check ${3=24}>0 skip ${1=1},${2=1}
18919  e[^-1] "Input 3D cone, with radius $1, height $2 and $3 subdivisions."
18920  # Header.
18921  (67.5;73.5;109.5;103.5;51.5;100.5)
18922  ({$3+2};{2*$3})
18923
18924  # Vertices.
18925  (0,0,0;0,0,$2)
18926  (0;{2*pi}) r. 1,{$3+1},1,1,3 rows. 0,{$3-1} +sin. cos.. *[-2,-1] $1 a[-2,-1] x z. 0,2 a[-2,-1] y
18927
18928  # Primitives.
18929  1,$3,1,1,'y' +shift. 0,-1 +[-2,-1] 2
18930  2,$3,1,1,3,0 .. [-4] a[-3--1] x
18931  i[-4] 2,$3,1,1,3,1 a[-4--2] x
18932  a[-2,-1] y
18933
18934  # Colors / opacities.
18935  3,{h},1,1,200
18936  1,{h},1,1,1
18937  y[-4--2] a[-6--1] y nm. [3D\ cone]
18938
18939#@cli cubes3d : _size>=0
18940#@cli : Convert specified 3D objects to sets of 3D cubes with specified size.
18941#@cli : Default value: 'size=1'.
18942#@cli : $ image.jpg luminance resize2dy 40 threshold 50% * 255 pointcloud3d color3d[-1] 255,255,255 cubes3d 1
18943cubes3d : check ${1=1}>=0
18944  e[^-1] "Convert 3D object$? to sets of 3D cubes with size $1."
18945  p3d 0 repeat $! l[$>]
18946    nbv={@6} nbp={@7}
18947    if $nbv&&$nbp
18948      s3d
18949      l[1] = {8*i[0]} = {6*i[1]},0,1 endl  # Header.
18950      l[2] r 3,{h/3},1,1,-1                  # Vertices.
18951      half={$1/2}
18952      - '$half,0,0' ++ '$1,0,0' a x
18953      - '0,$half,0' ++ '0,$1,0' a x
18954      - '0,0,$half' ++ '0,0,$1' a x
18955      endl
18956      l[3] r 2,{h/2},1,1,-1                  # Primitives.
18957      z 1,1 * 8 r 4,100% i[0] 1,100%,1,1,4 a x [-1]x5 a x
18958      + '"0,0,2,3,1, 0,4,5,7,6, 0,0,1,5,4, 0,2,6,7,3, 0,0,4,6,2, 0,1,3,7,5"'
18959      endl
18960      l[4] r 3,{h/3},1,1,-1 r 18,100%,1,1,0,2 endl r[5] 6,100% # Colors & opacities.
18961      y a y
18962    fi
18963  endl done
18964
18965#@cli cup3d : _resolution>0
18966#@cli : Input 3D cup object.
18967#@cli : Default value: 'resolution=128'.
18968#@cli : $ cup3d ,
18969cup3d : check ${1=128}>0
18970  e[^-1] "Input 3D cup, with resolution $1."
18971  100,200
18972  ellipse. 0%,0%,40%,40%,0,1,1
18973  ellipse. 0,0,35%,35%,0,1,0
18974  polygon. 4,0,45%,8%,45%,20%,90%,0,90%,1,1
18975  ellipse. 0%,100%,30%,10%,0,1,1 b. 0.1%
18976  lathe3d. $1,2 nm. [3D\ cup]
18977
18978#@cli cylinder3d : _radius,_height,_nb_subdivisions>0
18979#@cli : Input 3D cylinder at (0,0,0), with specified geometry.
18980#@cli : Default value: 'radius=1','height=1' and 'nb_subdivisions=24'.
18981#@cli : $ cylinder3d 10,40 +primitives3d 1 color3d[-2] ${-rgb}
18982cylinder3d : check ${3=24}>0 skip ${1=1},${2=1}
18983  e[^-1] "Input 3D cylinder, with radius $1, height $2 and $3 subdivisions."
18984  l[]
18985    N={round($3)}
18986    nbv,nbp={[2*$N+2,3*$N]}
18987    ({0.5+[{'CImg3d'}]})
18988    ($nbv,$nbp)
18989    1,$nbv,1,3,"Z = (y<="$N"?0:$2); ang = ((y%("$N"+1))-1)*2*pi/"$N";
18990                !(y%("$N"+1))?[0,0,Z]:[$1*cos(ang),$1*sin(ang),Z]"
18991    1,$N,1,13,"i1 = 1 + y; i2 = 1 + (i1%"$N"); const j0 = "$N" + 1; j1 = j0 + i1; j2 = j0 + i2;
18992               [ 3,0,i2,i1, 3,j0,j1,j2, 4,i1,i2,j2,j1 ]"
18993    permute[^0,1] "cyzx" 1,$nbp,1,3,200 1,$nbp,1,1,1 y a y
18994   nm. [3D\ cylinder] endl
18995
18996#@cli delaunay3d
18997#@cli : Generate 3D delaunay triangulations from selected images.
18998#@cli : One assumes that the selected input images are binary images containing the set of points to mesh.
18999#@cli : The output 3D object is a mesh composed of non-oriented triangles.
19000#@cli : $ 500,500 noise 0.05,2 eq 1 * 255 +delaunay3d color3d[1] 255,128,0 dilate_circ[0] 5 to_rgb[0] \
19001# +object3d[0] [1],0,0,0,1,1 max[-1] [0]
19002delaunay3d :
19003  e[^-1] "Generate 3D delaunay triangulation from image$?."
19004  repeat $! l[$>]
19005    channels 0 != 0
19006
19007    # Label each point separately
19008    whd={w},{h},{d} +r 1,{w*h*d},1,1,-1 cumulate. *. .. r. $whd,1,-1
19009
19010    # Compute voronoi diagram of point cloud.
19011    +distance[0] 1 *[2] -1 watershed[1] [2] rm[2]
19012
19013    # Get redondant set of delaunay triangles from the voronoi diagram.
19014    r[1] 100%,100%,100%,3
19015    if d>1 # Add detection cases for 3D images.
19016      +_delaunay3d[1] 1,0,0,0,0,1 +_delaunay3d[1] -1,0,0,0,0,-1
19017      +_delaunay3d[1] 0,1,0,0,0,1 +_delaunay3d[1] 0,-1,0,0,0,-1
19018    fi
19019    +_delaunay3d[1] 1,0,0,0,1,0 _delaunay3d[1] -1,0,0,0,-1,0 # 2D detection.
19020    a[^0] x transpose. -. 1
19021
19022    # Build 3D mesh.
19023    pointcloud3d[0]
19024    s3d[0] rm[3-5] i.. 1,100%,1,1,3 a[-2,-1] x
19025    3,100%,1,1,200 1,100%,1,1,1 =[1] {h},0,1 y a y
19026  endl done
19027
19028_delaunay3d :
19029  f. "A=j($1,$2,$3,0,0,1); B=j($4,$5,$6,0,0,1);
19030      if(i!=A && i!=B && A!=B, kth(1+c,i,A,B),0)"
19031  discard. 0 r. {h/3},3,1,1,-1
19032
19033#@cli distribution3d
19034#@cli : Get 3D color distribution of selected images.
19035#@cli : $ image.jpg distribution3d colorcube3d primitives3d[-1] 1 add3d
19036distribution3d :
19037  e[^-1] "Get 3D color distribution of image$?."
19038  to_rgb permute "cxyz" y
19039  repeat $! l[$>]
19040    nbp={round(h/3)}
19041    i.. (67.5;73.5;109.5;103.5;51.5;100.5;\    # Magick number for CImg3d.
19042         $nbp;$nbp)                            # Number of vertices and primitives.
19043    1,$nbp,1,1,1 +f. y a[-2,-1] x y.           # Primitives.
19044    ..                                         # Colors.
19045    1,$nbp,1,1,1                               # Opacities.
19046    a y nm. [3D\ distribution]                 # Build 3D object.
19047  endl done
19048
19049#@cli /3d : eq. to 'div3d'. : (+)
19050
19051#@cli div3d : factor : factor_x,factor_y,_factor_z : (+)
19052#@cli : Scale selected 3D objects isotropically or anisotropically, with the inverse of specified
19053#@cli : factors.
19054#@cli : (eq. to '/3d').
19055#@cli : Default value: 'factor_z=0'.
19056#@cli : $ torus3d 5,2 repeat 5 +add3d[-1] 12,0,0 div3d[-1] 1.2 color3d[-1] ${-rgb} done add3d
19057
19058#@cli db3d : eq. to 'double3d'. : (+)
19059
19060#@cli double3d : _is_double_sided={ 0 | 1 } : (+)
19061#@cli : Enable/disable double-sided mode for 3D rendering.
19062#@cli : (eq. to 'db3d').
19063#@cli : Default value: 'is_double_sided=1'.
19064#@cli : $ mode3d 1 repeat 2 torus3d 100,30 rotate3d[-1] 1,1,0,60 double3d $> snapshot3d[-1] 400 done
19065
19066#@cli elevation3d : { z-factor | [elevation_map] | 'formula' },base_height={ -1 | >=0 } : (no arg)
19067#@cli : Generate 3D elevation of selected images, opt. with a specified elevation map.
19068#@cli : When invoked with (no arg) or 'z-factor', the elevation map is computed as the pointwise L2 norm of the
19069#@cli : pixel values. Otherwise, the elevation map is taken from the specified image or formula.
19070#@cli : $ image.jpg +blur 5 elevation3d. 0.75
19071#@cli : $ 128,128,1,3,u(255) plasma 10,3 blur 4 sharpen 10000 n 0,255 \
19072# elevation3d[-1] 'X=(x-64)/6;Y=(y-64)/6;-100*exp(-(X^2+Y^2)/30)*abs(cos(X)*sin(Y))'
19073elevation3d : skip "${1=_noarg}" check "${2=-1}==-1 || $2>=0"
19074  if $2>=0 base_str=" and base height $2" else base_str= fi
19075  if isnum($1)
19076    e[^-1] "Generate 3D elevation of image$?, with z-factor $1"$base_str. argtype,zfactor=0,$1
19077  elif ${"is_image_arg $1"}
19078    e[^-1] "Generate 3D elevation of image$?, from elevation $1"$base_str. argtype=2
19079    pass$1 0 if s>1 norm. fi store. elevation_img
19080  elif isexpr($1)
19081    e[^-1] "Generate 3D elevation of image$?, with formula '$1'"$base_str. argtype=1
19082  else
19083    e[^-1] "Generate 3D elevation of image$?." argtype,zfactor=0,1
19084  fi
19085  is_base={$2>=0}
19086
19087  repeat $! l[$>] nm={n}
19088    to_rgb M,N={[w,h]}
19089
19090    # Generate vertices.
19091    100%,100%,1,2,[x,y]
19092    if !$argtype +norm[0] *. $zfactor # with z-factor
19093    elif $argtype==1 [0],[0],1,1,"$1" # with formula
19094    else $elevation_img # from [image]
19095    fi
19096    a[-2,-1] c
19097
19098    if $is_base . sh. 100% f. {-sign($1)*$2} rm. a[-2,-1] y fi
19099    r. {wh},1,1,3,-1 permute. cxyz nbv={h}
19100
19101    # Generate primitives.
19102    header="const M = "$M"; const N = "$N"; const MN = M*N"
19103    {[$M,$N]-1},1,5,$header"; # Rear
19104      i0 = M*y + x; i1 = i0 + MN;
19105      [ 4,ui2f(i0),ui2f(i0 + M),ui2f(i0 + 1 + M),ui2f(i0 + 1) ]"
19106    r. 1,{wh},1,100%,-1 nbp={h}
19107
19108    if $is_base # Add base primitives if necessary
19109      {[$M,$N]-1},1,5,$header"; # Front
19110        i0 = M*y + x; i1 = i0 + MN;
19111        [ 4,ui2f(i1),ui2f(i1 + 1),ui2f(i1 + 1 + M),ui2f(i1 + M) ]"
19112      r. 1,{wh},1,100%,-1 nbp+={h}
19113      {$M-1},1,1,10,$header" ; # Top and bottom
19114        i0 = x; i1 = i0 + MN; i2 = i0 + M*(N - 1); i3 = i2 + MN;
19115        [ 4,ui2f(i0),ui2f(i0 + 1),ui2f(i1 + 1),ui2f(i1),
19116          4,ui2f(i2),ui2f(i3),ui2f(i3 + 1),ui2f(i2 + 1) ]"
19117      r. 1,{wh},1,100%,-1 s. c,2 nbp+={2*h}
19118      {$N-1},1,1,10,$header"; # Left and right
19119        i0 = M*x; i1 = i0 + MN; i2 = i0 + M - 1; i3 = i2 + MN;
19120        [ 4,ui2f(i0),ui2f(i1),ui2f(i1 + M),ui2f(i0 + M),
19121          4,ui2f(i2),ui2f(i2 + M),ui2f(i3 + M),ui2f(i3) ]"
19122      r. 1,{wh},1,100%,-1 s. c,2 nbp+={2*h}
19123      permute[-6--1] cyxz -a[-6--1] y
19124    else permute. cyxz
19125    fi
19126
19127    # Generate colors / opacities.
19128    mv[0] $! r. {[w,h]-1},1,3,0 r. {wh},1,1,3,-1 permute. cxyz
19129    if $is_base 3,{$nbp-h},1,1,200 fi
19130    1,$nbp,1,1,1
19131
19132    # Add header and merge object.
19133    i[0] ('CImg3d':y) +[0] 0.5 i[1] ({ui2f([$nbv,$nbp]):;}) y a y
19134    nm $nm
19135  endl done
19136
19137#@cli empty3d
19138#@cli : Input empty 3D object.
19139#@cli : $ empty3d
19140empty3d :
19141  e[^-1] "Input empty 3D object."
19142  (67.5;73.5;109.5;103.5;51.5;100.5;0;0) nm. [3D\ empty]
19143
19144#@cli extrude3d : _depth>0,_resolution>0,_smoothness[%]>=0
19145#@cli : Generate extruded 3D object from selected binary XY-profiles.
19146#@cli : Default values: 'depth=16', 'resolution=1024' and 'smoothness=0.5%'.
19147#@cli : $ image.jpg threshold 50% extrude3d 16
19148extrude3d : check "${1=16}>0 && ${2=1024}>0 && ${3=0.5%}>=0"
19149  e[^-1] "Generate extruded 3D object from XY-profile$?, with depth $1, resolution $2 and smoothness $3."
19150  norm n 0,1 autocrop 0 repeat $! l[$>] nm={0,n}
19151    wr={round(max(1,if(w>h,min($2,w),min($2,h)*w/h)))}
19152    hr={round(max(1,if(w>h,min($2,w)*h/w,min($2,h))))}
19153    fact={$1/max(w/$wr,h/$hr)}
19154    b $3,0 r $wr,$hr,1,1,2 expand_xyz 1,0
19155    isosurface3d 50% *3d 1,1,$fact rv3d
19156  nm $nm endl done
19157
19158#@cli f3d : eq. to 'focale3d'. : (+)
19159
19160#@cli focale3d : focale : (+)
19161#@cli : Set 3D focale.
19162#@cli : (eq. to 'f3d').\n
19163#@cli : Set 'focale' to 0 to enable parallel projection (instead of perspective).
19164#@cli : Set negative 'focale' will disable 3D sprite zooming.
19165#@cli : Default value: 'focale=700'.
19166#@cli : $ repeat 5 torus3d 100,30 rotate3d[-1] 1,1,0,60 focale3d {$<*90} snapshot3d[-1] 400 done remove[0]
19167
19168#@cli gaussians3d : _size>0,_opacity
19169#@cli : Convert selected 3D objects into set of 3D gaussian-shaped sprites.
19170#@cli : $ image.jpg r2dy 32 distribution3d gaussians3d 20 colorcube3d primitives3d[-1] 1 +3d
19171gaussians3d : check "${1=32}>0" skip ${2=0.3}
19172  e[^-1] "Convert 3D object$? into sets of gaussian-shaped 3D sprites, with size $1 and opacity $2."
19173  p3d 2 p3d 0 repeat $! l[$>] nm={0,n} s3d
19174    nbv={h} rm. (-128;$1;$1;1)
19175    $1,$1 gaussian. 35%,35%,0 c. 30%,100% n. 0,$2 y. a[-2,-1] y      # First opacity is generated.
19176    if $nbv>1 4,{$nbv-1},1,1,-128,0,0,0 y[-2,-1] a[-2,-1] y fi   # Other ones are shared copies of the first one.
19177    a y
19178  nm $nm endl done
19179
19180#@cli gmic3d
19181#@cli : Input a 3D G'MIC logo.
19182#@cli : $ gmic3d +primitives3d 1
19183gmic3d :
19184  e[^-1] "Input 3D G\47MIC logo."
19185  text3d G,60,20,2 col3d. 16,64,255
19186  text3d \',60,20,2 +3d. 40 col3d. 64,128,255
19187  text3d M,60,20,2 +3d. 50 col3d. 96,196,255
19188  text3d I,60,20,2 +3d. 90 col3d. 64,128,255
19189  text3d C,60,20,2 +3d. 100 col3d. 16,64,255
19190  sphere3d 8 +3d. 102,-3,20 col3d. 192,128,255
19191  +3d[-6--1] c3d.
19192  repeat 30
19193    box3d {min(3+$</2,10)} col3d. {30*$>},{20+80*$>},{10*$>},0.5
19194    r3d. 1,1,1,{$>*12}
19195    +3d. {80*cos(0.5+1.02*$>*12*pi/180)},{30*sin(0.8+$>*12*pi/180)},{2*$>-60}
19196  done
19197  +3d[-30--1] +3d. 0,5,30 +3d[-2--1] nm. [3d\ gmic]
19198
19199#@cli gyroid3d : _resolution>0,_zoom
19200#@cli : Input 3D gyroid at (0,0,0), with specified resolution.
19201#@cli : Default values: 'resolution=32' and 'zoom=5'.
19202#@cli : $ gyroid3d 48 +primitives3d 1
19203gyroid3d : check ${1=32}>0 skip ${2=5}
19204  e[^-1] "Input 3D gyroid, with resolution $1 and range $2."
19205  isosurface3d "'0.49*(\
19206    cos( 2*x + y + z - pi) + cos( 2*x - y + z - pi)\
19207    + cos(- 2*x + y - z - pi) + cos(- 2*x - y - z - pi)\
19208    + cos( x + 2*y + z - pi) + cos( x + 2*y - z - pi)\
19209    + cos(- x - 2*y + z - pi) + cos(- x - 2*y - z - pi)\
19210    + cos( x + y + 2*z - pi) + cos(- x + y + 2*z - pi)\
19211    + cos( x - y - 2*z - pi) + cos(- x - y - 2*z - pi)\
19212    + cos(- 2*x + y + z) + cos( 2*x + y - z)\
19213    + cos(- 2*x - y + z) + cos( 2*x - y - z)\
19214    + cos(- x + 2*y + z) + cos( x - 2*y + z)\
19215    + cos(- x + 2*y - z) + cos( x - 2*y - z)\
19216    + cos( x - y + 2*z) + cos( x + y - 2*z)\
19217    + cos(- x - y + 2*z) + cos(- x + y - 2*z)\
19218    ) + 0.27*( \
19219    cos(- 2*x + 2*y - pi) + cos( 2*x - 2*y - pi)\
19220    + cos( 2*x + 2*y - pi) + cos(- 2*x - 2*y - pi)\
19221    + cos(- 2*y + 2*z - pi) + cos( 2*y - 2*z - pi)\
19222    + cos( 2*y + 2*z - pi) + cos(- 2*y - 2*z - pi)\
19223    + cos(- 2*z + 2*x - pi) + cos( 2*z - 2*x - pi)\
19224    + cos( 2*z + 2*x - pi) + cos(- 2*z - 2*x - pi)\
19225    ) - 0.69'",0,{-$2},{-$2},{-$2},$2,$2,$2,$1,$1,$1
19226  c3d. n3d. nm. [3D\ gyroid]
19227
19228#@cli histogram3d
19229#@cli : Get 3D color histogram of selected images.
19230#@cli : $ image.jpg resize2dx 64 histogram3d circles3d 3 opacity3d. 0.75 colorcube3d primitives3d[-1] 1 add3d
19231histogram3d :
19232  e[^-1] "Get 3D color histogram of image$?."
19233  to_rgb repeat $! l[$>]
19234    r {wh},3,1,1,-1 pointcloud 1 n 0,255 palette hot point. 0,0,0,1,0 map.. . rm. pointcloud3d nm "[3D histogram]"
19235  endl done
19236
19237#@cli image6cube3d
19238#@cli : Generate 3D mapped cubes from 6-sets of selected images.
19239#@cli : $ image.jpg animate flower,"30,0","30,5",6 image6cube3d
19240image6cube3d :
19241  e[^-1] "Generate 3D mapped cubes from image$?."
19242  M={max(${-max_wh})} r $M,$M,1,3 imageplane3d n3d c3d
19243  repeat int($!/6) l[$>-{$>+5}]
19244    +3d[0] 0,0,-0.5
19245    r3d[1] 0,1,0,90 +3d[1] -0.5,0,0
19246    r3d[2] 0,1,0,180 +3d[2] 0,0,0.5
19247    r3d[3] 0,1,0,270 +3d[3] 0.5,0,0
19248    r3d[4] 1,0,0,90 +3d[4] 0,0.5,0
19249    r3d[5] 1,0,0,270 +3d[5] 0,-0.5,0
19250    +3d nm "[3D image cube]"
19251  endl done
19252
19253#@cli imageblocks3d : _maximum_elevation,_smoothness[%]>=0
19254#@cli : Generate 3D blocks from selected images.
19255#@cli : Transparency of selected images is taken into account.
19256#@cli : Default values: 'maximum_elevation=10' and 'smoothness=0'.
19257#@cli : $ image.jpg resize2dy 32 imageblocks3d -20 mode3d 3
19258imageblocks3d : check ${2=0}>=0 skip ${1=10},${3=0}
19259  e[^-1] "Generate 3D blocks from image$?, with maximum elevation $1 and smoothness $2."
19260  repeat $! l[$>]
19261    w={w} h={h}
19262    split_opacity to_rgb[0] is_opacity={$!==2}
19263
19264    # Create 3D object template.
19265    l[] box3d 1,1,0
19266    repeat $w-1 ++3d. 1,0,0 done +3d
19267    repeat $h-1 ++3d. 0,1,0 done +3d
19268    endl
19269    s3d.
19270
19271    # Set vertex altitudes.
19272    +norm[0] b. $2
19273    y. n. 0,$1
19274    r[-5] 24,{-5,round(w*h/24)},1,1,-1
19275    if $1<0 j[-5] .,2 j[-5] .,5 j[-5] .,8 j[-5] .,11
19276    else j[-5] .,14 j[-5] .,17 j[-5] .,20 j[-5] .,23
19277    fi
19278    rm. y[-4]
19279
19280    # Set primitive colors.
19281    rm.. r[0] {0,wh},1,1,100%,-1 permute[0] cxyz r[0] 600%,100%,1,1,0,2 y[0] mv[0] -1
19282
19283    # Set primitive opacities.
19284    if $is_opacity rm. mv[0] $! /. 255 y. r. 6,100%,1,1 y. fi
19285
19286    a y
19287  endl done
19288
19289#@cli imagecube3d
19290#@cli : Generate 3D mapped cubes from selected images.
19291#@cli : $ image.jpg imagecube3d
19292imagecube3d :
19293  e[^-1] "Generate 3D mapped cubes from image$?."
19294  slices 50% to_rgb repeat $! l[$>] nm={0,n}
19295    i.. (67.5;73.5;109.5;103.5;51.5;100.5;\  # Magick number for CImg3d.
19296         8;6;\                               # Number of vertices and primitives.
19297         -0.5;-0.5;-0.5;\                    # Vertex coordinates.
19298         0.5;-0.5;-0.5;\
19299         0.5;0.5;-0.5;\
19300         -0.5;0.5;-0.5;\
19301         -0.5;-0.5;0.5;\
19302         0.5;-0.5;0.5;\
19303         0.5;0.5;0.5;\
19304         -0.5;0.5;0.5;\
19305         12;0;3;2;1;0;0;0;{h};{w};{h};{w};0;\    # Primitives description.
19306         12;1;2;6;5;0;0;0;{h};{w};{h};{w};0;\
19307         12;5;6;7;4;0;0;0;{h};{w};{h};{w};0;\
19308         12;4;7;3;0;0;0;0;{h};{w};{h};{w};0;\
19309         12;4;0;1;5;0;0;0;{h};{w};{h};{w};0;\
19310         12;3;7;6;2;0;0;0;{h};{w};{h};{w};0;\
19311         -128;{w};{h};{s})       # Texture map for the first face.
19312    y.
19313    (-128;0;0;0;-128;0;0;0;-128;0;0;0;-128;0;0;0;-128;0;0;0;1;1;1;1;1;1)  # Other faces and opacities.
19314    a y
19315  nm $nm endl done
19316
19317#@cli imageplane3d
19318#@cli : Generate 3D mapped planes from selected images.
19319#@cli : $ image.jpg imageplane3d
19320imageplane3d :
19321  e[^-1] "Generate 3D mapped planes from image$?."
19322  slices 50% to_color repeat $! l[$>] nm={0,n}
19323    i.. (67.5;73.5;109.5;103.5;51.5;100.5;\   # Magick number for CImg3d.
19324         4;1;\                                # Number of vertices and primitives.
19325         0;0;0;\                              # Vertex coordinates.
19326         {w};0;0;\
19327         {w};{h};0;\
19328         {0};{h};0;\
19329         12;0;3;2;1;0;0;0;{h};{w};{h};{w};0;\ # Primitives description.
19330         -128;{w};{h};{s})                    # Texture map.
19331    y.
19332    (1)  # Opacity.
19333    a y
19334  nm $nm endl done
19335
19336#@cli imagepyramid3d
19337#@cli : Generate 3D mapped pyramids from selected images.
19338#@cli : $ image.jpg imagepyramid3d
19339imagepyramid3d :
19340  e[^-1] "Generate 3D mapped pyramids from image$?."
19341  to_rgb repeat $! l[$>] nm={0,n}
19342    w2={w/2}
19343    i.. (67.5;73.5;109.5;103.5;51.5;100.5;\  # Magick number for CImg3d.
19344         5;5;\                               # Number of vertices and primitives.
19345         -0.5;-0.5;-0.5;\                    # Vertex coordinates.
19346         0.5;-0.5;-0.5;\
19347         0.5;0.5;-0.5;\
19348         -0.5;0.5;-0.5;\
19349         0;0;0.5;\
19350         12;0;3;2;1;0;0;0;{h};{w};{h};{w};0;\    # Primitives description.
19351         9;0;4;3;0;{h};$w2;0;{w};{h};\
19352         9;1;4;0;0;{h};$w2;0;{w};{h};\
19353         9;2;4;1;0;{h};$w2;0;{w};{h};\
19354         9;3;4;2;0;{h};$w2;0;{w};{h};\
19355         -128;{w};{h};{s})                   # Texture map for the first face.
19356    y.
19357    (-128;0;0;0;-128;0;0;0;-128;0;0;0;-128;0;0;0;1;1;1;1;1)  # Other faces and opacities.
19358    a y
19359  nm $nm endl done
19360
19361#@cli imagerubik3d : _xy_tiles>=1,0<=xy_shift<=100,0<=z_shift<=100
19362#@cli : Generate 3D mapped rubik's cubes from selected images.
19363#@cli : Default values: 'xy_tiles=3', 'xy_shift=5' and 'z_shift=5'.
19364#@cli : $ image.jpg imagerubik3d ,
19365imagerubik3d : check "${1=3}>=1 && ${2=5}>=0 && $2<=100 && ${3=5}>=0 && $3<=100"
19366  e[^-1] "Generate 3D mapped rubik\47s cubes from image$? with $1 xy-tiles, xy-shift $2 and z-shift $3."
19367  repeat $! l[$>] nm={0,n}
19368    # Generate primary 3D side.
19369    ('CImg3d') +. 0.5
19370    (8,5)
19371    (0,0,0;\
19372     100,0,0;\
19373     100,100,0;\
19374     0,100,0;\
19375     $2,$2,{-$3};\
19376     {100-$2},$2,{-$3};\
19377     {100-$2},{100-$2},{-$3};\
19378     $2,{100-$2},{-$3})
19379    (4,4,7,6,5;\
19380     4,0,4,5,1;\
19381     4,3,2,6,7;\
19382     4,0,3,7,4;\
19383     4,1,5,6,2)
19384    3,5,1,1,200
19385    1,5,1,1,1
19386    y[-6--1] a[-6--1] y
19387    repeat $1-1 ++3d. 100 done +3d[-$1--1]   # Duplicate along X
19388    repeat $1-1 ++3d. 0,100 done +3d[-$1--1] # Duplicate along Y
19389    t3d. .. rm..
19390    /3d. $1 -3d. 50,50,50
19391    +r3d. 0,1,0,-90 +r3d. 0,1,0,-90 +r3d. 0,1,0,-90  # Generate the 5 other sides.
19392    +r3d. 0,0,1,-90 +r3d. 0,0,1,180
19393    +3d
19394  nm $nm endl done
19395
19396#@cli imagesphere3d : _resolution1>=3,_resolution2>=3
19397#@cli : Generate 3D mapped sphere from selected images.
19398#@cli : Default values: 'resolution1=32' and 'resolutions2=16'.
19399#@cli : $ image.jpg imagesphere3d 32,16
19400imagesphere3d : check "${1=32}>=3 && ${2=16}>=3"
19401  e[^-1] "Generate 3D mapped sphere from image$?, with resolutions ($1,$2)."
19402  to_rgb repeat $! l[$>] nm={0,n}
19403
19404    # Generate object header.
19405    tw={w-1} th={h-1}                    # Maximum texture xy-coordinates.
19406    nbv={2+$1*($2-2)}                    # Number of vertices.
19407    nbp={$1*($2-1)}                      # Number of primitives.
19408    (67.5;73.5;109.5;103.5;51.5;100.5;\  # Magick number for CImg3d.
19409     $nbv;$nbp)                          # Number of vertices and primitives.
19410
19411    # Define sphere vertices.
19412    (0;0;1) (0;0;-1) (0,{2*pi};0,{2*pi}^0,0;{pi},{pi})
19413    r. {$1+1},$2,1,2,3 z. 0,1,{w-2},{h-2} s. c
19414    +sin. +sin... *[-2,-1] +cos.. sin... cos[-4] *[-4,-3]
19415    a[-3--1] c permute. cxyz y. a[-3--1] y
19416
19417    # Define sphere primitives (triangles and quadrangles).
19418    repeat $1,v
19419      tx0={$v*$tw/$1} tx1={($v+1)*$tw/$1} ty1={$th/($2-1)}
19420      (9;0;{2+$v};{2+($v+1)%$1};{$tw/2};0;$tx0;$ty1;$tx1;$ty1) # Textured triangle from 1st pole.
19421      repeat $2-3,u
19422        ty0=$ty1 ty1={($u+2)*$th/($2-1)} i0={2+$u*$1+$v} i1={2+$u*$1+($v+1)%$1}
19423        (12;$i0;{$i0+$1};{$i1+$1};$i1;$tx0;$ty0;$tx0;$ty1;$tx1;$ty1;$tx1;$ty0) # Textured quadrangle.
19424      done
19425      (9;1;{2+$1*($2-3)+($v+1)%$1};{2+$1*($2-3)+$v};{$tw/2};$th;$tx1;$ty1;$tx0;$ty1) # Textured triangle from 2nd pole.
19426    done
19427    a[-$nbp--1] y
19428
19429    # Define sphere textures, opacities and generate object.
19430    mv[-4] $! i.. (-128;{w};{h};3) y. 1,{4*($nbp-1)},1,1,-128,0,0,0 1,$nbp,1,1,1 a y
19431  nm $nm endl done
19432
19433#@cli isoline3d : isovalue[%] : 'formula',value,_x0,_y0,_x1,_y1,_size_x>0[%],_size_y>0[%] : (+)
19434#@cli : Extract 3D isolines with specified value from selected images or from specified formula.
19435#@cli : Default values: 'x0=y0=-3', 'x1=y1=3' and 'size_x=size_y=256'.
19436#@cli : $ image.jpg blur 1 isoline3d 50%
19437#@cli : $ isoline3d 'X=x-w/2;Y=y-h/2;(X^2+Y^2)%20',10,-10,-10,10,10
19438
19439#@cli isosurface3d : isovalue[%] : 'formula',value,_x0,_y0,_z0,_x1,_y1,_z1,_size_x>0[%],_size_y>0[%],_size_z>0[%] : (+)
19440#@cli : Extract 3D isosurfaces with specified value from selected images or from specified formula.
19441#@cli : Default values: 'x0=y0=z0=-3', 'x1=y1=z1=3' and 'size_x=size_y=size_z=32'.
19442#@cli : $ image.jpg resize2dy 128 luminance threshold 50% expand_z 2,0 blur 1 isosurface3d 50% mul3d 1,1,30
19443#@cli : $ isosurface3d 'x^2+y^2+abs(z)^abs(4*cos(x*y*z*3))',3
19444
19445#@cli label3d : "text",font_height>=0,_opacity,_color1,...
19446#@cli : Generate 3D text label.
19447#@cli : Default values: 'font_height=13', 'opacity=1' and 'color=255,255,255'.
19448label3d : check ${2=13}>=0 skip ${3=1},${4=255},${5=$4},${6=$5}
19449  e[^-1] "Generate 3D label '$1' with font height $2, opacity $3 and color (${4--1})."
19450  l[] 0 t "$1",0,0,$2,1,${4--1},255 sprite3d endl
19451
19452#@cli label_points3d : _label_size>0,_opacity
19453#@cli : Add a numbered label to all vertices of selected 3D objects.
19454#@cli : Default values: 'label_size=13' and 'opacity=0.8'.
19455#@cli : $ torus3d 100,40,6,6 label_points3d 23,1 mode3d 1
19456label_points3d : check ${1=13}>0 skip ${2=0.8}
19457  e[^-1] "Label vertices of 3D object$?."
19458  repeat $!
19459    +p3d[$>] 0 l. s3d rm[-3--1]
19460    nbp={-2,@0} =.. $nbp,0,1                               # Set correct number of primitives
19461    (1,0;1,{$nbp-1}) r. 2,$nbp,1,1,3 r. 1,{2*h},1,1,-1    # Create new primitive data
19462    repeat $nbp                                            # Create texture labels as primitive colors.
19463      0 t. $>,0,0,$1,1,255,255,255 autocrop. 0
19464      i.. (-128;{w};{h};3) y.
19465    done
19466    repeat $nbp                                            # Create texture masks as primitive opacities.
19467      0 t. $>,0,0,$1,1,$2 autocrop. 0
19468      i.. (-128;{w};{h};1) y.
19469    done
19470    a y  # Merge final object data.
19471    endl
19472    +3d[$>,-1]
19473  done
19474
19475#@cli lathe3d : _resolution>0,_smoothness[%]>=0,_max_angle>=0
19476#@cli : Generate 3D object from selected binary XY-profiles.
19477#@cli : Default values: 'resolution=128', 'smoothness=0.5%' and 'max_angle=361'.
19478#@cli : $ 300,300 rand -1,1 blur 40 sign normalize 0,255 lathe3d ,
19479lathe3d : check "${1=128}>0 && ${2=0.5%}>=0 && ${3=361}>=0"
19480  e[^-1] "Generate lathed 3D object from XY-profile$?, with resolution $1, smoothness $2 and maximum angle $3 deg."
19481  tmax={($3-180)*pi/180} norm n 0,1 autocrop 0
19482  repeat $! l[$>]
19483    wr={max(1,w2=2*w;if(w2>h,min($1,w2),min($1,h)*w2/h))}
19484    hr={max(1,w2=2*w;if(w2>h,min($1,w2)*h/w2,min($1,h)))}
19485    rmax={sqrt(($wr)^2+($hr)^2)/2}
19486    $wr,1,$wr,1,"xc = x - w/2; zc = z - d/2; t = atan2(zc,xc); if(t>"$tmax","$rmax",sqrt(xc*xc+zc*zc))"
19487    *. {2*({-2,w}-1)/(w-1)} r. $wr,$hr,$wr
19488    (0;{{-2,h}-1}) r. $wr,$hr,$wr,1,3 a[-2--1] c
19489    warp.. .,0,1,0 rm.
19490    expand_xyz 10,0 b $2 isosurface3d 50% rv3d
19491  endl done
19492
19493#@cli l3d : eq. to 'light3d'. : (+)
19494
19495#@cli light3d : position_x,position_y,position_z : [texture] : (no arg) : (+)
19496#@cli : Set the light coordinates or the light texture for 3D rendering.
19497#@cli : (eq. to 'l3d').\n
19498#@cli : (no arg) resets the 3D light to default.
19499#@cli : $ torus3d 100,30 double3d 0 specs3d 1.2 repeat 5 light3d {$>*100},0,-300 +snapshot3d[0] 400 done remove[0]
19500
19501#@cli line3d : x0,y0,z0,x1,y1,z1
19502#@cli : Input 3D line at specified coordinates.
19503#@cli : $ repeat 100 a={$>*pi/50} line3d 0,0,0,{cos(3*$a)},{sin(2*$a)},0 color3d. ${-rgb} done add3d
19504line3d :
19505  e[^-1] "Input 3D line (${1-3})-(${4-6})."
19506  1,21,1,1,67.5,73.5,109.5,103.5,51.5,100.5,2,1,${1-6},2,0,1,200,200,200,1 nm. [3D\ line]
19507
19508#@cli lissajous3d : resolution>1,a,A,b,B,c,C
19509#@cli : Input 3D lissajous curves __(x(t)=sin(a*t+A*2*pi), y(t)=sin(b*t+B*2*pi), z(t)=sin(c*t+C*2*pi))__.
19510#@cli : Default values: 'resolution=1024', 'a=2', 'A=0', 'b=1', 'B=0', 'c=0' and 'C=0'.
19511#@cli : $ lissajous3d ,
19512lissajous3d : check ${1=1024}>1 skip ${2=2},${3=0},${4=1},${5=0},${6=0},${7=0}
19513  e[^-1] "Input 3D lissajous curve, with resolution $1, (a,A)=($2,$3), (b,B)=($4,$5) and (c,C)=($6,$7)."
19514  res={round($1)}
19515
19516  # Define object header and vertices.
19517  (67.5;73.5;109.5;103.5;51.5;100.5;$res;{$res-1})
19518  (0,{2*pi}) r. $res,1,1,1,3 [-1]x2
19519  *... $2 +... {$3*2*pi} *.. $4 +.. {$5*2*pi} *. $6 +. {$7*2*pi}
19520  a[-3--1] y sin. transpose. r. 1,{w*h},1,1,-1
19521
19522  # Define object primitives, colors and opacities.
19523  1,{$res-1},1,1,2 (0;{$res-2}) r. 1,{$res-1},1,1,3 ++. 1 a[-3--1] x round. 1 r. 1,{w*h},1,1,-1
19524  1,{3*($res-1)},1,1,200 1,{$res-1},1,1,1 a[-5--1] y nm. [3D\ lissajou]
19525
19526#@cli m3d : eq. to 'mode3d'. : (+)
19527
19528#@cli mode3d : _mode : (+)
19529#@cli : Set static 3D rendering mode.
19530#@cli : (eq. to 'm3d').\n
19531#@cli : 'mode' can be { -1=bounding-box | 0=dots | 1=wireframe | 2=flat | 3=flat-shaded | 4=gouraud-shaded | \
19532# 5=phong-shaded }.");
19533#@cli : Bounding-box mode ('mode==-1') is active only for the interactive 3D viewer.
19534#@cli : Default value: 'mode=4'.
19535#@cli : $ (0,1,2,3,4,5) double3d 0 repeat w torus3d 100,30 rotate3d[-1] 1,1,0,60 mode3d {0,@$>} \
19536# snapshot3d[-1] 300 done remove[0]
19537
19538#@cli md3d : eq. to 'moded3d'. : (+)
19539
19540#@cli moded3d : _mode : (+)
19541#@cli : Set dynamic 3D rendering mode for interactive 3D viewer.
19542#@cli : (eq. to 'md3d').\n
19543#@cli : 'mode' can be { -1=bounding-box | 0=dots | 1=wireframe | 2=flat | 3=flat-shaded | 4=gouraud-shaded | \
19544# 5=phong-shaded }.
19545#@cli : Default value: 'mode=-1'.
19546
19547#@cli *3d : eq. to 'mul3d'. : (+)
19548
19549#@cli mul3d : factor : factor_x,factor_y,_factor_z : (+)
19550#@cli : Scale selected 3D objects isotropically or anisotropically, with specified factors.
19551#@cli : (eq. to '*3d').
19552#@cli : Default value: 'factor_z=0'.
19553#@cli : $ torus3d 5,2 repeat 5 +add3d[-1] 10,0,0 mul3d[-1] 1.2 color3d[-1] ${-rgb} done add3d
19554
19555#@cli n3d : eq. to 'normalize3d'.
19556n3d :
19557  _normalize3d
19558
19559#@cli normalize3d
19560#@cli : Normalize selected 3D objects to unit size.
19561#@cli : (eq. to 'n3d').
19562#@cli : $ repeat 100 circle3d {u(3)},{u(3)},{u(3)},0.1 done add3d color3d[-1] 255,0,0 +normalize3d[-1] \
19563# color3d[-1] 0,255,0 add3d
19564normalize3d :
19565  _$0
19566
19567_normalize3d :
19568  e[0--3] "Normalize size of 3D object$?."
19569  check3d 0 repeat $! l[$>]
19570    if i[6]
19571      s3d r[2] 3,{2,h/3},1,1,-1 s[2] x
19572      factor={v=max({2,iM-im},{3,iM-im},{4,iM-im});if(v,v,1)}
19573      a[2-4] x /[2] $factor y[2] a y
19574    fi
19575  endl done
19576
19577#@cli o3d : eq. to 'opacity3d'. : (+)
19578
19579#@cli opacity3d : _opacity : (+)
19580#@cli : Set opacity of selected 3D objects.
19581#@cli : (eq. to 'o3d').
19582#@cli : Default value: 'opacity=1'.
19583#@cli : $ torus3d 100,10 double3d 0 repeat 7 +rotate3d[-1] 1,0,0,20 opacity3d[-1] {u} done add3d
19584
19585#@cli parametric3d : _x(a,b),_y(a,b),_z(a,b),_amin,_amax,_bmin,_bmax,_res_a>0,_res_b>0,_res_x>0,_res_y>0,_res_z>0,\
19586# _smoothness>=0,_isovalue>=0
19587#@cli : Input 3D object from specified parametric surface `(a,b) ⟶ (x(a,b),y(a,b),z(a,b))`.
19588#@cli : Default values: 'x=(2+cos(b))*sin(a)', 'y=(2+cos(b))*cos(a)', 'c=sin(b)', 'amin=-pi', 'amax=pi', \
19589# 'bmin=-pi', 'bmax=pi', \
19590# 'res_a=512', 'res_b=res_a', 'res_x=64', 'res_y=res_x', 'res_z=res_y', 'smoothness=2%' and 'isovalue=10%'.
19591#@cli : $ parametric3d ,
19592parametric3d : skip "${1=(2+cos(b))*sin(a)}","${2=(2+cos(b))*cos(a)}","${3=sin(b)}"
19593               skip ${4={-pi}},${5={pi}},${6={-pi}},${7={pi}}
19594               check "${8=512}>0 && ${9=$8}>0 && ${10=64}>0 && ${11=$10}>0 && ${12=$11}>0 && \
19595                       ${13=2%}>=0 && ${14=10%}>=0"
19596  e[^-1] "Input 3D object from parametric surface ($1,$2,$3)."
19597  # Compute (x(a,b),y(a,b),z(a,b)) and normalize it.
19598  ($4,$5;$4,$5^$6,$6;$7,$7) r. $8,$9,1,2,3 channels. 0,2
19599  f. "a=i(x,y,0,0);b=i(x,y,0,1);if(c==0,$1,if(c==1,$2,$3))"
19600  sh. 0 xmin={im} xmax={iM} n. 16,{$10-17} rm.
19601  sh. 1 ymin={im} ymax={iM} n. 16,{$11-17} rm.
19602  sh. 2 zmin={im} zmax={iM} n. 16,{$12-17} rm.
19603  r. {w*h},3,1,1,-1
19604
19605  # Extract 3D surface.
19606  pointcloud. 1 r. $10,$11,$12,1,0 b. $13,0
19607  isosurface3d. $14
19608  c3d. n3d. *3d. {$xmax-$xmin},{$ymax-$ymin},{$zmax-$zmin} nm. [3D\ parametric]
19609
19610#@cli pca_patch3d : _patch_size>0,_M>0,_N>0,_normalize_input={ 0 | 1 },_normalize_output={ 0 | 1 },_lambda_xy
19611#@cli : Get 3D patch-pca representation of selected images.
19612#@cli : The 3D patch-pca is estimated from M patches on the input image, and displayed as a cloud of N 3D points.
19613#@cli : Default values: 'patch_size=7', 'M=1000', 'N=3000', 'normalize_input=1', 'normalize_output=0', \
19614# and 'lambda_xy=0'.
19615#@cli : $ image.jpg pca_patch3d 7
19616pca_patch3d : check "isint(${1=7}) && $1>0 && isint(${2=1000}) && $2>0 && isint(${3=3000}) && $3>0"
19617              skip ${4=1},${5=0},${6=0}
19618  e[^-1] "Get 3D patch-pca representation"${arg\ 1+($!>1),s,""}" of image$?, from $2 $1x$1 input patches,
19619          with $3 output patches, input normalization "${arg\ 1+!$4,enabled,disabled}", output normalization "\
19620          ${arg\ 1+!$5,enabled,disabled}" and lambda_xy $6."
19621  P1={int($1/2)}       # Backward half-patch size.
19622  P2={$1-$P1-1}        # Forward half-patch size.
19623
19624  n 0,255 round 1
19625  repeat $! l[$>] nm={0,n}
19626    s={s}
19627
19628    # Pick set of M random located patches.
19629    1,$2 rand. 0,{0,w-1} +rand. 0,{0,h-1} +f. 0 a[-3--1] x round. 1 +patches[0] $1,$1,1,{^} y[2--1] a[2--1] x
19630    z[1] 0,1 transpose[1] *[1] $6 a[1,2] y s[^0] x
19631
19632    # Normalize patch coordinates by using average and standard deviation.
19633    ++[^0] /. $2 -[1--2] . rm.
19634    a[^0] x
19635    if $4 l. s y / 'sqrt(1e-8+iv)' a y endl fi
19636
19637    # Do PCA for dimension reduction.
19638    +transpose. m*[-2,-1]
19639    eigen. rows.. 0,2 columns. 0,2 transpose.
19640    if $5 sqrt.. /.. {-2,iM} ri.. . /. .. fi
19641    rm..
19642
19643    # Pick set of N random located patches.
19644    repeat $3
19645      x={round(u({0,w}))}
19646      y={round(u({0,h}))}
19647      ({$6*$x};{$6*$y})
19648      +z[0] {$x-$P1},{$y-$P1},{$x+$P2},{$y+$P2},1
19649      y. a[-2,-1] y
19650    done
19651
19652    # Generate 3D representation of the projected patch set.
19653    +a[2--1] x m*[1,-1] transpose[1]                    # Vertex coordinates.
19654    rows[2--1] 2,100%                                   # Colors
19655    if $s!=3
19656      r[2--1] $1,$1,1,{min(3,$s)},-1
19657      r[2--1] $1,$1,1,3,{if($s!=1,0,1)}
19658      y[2--1]
19659    fi
19660    i[2--2] (-128;$1;$1;3) a[2--1] y
19661    rm[0]                                               # Remove input image (now useless).
19662    i[0] ('CImg3d')                                     # Header.
19663    i[1] ($3;$3)                                        # Geometry.
19664    i[3] 2,$3,1,1,if(x==0,1,y)                          # Primitives.
19665    1,$3,1,1,1                                          # Opacities.
19666    y a[-6--1] y                                        # Merge as a 3D object.
19667
19668  nm $nm endl done
19669
19670#@cli plane3d : _size_x,_size_y,_nb_subdivisions_x>0,_nb_subdisivions_y>0
19671#@cli : Input 3D plane at (0,0,0), with specified geometry.
19672#@cli : Default values: 'size_x=1', 'size_y=size_x' and 'nb_subdivisions_x=nb_subdivisions_y=24'.
19673#@cli : $ plane3d 50,30 +primitives3d 1 color3d[-2] ${-rgb}
19674plane3d : check "${3=24}>0 && ${4=24}>0" skip ${1=1},${2=$1}
19675  e[^-1] "Input 3D plane, with size (${1,2}) and subdivisions (${3,4})."
19676  {$3+1},{$4+1} elevation3d. 0 *3d. {$1/$3},{$2/$4} col3d. 200 nm. [3D\ plane]
19677
19678#@cli point3d : x0,y0,z0
19679#@cli : Input 3D point at specified coordinates.
19680#@cli : $ repeat 1000 a={$>*pi/500} point3d {cos(3*$a)},{sin(2*$a)},0 color3d[-1] ${-rgb} done add3d
19681point3d :
19682  e[^-1] "Input 3D point ($1,$2,$3)."
19683  1,17,1,1,67.5,73.5,109.5,103.5,51.5,100.5,1,1,${1-3},1,0,200,200,200,1 nm. [3D\ point]
19684
19685#@cli pointcloud3d
19686#@cli : Convert selected planar or volumetric images to 3D point clouds.
19687#@cli : $ image.jpg luminance resize2dy 100 threshold 50% mul 255 pointcloud3d color3d[-1] 255,255,255
19688pointcloud3d :
19689  e[^-1] "Convert image$? to 3D point cloud."
19690  repeat $! l[$>] nm={0,n}
19691    s z repeat $! l[$>]
19692      +norm !=. 0
19693      i.. (1,{w};1,{w}^1,1;{h},{h}) r.. .,.,1,2,3 *[-2,-1] round. permute. cxyz
19694      l. s -,0 a y is_points=$! endl
19695      if $is_points
19696        -. 1 r. 2,{h/2},1,1,-1 permute. cyzx +warp.. .,0,0 rm...
19697        permute.. cyzx i.. 1,{h},1,1,$> a[-3,-2] x   # Coordinates.
19698        i... ('CImg3d') i... ({h},{h})               # Header and size.
19699        i.. 1,{h},1,1,1 i.. 1,{h},1,1,y a[-3,-2] x   # Primitives.
19700        permute. cyzx                                # Colors.
19701        if w==1 r. 3,{h},1,1
19702        elif w>3 i.. 4,{h},1,1,-128,1,1,{w} a[-2,-1] x
19703        else r. 3,{h},1,1,0
19704        fi
19705        1,{h},1,1,1                                      # Opacities.
19706        y[-6--1] a[-6--1] y
19707      else rm empty3d
19708      fi
19709      endl done
19710    +3d
19711  nm $nm endl done
19712
19713#@cli pose3d : p1,...,p12
19714#@cli : Apply 3D pose matrix to selected 3D objects.
19715#@cli : $ torus3d 100,20 pose3d 0.152437,1.20666,-0.546366,0,-0.535962,0.559129,1.08531,0,1.21132,0.0955431,\
19716# 0.548966,0,0,0,-206,1 snapshot3d 400
19717pose3d :
19718  e[^-1] "Apply 3D pose matrix [ $1,$2,$3,$4; $5,$6,$7,$8; $9,$10,$11,$12 ] to 3D object$?."
19719  repeat $! l[$>] if ${-_is_3d}
19720    s3d r[2] 3,{2,h/3},1,1,-1 i[3] 1,{2,h},1,1,1 a[2,3] x
19721    i[3] ($1,$5,$9;$2,$6,$10;$3,$7,$11;$4,$8,$12) m*[2,3]
19722    r[2] 1,{2,3*h},1,1,-1 a y
19723  else error "Command '$0': Image ["{$!-$>-1}"] does not represent a 3D object."
19724  fi endl done
19725
19726#@cli p3d : eq. to 'primitives3d'.
19727p3d : check "isint($1) && $1>=0 && $1<=2"
19728  _primitives3d $*
19729
19730#@cli primitives3d : mode
19731#@cli : Convert primitives of selected 3D objects.
19732#@cli : (eq. to 'p3d').\n
19733#@cli : 'mode' can be { 0=points | 1=outlines | 2=non-textured }.
19734#@cli : $ sphere3d 30 primitives3d 1 torus3d 50,10 color3d[-1] ${-rgb} add3d
19735primitives3d : check "isint($1) && $1>=0 && $1<=2"
19736  _$0 $*
19737
19738_primitives3d :
19739  e[0--3] "Convert primitives of 3D object$? to "${"arg 1+$1,points,outlines,non-textured"}"."
19740  repeat $! l[$>]
19741    s3d 1,64 .x2
19742    eval "
19743      const mode = $1;
19744
19745      # Read a RGBA color from the current primitive material.
19746      get_RGBA(tx,ty,goto_next) = (
19747        R = i[#4,pc];
19748        R!=-128?( # RGB color
19749          G = i[#4,pc+1];
19750          B = i[#4,pc+2];
19751          goto_next?(pc+=3);
19752        ):( # Texture
19753          W = i[#4,pc+1]; H = i[#4,pc+2]; S = i[#4,pc+3]; WH = W*H;
19754          WH?( # Non-shared texture
19755            off = pc + (ty%H)*W + (tx%W) + 4;
19756            goto_next?(pc+=WH*S + 4);
19757          ):( # Shared texture
19758            off = pcol[W];
19759            i[#4,off]==-128?(
19760              ++off;
19761              W = i[#4,off++]; H = i[#4,off++]; S = i[#4,off++]; WH = W*H;
19762              off += (ty%H)*W + (tx%W);
19763            ):(WH=1);
19764            goto_next?(pc+=4);
19765          );
19766          R = i[#4,off];
19767          S==1?(G = B = R):(
19768            G = i[#4,off + WH];
19769            S==2?(B = 0):(
19770              B = i[#4,off + 2*WH];
19771            );
19772          );
19773        );
19774
19775        A = i[#5,po];
19776        A!=-128?( # Scalar
19777          goto_next?(po+=1);
19778        ):( # Texture
19779          W = i[#5,po+1]; H = i[#5,po+2]; S = i[#5,po+3]; WH = W*H;
19780          WH?( # Non-shared texture
19781            off = po + (ty%H)*W + (tx%W) + 4;
19782            goto_next?(po+=WH*S + 4);
19783          ):( # Shared texture
19784            off = popa[W];
19785            i[#5,off]==-128?(
19786              ++off;
19787              W = i[#5,off++]; H = i[#5,off++]; S = i[#5,off++]; WH = W*H;
19788              off += (ty%H)*W + (tx%W);
19789            );
19790            goto_next?(po+=4);
19791          );
19792          A = i[#5,off];
19793        );
19794      );
19795
19796      # Copy material of current primitives 'nb' times.
19797      copy_material(nb) = (
19798        R = i[#4,pc++]; G = i[#4,pc++]; B = i[#4,pc++];
19799        R!=-128?( # RGB color
19800          unref(data); data = [ R,G,B ];
19801          repeat (nb, copy(i[#-2,qc],data,3); qc+=3);
19802        ):(
19803          W = G; H = B; S = i[#4,pc++]; WHS = W*H*S;
19804          WHS?( # Non-shared texture
19805            qc + WHS + 4*nb>=h(#-2)?resize(#-2,1,int(1.5*qc + WHS + 4*nb),1,1,0,0);
19806            copy(i[#-2,qc],i[#4,pc-4],WHS+4);
19807            pc+=WHS; qc+=WHS+4;
19808            unref(data); data = [-128,nq,0,0];
19809            for (k = 1, k<nb, ++k, copy(i[#-2,qc],data,4); qc+=4);
19810          ):( # Shared texture
19811            unref(data); data = [-128,pref[W],0,0];
19812            repeat (nb, copy(i[#-2,qc],data,4); qc+=4);
19813          );
19814        );
19815
19816        A = i[#5,po++];
19817        A!=-128?( # Opacity value
19818          repeat (nb, i[#-1,qo++] = A);
19819        ):( # Texture
19820          W = i[#5,po++]; H = i[#5,po++]; S = i[#5,po++]; WHS = W*H*S;
19821          WHS?( # Non-shared texture
19822            qo + WHS + 4*nb>=h(#-1)?resize(#-1,1,int(1.5*qo + WHS + 4*nb),1,1,0,0);
19823            copy(i[#-1,qo],i[#5,po-4],WHS+4);
19824            po+=WHS; qo+=WHS+4;
19825            unref(data); data = [-128,nq,0,0];
19826            for (k = 1, k<nb, ++k, copy(i[#-1,qo],data,4); qo+=4);
19827          ):( # Shared texture
19828            unref(data); data = [ -128,pref[W],0,0 ];
19829            repeat (nb, copy(i[#-1,qo],data,4); qo+=4);
19830          );
19831        );
19832      );
19833
19834      # Add a new colored point primitive.
19835      add_point(ind,R,G,B,A) = (
19836        copy(i[#-3,qp],[1,ind],2); qp+=2;
19837        copy(i[#-2,qc],[R,G,B],3); qc+=3;
19838        i[#-1,qo++] = A;
19839        ++nq;
19840      );
19841
19842      # Add a new colored segment primitive.
19843      add_segment(ind0,ind1,R,G,B,A) = (
19844        copy(i[#-3,qp],[2,ind0,ind1],3); qp+=3;
19845        copy(i[#-2,qc],[R,G,B],3); qc+=3;
19846        i[#-1,qo++] = A;
19847        ++nq;
19848      );
19849
19850      pcol = popa = pref = vector"{i[#1,1]}"();
19851      for (pp = pc = po = qp = qc = qo = np = nq = 0, pp<h#3, ++np,
19852        qp + 28>=h(#-3)?resize(#-3,1,int(1.5*qp + 28),1,1,0,0);
19853        qc + 16>=h(#-2)?resize(#-2,1,int(1.5*qc + 16),1,1,0,0);
19854        qo + 4>=h(#-1)?resize(#-1,1,int(1.5*qo + 4),1,1,0,0);
19855        N = i[#3,pp++];
19856        pcol[np] = pc;
19857        popa[np] = po;
19858        pref[np] = nq;
19859
19860        N==1?( # Colored point
19861          v0 = i[#3,pp++];
19862          get_RGBA(0,0,1);
19863          add_point(v0,R,G,B,A);
19864
19865        ):N==2?( # Colored segment
19866          v0 = i[#3,pp++]; v1 = i[#3,pp++];
19867          get_RGBA(0,0,1);
19868          mode==0?(
19869            add_point(v0,R,G,B,A);
19870            add_point(v1,R,G,B,A);
19871          ):(
19872            add_segment(v0,v1,R,G,B,A);
19873          );
19874
19875        ):N==3?( # Colored triangle
19876          v0 = i[#3,pp++]; v1 = i[#3,pp++]; v2 = i[#3,pp++];
19877          get_RGBA(0,0,1);
19878          mode==0?(
19879            add_point(v0,R,G,B,A);
19880            add_point(v1,R,G,B,A);
19881            add_point(v2,R,G,B,A);
19882          ):mode==1?(
19883            add_segment(v0,v1,R,G,B,A);
19884            add_segment(v1,v2,R,G,B,A);
19885            add_segment(v2,v0,R,G,B,A);
19886          ):(
19887            copy(i[#-3,qp],i[#3,pp-4],4); qp+=4;
19888            copy(i[#-2,qc],i[#4,pc-3],3); qc+=3;
19889            i[#-1,qo++] = A;
19890            ++nq;
19891          );
19892
19893        ):N==4?( # Colored quadrangle
19894          v0 = i[#3,pp++]; v1 = i[#3,pp++]; v2 = i[#3,pp++]; v3 = i[#3,pp++];
19895          get_RGBA(0,0,1);
19896          mode==0?(
19897            add_point(v0,R,G,B,A);
19898            add_point(v1,R,G,B,A);
19899            add_point(v2,R,G,B,A);
19900            add_point(v3,R,G,B,A);
19901          ):mode==1?(
19902            add_segment(v0,v1,R,G,B,A);
19903            add_segment(v1,v2,R,G,B,A);
19904            add_segment(v2,v3,R,G,B,A);
19905            add_segment(v3,v0,R,G,B,A);
19906          ):(
19907            copy(i[#-3,qp],i[#3,pp-5],5); qp+=5;
19908            copy(i[#-2,qc],i[#4,pc-3],3); qc+=3;
19909            i[#-1,qo++] = A;
19910            ++nq;
19911          );
19912
19913        ):N==5?( # Colored sphere
19914          v0 = i[#3,pp++]; v1 = i[#3,pp++]; i[#3,pp++] = (mode==1); v2 = i[#3,pp++]; v3 = i[#3,pp++];
19915          get_RGBA(0,0,1);
19916          mode==0?(
19917            x0 = i[#2,3*v0]; y0 = i[#2,3*v0+1]; z0 = i[#2,3*v0+2];
19918            x1 = i[#2,3*v1]; y1 = i[#2,3*v1+1]; z1 = i[#2,3*v1+2];
19919            copy(i[#2,3*v0],([ x0,y0,z0 ] + [ x1,y1,z1 ])/2,3);
19920            add_point(v0,R,G,B,A);
19921          ):(
19922            copy(i[#-3,qp],i[#3,pp-6],6); qp+=6;
19923            copy(i[#-2,qc],i[#4,pc-3],3); qc+=3;
19924            i[#-1,qo++] = A;
19925            ++nq;
19926          );
19927
19928        ):N==6?( # Textured segment
19929          v0 = i[#3,pp++]; v1 = i[#3,pp++];
19930          tx0 = i[#3,pp++]; ty0 = i[#3,pp++]; tx1 = i[#3,pp++]; ty1 = i[#3,pp++];
19931          mode==0?(
19932            get_RGBA(tx0,ty0,0); add_point(v0,R,G,B,A);
19933            get_RGBA(tx1,ty1,1); add_point(v1,R,G,B,A);
19934          ):mode==1?(
19935            copy(i[#-3,qp],i[#3,pp-7],7); qp+=7;
19936            copy_material(1);
19937            ++nq;
19938          ):(
19939            get_RGBA(tx0,ty0,0); R0 = R; G0 = G; B0 = B; A0 = A;
19940            get_RGBA(tx1,ty1,1); (R0+=R)/=2; (G0+=G)/=2; (B0+=B)/=2; (A0+=A)/=2;
19941            add_segment(v0,v1,R0,G0,B0,A0);
19942          );
19943
19944        ):N==9?( # Textured triangle
19945          v0 = i[#3,pp++]; v1 = i[#3,pp++]; v2 = i[#3,pp++];
19946          tx0 = i[#3,pp++]; ty0 = i[#3,pp++];
19947          tx1 = i[#3,pp++]; ty1 = i[#3,pp++];
19948          tx2 = i[#3,pp++]; ty2 = i[#3,pp++];
19949          mode==0?(
19950            get_RGBA(tx0,ty0,0); add_point(v0,R,G,B,A);
19951            get_RGBA(tx1,ty1,0); add_point(v1,R,G,B,A);
19952            get_RGBA(tx2,ty2,1); add_point(v2,R,G,B,A);
19953          ):mode==1?(
19954            copy(i[#-3,qp],[ 6,v0,v1,tx0,ty0,tx1,ty1,
19955                             6,v1,v2,tx1,ty1,tx2,ty2,
19956                             6,v2,v0,tx2,ty2,tx0,ty0 ],21); qp+=21;
19957            copy_material(3);
19958            nq+=3;
19959          ):(
19960            get_RGBA(tx0,ty0,0); R0 = R; G0 = G; B0 = B; A0 = A;
19961            get_RGBA(tx1,ty1,0); R0+=R; G0+=G; B0+=B; A0+=A;
19962            get_RGBA(tx2,ty2,2); (R0+=R)/=3; (G0+=G)/=3; (B0+=B)/=3; (A0+=A)/=3;
19963            copy(i[#-3,qp],[ 3,v0,v1,v2 ],4); qp+=4;
19964            copy(i[#-2,qc],[ R,G,B ],3); qc+=3;
19965            i[#-1,qo++] = A;
19966            ++nq;
19967          );
19968
19969        ):N==12?( # Textured quadrangle
19970          v0 = i[#3,pp++]; v1 = i[#3,pp++]; v2 = i[#3,pp++]; v3 = i[#3,pp++];
19971          tx0 = i[#3,pp++]; ty0 = i[#3,pp++];
19972          tx1 = i[#3,pp++]; ty1 = i[#3,pp++];
19973          tx2 = i[#3,pp++]; ty2 = i[#3,pp++];
19974          tx3 = i[#3,pp++]; ty3 = i[#3,pp++];
19975          mode==0?(
19976            get_RGBA(tx0,ty0,0); add_point(v0,R,G,B,A);
19977            get_RGBA(tx1,ty1,0); add_point(v1,R,G,B,A);
19978            get_RGBA(tx2,ty2,0); add_point(v2,R,G,B,A);
19979            get_RGBA(tx3,ty3,1); add_point(v3,R,G,B,A);
19980          ):mode==1?(
19981            copy(i[#-3,qp],[ 6,v0,v1,tx0,ty0,tx1,ty1,
19982                             6,v1,v2,tx1,ty1,tx2,ty2,
19983                             6,v2,v3,tx2,ty2,tx3,ty3,
19984                             6,v3,v0,tx3,ty3,tx0,ty0 ],28); qp+=28;
19985            copy_material(4);
19986            nq+=4;
19987          ):(
19988            get_RGBA(tx0,ty0,0); R0 = R; G0 = G; B0 = B; A0 = A;
19989            get_RGBA(tx1,ty1,0); R0+=R; G0+=G; B0+=B; A0+=A;
19990            get_RGBA(tx2,ty2,0); R0+=R; G0+=G; B0+=B; A0+=A;
19991            get_RGBA(tx3,ty3,2); (R0+=R)/=4; (G0+=G)/=4; (B0+=B)/=4; (A0+=A)/=4;
19992            copy(i[#-3,qp],[ 4,v0,v1,v2,v3 ],5); qp+=5;
19993            copy(i[#-2,qc],[ R,G,B ],3); qc+=3;
19994            i[#-1,qo++] = A;
19995            ++nq;
19996          );
19997        );
19998      );
19999      resize(#-3,1,qp,1,1,0,0);
20000      resize(#-2,1,qc,1,1,0,0);
20001      resize(#-1,1,qo,1,1,0,0);
20002      i[#1,1] = nq"
20003
20004    rm[3-5] a y
20005  endl done
20006
20007#@cli projections3d : _x[%],_y[%],_z[%],_is_bounding_box={ 0 | 1 }
20008#@cli : Generate 3D xy,xz,yz projection planes from specified volumetric images.
20009projections3d : skip ${1=50%},${2=50%},${3=50%},${4=1}
20010  e[^-1] "Generate 3D xy,xz,yz projection planes from image$?."
20011  n 0,255 repeat $! l[$>]
20012    w={w} h={h} d={d}
20013    x={if(${is_percent\ $1},$1*w,$1)}
20014    y={if(${is_percent\ $2},$2*h,$2)}
20015    z={if(${is_percent\ $3},$3*d,$3)}
20016    +rows $2,$2 r. {w},{d},1,100%,-1
20017    +columns.. $1,$1 permute. zyxc
20018    slices... $3,$3 r[-3--1] 100%,100%,1,3
20019    imageplane3d[-3--1]
20020    r3d. 0,1,0,-90 r3d.. 1,0,0,90
20021    +3d... 0,0,$z +3d.. 0,$y,0 +3d. $x,0,0
20022    +3d[-3--1] o3d. 0.8
20023    if $4 box3d $w,$h,$d p3d. 1 o3d. 0.4 +3d[-2,-1] fi
20024  endl done
20025
20026#@cli pyramid3d : width,height
20027#@cli : Input 3D pyramid at (0,0,0), with specified geometry.
20028#@cli : $ pyramid3d 100,-100 +primitives3d 1 color3d[-2] ${-rgb}
20029pyramid3d :
20030  e[^-1] "Input new 3D pyramid, with width $1 and height $2."
20031  (67.5;73.5;109.5;103.5;51.5;100.5;\  # Magick number for CImg3d.
20032   5;5;\                               # Number of vertices and primitives.
20033   {-$1/2};{-$1/2};{-$2/2};\           # Vertex coordinates.
20034   {$1/2};{-$1/2};{-$2/2};\
20035   {$1/2};{$1/2};{-$2/2};\
20036   {-$1/2};{$1/2};{-$2/2};\
20037   0;0;{$2/2};\
20038   4;0;3;2;1;\                         # Primitives description.
20039   3;0;4;3;\
20040   3;1;4;0;\
20041   3;2;4;1;\
20042   3;3;4;2)
20043  1,15,1,1,200 1,5,1,1,1 a[-3--1] y nm. [3D\ pyramid]
20044
20045#@cli quadrangle3d : x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3
20046#@cli : Input 3D quadrangle at specified coordinates.
20047#@cli : $ quadrangle3d -10,-10,10,10,-10,10,10,10,10,-10,10,10 repeat 10 +rotate3d[-1] 0,1,0,30 \
20048# color3d[-1] ${-rgb},0.6 done add3d mode3d 2
20049quadrangle3d :
20050  e[^-1] "Input 3D quadrangle ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)-($10,$11,$12)."
20051  1,29,1,1,67.5,73.5,109.5,103.5,51.5,100.5,4,1,${1-12},4,0,1,2,3,200,200,200,1 nm. [3D\ quadrangle]
20052
20053#@cli random3d : nb_points>=0
20054#@cli : Input random 3D point cloud in [0,1]^3.
20055#@cli : $ random3d 100 circles3d 0.1 opacity3d 0.5
20056random3d : check "$1>=0"
20057  e[^-1] "Input random 3D point cloud, with $1 points."
20058  if $1<0.5 empty3d
20059  else l[]
20060    N={round($1)}
20061    ({'CImg3d'},$N,$N)
20062    3,$N rand. 0,1
20063    1,$N,1,1,1 1,$N,1,1,y a[-2,-1] x
20064    3,$N,1,1,200 1,$N,1,1,1
20065    y a y
20066  endl fi
20067  nm. [3D\ random\ pointcloud]
20068
20069#@cli rv3d : eq. to 'reverse3d'. : (+)
20070
20071#@cli reverse3d : (+)
20072#@cli : Reverse primitive orientations of selected 3D objects.
20073#@cli : (eq. to 'rv3d').
20074#@cli : $ torus3d 100,40 double3d 0 +reverse3d
20075
20076#@cli r3d : eq. to 'rotate3d'. : (+)
20077
20078#@cli rotate3d : u,v,w,angle : (+)
20079#@cli : Rotate selected 3D objects around specified axis with specified angle (in deg.).
20080#@cli : (eq. to 'r3d').
20081#@cli : $ torus3d 100,10 double3d 0 repeat 7 +rotate3d[-1] 1,0,0,20 done add3d
20082
20083#@cli rotation3d : u,v,w,angle
20084#@cli : Input 3x3 rotation matrix with specified axis and angle (in deg).
20085#@cli : $ rotation3d 1,0,0,0 rotation3d 1,0,0,90 rotation3d 1,0,0,180
20086rotation3d :
20087  e[^-1] "Input 3D rotation matrix around axis ($1,$2,$3) with angle $4 deg."
20088  3,3,1,1,{"rot(${1-4}°)"} nm. [3D\ rotation]
20089
20090#@cli sierpinski3d : _recursion_level>=0,_width,_height
20091#@cli : Input 3d Sierpinski pyramid.
20092#@cli : $ sierpinski3d 3,100,-100 +primitives3d 1 color3d[-2] ${-rgb}
20093sierpinski3d : check ${1=4}>=0 skip ${2=1},${3=1}
20094-e[^-1] "Input 3D Sierpinski pyramid of degree $1, with width $2 and height $3."
20095  l[]
20096    _sierpinski3d {-$2/2},{-$2/2},{-$3/2},{$2/2},{-$2/2},{-$3/2},\
20097                  {$2/2},{$2/2},{-$3/2},{-$2/2},{$2/2},{-$3/2},\
20098                  0,0,{$3/2},$1
20099  +3d endl
20100  nm. [3D\ sierpinski]
20101
20102_sierpinski3d :
20103  if $16<=0
20104    (67.5;73.5;109.5;103.5;51.5;100.5;\
20105     5;5;\
20106     $1;$2;$3;\
20107     $4;$5;$6;\
20108     $7;$8;$9;\
20109     $10;$11;$12;\
20110     $13;$14;$15;\
20111     4;0;3;2;1;\
20112     3;0;4;3;\
20113     3;1;4;0;\
20114     3;2;4;1;\
20115     3;3;4;2)
20116    1,15,1,1,200 1,5,1,1,1 a[-3--1] y
20117  return fi
20118  _sierpinski3d $1,$2,$3,\
20119                 {($1+$4)/2},{($2+$5)/2},{($3+$6)/2},\
20120                 {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\
20121                 {($1+$10)/2},{($2+$11)/2},{($3+$12)/2},\
20122                 {($1+$13)/2},{($2+$14)/2},{($3+$15)/2},\
20123                 {$16-1}
20124  _sierpinski3d {($1+$4)/2},{($2+$5)/2},{($3+$6)/2},\
20125                 $4,$5,$6,\
20126                 {($4+$7)/2},{($5+$8)/2},{($6+$9)/2},\
20127                 {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\
20128                 {($4+$13)/2},{($5+$14)/2},{($6+$15)/2},\
20129                 {$16-1}
20130  _sierpinski3d {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\
20131                 {($4+$7)/2},{($5+$8)/2},{($6+$9)/2},\
20132                 $7,$8,$9,\
20133                 {($7+$10)/2},{($8+$11)/2},{($9+$12)/2},\
20134                 {($7+$13)/2},{($8+$14)/2},{($9+$15)/2},\
20135                 {$16-1}
20136  _sierpinski3d {($1+$10)/2},{($2+$11)/2},{($3+$12)/2},\
20137                 {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\
20138                 {($7+$10)/2},{($8+$11)/2},{($9+$12)/2},\
20139                 $10,$11,$12,\
20140                 {($10+$13)/2},{($11+$14)/2},{($12+$15)/2},\
20141                 {$16-1}
20142  _sierpinski3d {($1+$13)/2},{($2+$14)/2},{($3+$15)/2},\
20143                 {($4+$13)/2},{($5+$14)/2},{($6+$15)/2},\
20144                 {($7+$13)/2},{($8+$14)/2},{($9+$15)/2},\
20145                 {($10+$13)/2},{($11+$14)/2},{($12+$15)/2},\
20146                 $13,$14,$15,\
20147                 {$16-1}
20148
20149#@cli size3d
20150#@cli : Return bounding box size of the last selected 3D object.
20151size3d :
20152  +rows. 8,{8+3*i[6]} r. 3,{h/3},1,1,-1 s. x,3
20153  u {-3,iM-im},{-2,iM-im},{iM-im}
20154  rm[-3--1]
20155
20156#@cli skeleton3d : _metric,_frame_type={ 0=squares | 1=diamonds | 2=circles | 3=auto },_skeleton_opacity,\
20157# _frame_opacity,_is_frame_wireframe={ 0 | 1 }
20158#@cli : Build 3D skeletal structure object from 2d binary shapes located in selected images.
20159#@cli : 'metric' can be { 0=chebyshev | 1=manhattan | 2=euclidean }.
20160#@cli : Default values: 'metric=2', 'bones_type=3', 'skeleton_opacity=1' and 'frame_opacity=0.1'.
20161#@cli : $ shape_cupid 480 +skeleton3d ,
20162skeleton3d : check "isint(${1=2}) && $1>=0 && $1<=2 && isint(${2=3}) && $2>=0 && $2<=3" skip ${3=1},${4=0.1},${5=1}
20163  e[^-1] "Build 3D skeletal structure object from image$?, with "${arg\ 1+$1,chebyshev,manhattan,euclidean}" metric, "\
20164          ${arg\ 1+$2,squares,diamonds,circles,auto}" bones, skeleton opacity $3 and frame opacity $4 ."
20165  repeat $! l[$>] channels 0
20166
20167    # Construct skeleton representation.
20168    +distance 0,$1
20169    +f. "(i>j(-1)&&i>j(1)) || (i>j(0,-1)&&i>j(0,1)) || (i>j(-1,-1)&&i>j(1,1)) || (i>j(-1,1)&&i>j(1,-1))"
20170    if $3 +slices. -1,0 isosurface3d. 0.5 o3d. $3 col3d. 255,0,0 fi
20171    *[0-2] pointcloud3d[0]
20172
20173    # Construct bones from skeleton.
20174    if $4 l[0] s3d 0
20175      n={1,@0}
20176      if $n
20177        r[2] 3,$n,1,1,-1 r[3] 2,$n,1,1,-1 r[4] 3,$n,1,1,-1
20178
20179        if $2==0" || "($2==3" && "$1==0) # Frame with squares.
20180          =[1] {4*$n}
20181          i[3] [2]x3 +z.. 0,1 z. 0,2 -[2] . +[4] .
20182          s. x *.. -1 a[-3--1] x +[3] . -[5,-1] a[2-5] x
20183          rm[3] 1,$n,1,1,4 +f. 4*y ++. 1 ++. 1 ++. 1 rv[-3,-1] a[-5--1] x mv. 3
20184
20185        elif $2==1" || "($2==3" && "$1==1) # Frame with diamonds.
20186          =[1] {4*$n}
20187          i[3] [2]x3 +z.. 0,0 z. 0,2 -[2] . +[4] .
20188          shift. 1,0 -[3] . +[5,-1] a[2-5] x
20189          rm[3] 1,$n,1,1,4 +f. 4*y ++. 1 ++. 1 ++. 1 rv[-3,-1] a[-5--1] x mv. 3
20190
20191        elif $2==2" || "($2==3" && "$1==2) # Frame with circles.
20192          =[1] {2*$n}
20193          +z[4] 0,0 z. 0,2 ++[2,-1] -[2,-2] a[2,-1] x
20194          rm[3] 1,$n,1,1,5 +f. 2*y ++. 1 3,100% a[-4--1] x mv. 3
20195        fi
20196        y a y o3d $4 if $5 p3d 1 fi col3d 200
20197      else rm empty3d fi
20198    endl else rm[0] fi
20199    +3d
20200  endl done
20201
20202#@cli snapshot3d : _size>0,_zoom>=0,_backgroundR,_backgroundG,_backgroundB,_backgroundA : [background_image],zoom>=0
20203#@cli : Take 2d snapshots of selected 3D objects.
20204#@cli : Set 'zoom' to 0 to disable object auto-scaling.
20205#@cli : Default values: 'size=512', 'zoom=1' and '[background_image]=(default)'.
20206#@cli : $ torus3d 100,20 rotate3d 1,1,0,60 snapshot3d 400,1.2,128,64,32
20207#@cli : $ torus3d 100,20 rotate3d 1,1,0,60 sample ? +snapshot3d[0] [1],1.2
20208snapshot3d : check "${2=1}>=0" skip ${1=512},${3=""}
20209  if ${"is_image_arg $1"} # Background image specified.
20210    e[0--3] "Take $1x$1 snapshot$? of 3D object$?, with zoom factor $2 and background image $3."
20211    pass$1 0 to_color.
20212  elif isnum($3) # Background color specified.
20213    e[0--3] "Take $1x$1 snapshot$? of 3D object$?, with zoom factor $2 and background color ${3--1}."
20214    (${3--1}) y. c r. $1,$1 to_color.
20215  else # Default background color.
20216    e[0--3] "Take $1x$1 snapshot$? of 3D object$?, with zoom factor $2 and default background."
20217    1,2,1,3,32,64,32,116,64,96 r. $1,$1,1,3,3
20218  fi
20219  repeat $!-1 . l[$>,-1]
20220    if $2!=0 c3d[0] n3d[0] *3d[0] {3*min(w,h)*$2/4} fi
20221    if s>3 # RGBA rendering.
20222      100%,100%,1,3,-1 j3d. [0],50%,50%,0,1
20223      to_rgba. replace_color. 0,0,-1,-1,-1,255,0,0,0,0 blend[-2,-1] alpha
20224    else # RGB rendering.
20225      j3d[1] [0],50%,50%,0,1
20226    fi
20227   nm[1] {-2,n} rm[0]
20228  endl done rm.
20229
20230#@cli sl3d : eq. to 'specl3d'. : (+)
20231
20232#@cli specl3d : value>=0 : (+)
20233#@cli : Set lightness of 3D specular light.
20234#@cli : (eq. to 'sl3d').
20235#@cli : Default value: 'value=0.15'.
20236#@cli : $ (0,0.3,0.6,0.9,1.2) repeat w torus3d 100,30 rotate3d[-1] 1,1,0,60 color3d[-1] 255,0,0 specl3d {0,@$>} \
20237# snapshot3d[-1] 400 done remove[0]
20238
20239#@cli ss3d : eq. to 'specs3d'. : (+)
20240
20241#@cli specs3d : value>=0 : (+)
20242#@cli : Set shininess of 3D specular light.
20243#@cli : (eq. to 'ss3d').
20244#@cli : Default value: 'value=0.8'.
20245#@cli : $ (0,0.3,0.6,0.9,1.2) repeat w torus3d 100,30 rotate3d[-1] 1,1,0,60 color3d[-1] 255,0,0 specs3d {0,@$>} \
20246# snapshot3d[-1] 400 done remove[0]
20247
20248#@cli sphere3d : radius,_nb_recursions>=0 : (+)
20249#@cli : Input 3D sphere at (0,0,0), with specified geometry.
20250#@cli : Default value: 'nb_recursions=3'.
20251#@cli : $ sphere3d 100 +primitives3d 1 color3d[-2] ${-rgb}
20252
20253#@cli spherical3d : _nb_azimuth>=3,_nb_zenith>=3,_radius_function(phi,theta)
20254#@cli : Input 3D spherical object at (0,0,0), with specified geometry.
20255#@cli : Default values: 'nb_zenith=nb_azimut=64' and 'radius_function="abs(1+0.5*cos(3*phi)*sin(4*theta))"'.
20256#@cli : $ spherical3d 64 +primitives3d 1
20257spherical3d : check "${1=64}>=3 && ${2=$1}>=3" skip "${3=abs(1+0.5*cos(3*phi)*sin(4*theta))}"
20258  e[^-1] "Input 3D spherical object, with subdivisions ($1,$2) and height function '$3'."
20259  ('CImg3d':y) # Magic number.
20260  n1={round($1)} n2={round($2)}
20261
20262  # Define 3D vertices.
20263  $n1,{$n2-1},1,3,"phi = 2*pi*(x+0.5)/w;\
20264                   theta = -pi/2+pi*(y+0.5)/h;\
20265                   cp = cos(phi);\
20266                   sp = sin(phi);\
20267                   ct = cos(theta);\
20268                   ($3)*if(c==0,ct*cp,if(c==1,ct*sp,sin(theta)))"
20269  r. {w*h},3,1,1,-1 permute. yxzc
20270  i.. (0,0,{phi=0;theta=-pi/2;-$3};0,0,{phi=0;theta=pi/2;$3}) a[-2,-1] y  # Add the two poles.
20271  nbv={h} y.
20272
20273  # Define 3D primitives.
20274  $n1,{$n2-2},1,4,"nx=(x+1)%w;ny=(y+1);2+if(c==0,x+y*"$n1",if(c==1,nx+y*"$n1",if(c==2,nx+ny*"$n1",x+ny*"$n1")))"
20275  r. {w*h},4,1,1,-1 permute. yxzc i.. 1,{h},1,1,4 a[-2,-1] x
20276  2,$n1,1,1,3,0 1,$n1,1,1,'y' ++. 1 %. {h} 2,$n1,1,1,3,1 [-3,-2]  # Connect the two poles.
20277  +[-5,-4] 2 rv[-5,-4] +[-2,-1] {$nbv-$n1} a[-3--1] x a[-4--2] x
20278  nbp={h+{-2,h}+{-3,h}}
20279  y[-3--1] a[-3--1] y
20280
20281  # Define other object information (properties, colors and opacities).
20282  i... ($nbv;$nbp)
20283  1,{3*$nbp},1,1,200 1,$nbp,1,1,1 a[-2,-1] y
20284
20285  # Append as a 3D object.
20286  a[-5--1] y nm. "[3D spherical surface '$3']"
20287
20288#@cli spline3d : x0[%],y0[%],z0[%],u0[%],v0[%],w0[%],x1[%],y1[%],z1[%],u1[%],v1[%],w1[%],_nb_vertices>=2
20289#@cli : Input 3D spline with specified geometry.
20290#@cli : Default values: 'nb_vertices=128'.
20291#@cli : $ repeat 100 spline3d {u},{u},{u},{u},{u},{u},{u},{u},{u},{u},{u},{u},128 color3d[-1] ${-rgb} done \
20292# box3d 1 primitives3d[-1] 1 add3d
20293spline3d : check ${13=128}>=2
20294  e[^-1] "Input new 3D spline from (${1-3}) [${4-6}] to (${7-9}) [${10-12}] with $13 vertices."
20295  ('CImg3d') +. 0.5   # Header.
20296  ($13;{$13-1})       # Nb vertices / primitives.
20297  # Define vertices.
20298  1,$13,1,1,1 (0;1) r. 1,$13,1,1,3 +sqr. +*[-2,-1] a[-4--1] x
20299  +*. '$2,$5,{3*(($8)-($2))-2*($5)-($11)},{($5)+($11)+2*(($2)-($8))}' l. s x + endl
20300  +*.. '$3,$6,{3*(($9)-($3))-2*($6)-($12)},{($6)+($12)+2*(($3)-($9))}' l. s x + endl
20301  *... '$1,$4,{3*(($7)-($1))-2*($4)-($10)},{($4)+($10)+2*(($1)-($7))}' l... s x + endl
20302  a[-3--1] x
20303  1,{$13-1},1,1,2 (0,1;{$13-2},{$13-1}) r. 2,..,1,1,3 round. a[-2,-1] x # Primitives.
20304  1,{3*($13-1)},1,1,200 1,{$13-1},1,1,1 # Colors / opacities.
20305  y[-3,-4,-6] a[-6--1] y
20306
20307#@cli s3d : eq. to 'split3d'. : (+)
20308
20309#@cli split3d : _full_split={ 0 | 1 } : (+)
20310#@cli : Split selected 3D objects into feature vectors :
20311#@cli : * If 'full_split==0', { header, sizes, vertices, primitives, colors, opacities }.
20312#@cli : * If 'full_split==1', { header, sizes, vertices, p0,...,pP, c0,...,cP, o0,...,oP }.
20313#@cli : (eq. to 's3d').\n
20314#@cli : To recreate the 3D object, append all produced images along the y-axis.
20315#@cli : Default value: 'full_split=0'.
20316#@cli : $ box3d 100 +split3d
20317
20318#@cli sprite3d
20319#@cli : Convert selected images as 3D sprites.
20320#@cli : Selected images with alpha channels are managed.
20321#@cli : $ image.jpg sprite3d
20322sprite3d :
20323  e[^-1] "Convert image$? as 3D sprites."
20324  repeat $! l[$>] nm={0,n}
20325    split_opacity
20326    i[0] (67.5;73.5;109.5;103.5;51.5;100.5;1;1;0;0;0;1;0;-128;{w};{h};{0,s}) y[1]
20327    if $!==2 (1) a y
20328    else /. 255 i.. (-128;{w};{h};{s}) y.
20329    fi
20330    a y
20331  nm $nm endl done
20332
20333#@cli sprites3d : [sprite],_sprite_has_alpha_channel={ 0 | 1 }
20334#@cli : Convert selected 3D objects as a sprite cloud.
20335#@cli : Set 'sprite_has_alpha_channel' to 1 to make the last channel of the selected sprite be a transparency mask.
20336#@cli : Default value: 'mask_has_alpha_channel=0'.
20337#@cli : $ torus3d 100,20 image.jpg resize2dy[-1] 64 100%,100% gaussian[-1] 30%,30% *[-1] 255 append[-2,-1] c \
20338# +sprites3d[0] [1],1 display_rgba[-2]
20339sprites3d : check ${is_image_arg\ $1} skip ${2=0}
20340  e[^-1] "Convert image$? as 3D sprites clouds, using sprite $1 ("${"arg {1+!$2},with,without"}" alpha-channel)."
20341  repeat $!
20342    if !{$>,i(0,7)} continue fi # Do nothing if 3D object is empty.
20343    pass$1 0
20344    if !w empty3d rv[$>,-1] nm[$>] {n} rm. continue fi
20345    l[$>,-1]
20346    s3d[0] N={1,@0} =[1] $N,0,1
20347    rm[3-5] i[3] (1,0;1,{$N-1}) r[3] 2,$N,1,1,3 round[3]
20348    if $2 # With alpha-channel.
20349      if s==1 # Only alpha-channel.
20350        i.. 3,$N,1,1,200 /. 255
20351        i.. (-128;{w};{h};1)
20352        if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi
20353      else # Image + alpha.
20354        s. c,-{s-1} /. 255
20355        i... (-128;{w};{h};{-2,s})
20356        if $N>1 i.. 1,{4*($N-1)},1,1,-128,0,0,0 fi
20357        i.. (-128;{w};{h};1)
20358        if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi
20359      fi
20360    else  # Without alpha-channel.
20361      i.. (-128;{w};{h};{s}) y[-3,-1]
20362      if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi
20363      1,$N,1,1,1
20364    fi
20365    y a y
20366  endl done
20367
20368#@cli star3d : _nb_branches>0,0<=_thickness<=1
20369#@cli : Input 3D star at position `(0,0,0)`, with specified geometry.
20370#@cli : Default values: 'nb_branches=5' and 'thickness=0.38'.
20371#@cli : $ star3d , +primitives3d 1 color3d[-2] ${-rgb}
20372star3d : check "${1=5}>0 && ${2=0.38}>=0 && $2<=1"
20373  e[^-1] "Input 3D star, with $1 branches and thickness $2."
20374  N={2*$1} ('CImg3d') +. 0.5 ({$N+1};$N)
20375  ({-pi/2};{3*pi/2}) r. 1,{$N+1},1,1,3 rows. 0,{h-2} +sin. cos.. a[-2,-1] x
20376  (1,1;$2,$2) *[-2,-1] z. 0,2 r. 3,{h+1},1,1,0
20377  (3,$N,1,0;3,$N,$N,{$N-1}) r. 4,$N,1,1,3 round. =. 0,2,100%
20378  3,$N,1,1,200 1,$N,1,1,1 y[-6,-4--2] a[-6--1] y nm. [3D\ star]
20379
20380#@cli streamline3d : x[%],y[%],z[%],_L>=0,_dl>0,_interpolation,_is_backward={ 0 | 1 },_is_oriented={ 0 | 1 } : \
20381# 'formula',x,y,z,_L>=0,_dl>0,_interpolation,_is_backward={ 0 | 1 },_is_oriented={ 0 | 1 } : (+)
20382#@cli : Extract 3D streamlines from selected vector fields or from specified formula.
20383#@cli : 'interpolation' can be { 0=nearest integer | 1=1st-order | 2=2nd-order | 3=4th-order }.
20384#@cli : Default values: 'dl=0.1', 'interpolation=2', 'is_backward=0' and 'is_oriented=0'.
20385#@cli : $ 100,100,100,3 rand -10,10 blur 3 repeat 300 +streamline3d[0] {u(100)},{u(100)},{u(100)},1000,1,1 \
20386# color3d[-1] ${-rgb} done remove[0] box3d 100 primitives3d[-1] 1 add3d
20387
20388#@cli -3d : eq. to 'sub3d'. : (+)
20389
20390#@cli sub3d : tx,_ty,_tz : (+)
20391#@cli : Shift selected 3D objects with the opposite of specified displacement vector.
20392#@cli : (eq. to '3d').
20393#@cli : Default values: 'ty=tz=0'.
20394#@cli : $ sphere3d 10 repeat 5 +sub3d[-1] 10,{u(-10,10)},0 color3d[-1] ${-rgb} done add3d
20395
20396#@cli superformula3d : resolution>1,m>=1,n1,n2,n3
20397#@cli : Input 2D superformula curve as a 3D object.
20398#@cli : Default values: 'resolution=1024', 'm=8', 'n1=1', 'n2=5' and 'n3=8'.
20399#@cli : $ superformula3d ,
20400superformula3d : check "${1=1024}>1 && ${2=8}>=1" skip ${3=1},${4=5},${5=8}
20401  e[^-1] "Input 2D superformula curve, with resolution $1, m=$2 and (n1,n2,n3)=($3,$4,$5)."
20402  res={round($1)}
20403
20404  # Define object header and vertices.
20405  (67.5;73.5;109.5;103.5;51.5;100.5;$res;{$res-1})
20406  (0,{2*pi}) r. $res,1,1,1,3 .
20407
20408  *. {$2/4} +sin. cos.. abs[-2,-1]
20409  ^.. $4 ^. $5 +[-2,-1] ^. {-1/$3}
20410  +sin.. cos... *. .. *[-3,-2] n[-2,-1] -1,1
20411  a[-2,-1] y rows. 0,2 transpose. r. 1,{w*h},1,1,-1
20412
20413  # Define object primitives, colors and opacities.
20414  1,{$res-1},1,1,2 (0;{$res-2}) r. 1,{$res-1},1,1,3 ++. 1 a[-3--1] x round. 1 r. 1,{w*h},1,1,-1
20415  1,{3*($res-1)},1,1,200 1,{$res-1},1,1,1 a[-5--1] y nm. [3D\ superformula]
20416
20417#@cli tensors3d : _radius_factor>=0,_shape={ 0=box | >=N=ellipsoid },_radius_min>=0
20418#@cli : Generate 3D tensor fields from selected images.
20419#@cli : when 'shape'>0, it gives the ellipsoid shape precision.
20420#@cli : Default values: 'radius_factor=1', 'shape=2' and 'radius_min=0.05'.
20421#@cli : $ 6,6,6,9,"U = [x,y,z] - [w,h,d]/2; U/=norm(U); mul(U,U,3) + 0.3*eye(3)" tensors3d 0.8
20422tensors3d : check "${1=1}>=0 && isint(${2=2}) && $2>=0 && ${3=0.05}>=0"
20423  e[^-1] "Generate 3D tensor field(s) from image$?, with radius factor $1, "\
20424          ${"if $2 u ellipsoid else u box fi"}" shape and radius min $3."
20425  repeat $! l[$>]
20426
20427    # Check input image format.
20428    if s==1 100%,100%,100%,6,"[i#0,0,0,i#0,0,i#0]" k.
20429    elif s==3 100%,100%,100%,6,"[R#0,G#0,0,B#0,0,0]" k.
20430    elif s==4 100%,100%,100%,6,"[R#0,G#0,0,A#0,0,0]" k.
20431    elif s==9 100%,100%,100%,6,"I=I#0;[I[0],I[1],I[2],I[4],I[5],I[8]]" k.
20432    fi
20433    if s!=6 error[0--4] "Command '$0': Image '"{n}"' has an invalid size (spectrum="{s}")." fi
20434
20435    # Estimate eigenvalues/eigenvectors.
20436    100%,100%,100%,12,"
20437      T = I(#0);
20438      M = [ T[0], T[1], T[2], T[1], T[3], T[4], T[2], T[4], T[5] ];
20439      eig = eig(M);
20440      if (det(eig[3,9])<0, eig[3]*=-1; eig[4]*=-1; eig[5]*=-1);
20441      eig[0] = max(0,eig[0]);
20442      eig[1] = max(0,eig[1]);
20443      eig[2] = max(0,eig[2]);
20444      eig"
20445    k.
20446
20447    # Create 3D object.
20448    if $2 sphere3d 1,{$2-1} else box3d 1 fi
20449    N,P={[i[6],i[7]]} siz={h} n3d. c3d. .x{0,whd-1}
20450    f[0] "
20451      const N = "$N";
20452      const P = "$P";
20453      const siz = "$siz";
20454      eig = I;
20455      const d = size(eig);
20456      ind = 1 + x + w*y + wh*z;
20457      L = eig[0,3];
20458      if (max(L)==0, # Empty tensor -> do not display
20459        i[#ind,6] = i[#ind,7] = 0;
20460        resize(#ind,1,8,1,1,0);
20461      _(else),
20462        L*=$1;
20463        L[0] = max($3,L[0]);
20464        L[1] = max($3,L[1]);
20465        L[2] = max($3,L[2]);
20466        R = eig[3,9];
20467        anisotropy = sqrt(((L[0] - L[1])^2 + (L[1] - L[2])^2 + (L[2] - L[0])^2)/(2*(L[0]^2 + L[1]^2 + L[2]^2)));
20468
20469        ref(crop(#ind,0,8,0,0,1,3*N,1,1),pts);
20470        pts *= resize(L,size(pts),0,2);
20471        pts = mul(pts,R,3);
20472        pts += resize([x,y,z],size(pts),0,2);
20473        draw(#ind,pts,0,8,0,0,1,size(pts),1,1);
20474
20475        col0 = cut(255*anisotropy*abs([ R[0],R[1],R[2] ]) + (1 - anisotropy)*200,0,255);
20476        col = resize(col0,3*P,0,2);
20477        const off = siz - 4*P;
20478        draw(#ind,col,0,off,0,0,1,size(col),1,1);
20479        0); I"
20480    rm[0] +3d
20481  endl done
20482
20483#@cli text_pointcloud3d : _"text1",_"text2",_smoothness
20484#@cli : Input 3D text pointcloud from the two specified strings.
20485#@cli : Default values: 'text1="text1"', 'text2="text2"' and 'smoothness=1'.
20486#@cli : $ text_pointcloud3d "G'MIC","Rocks!"
20487text_pointcloud3d : skip "${1=text1}","${2=text2}",${3=1}
20488  e[^-1] "Input 3D pointcloud text object from strings '$1' and '$2', with smoothness $3."
20489  0 t. "$1",0,0,53,1,1
20490  0 t. "$2",0,0,53,1,1 mirror. y
20491  autocrop[-2,-1] 0
20492  expand_xy[-2,-1] 2,0 dilate[-2,-1] 2
20493  permute. zyxc r[-2,-1] ${-max_whd} &[-2,-1]
20494
20495  100%,100% rand. 0,{{-2,d}-1} round. ri. .. f. 'if(z==i,1,0)'
20496  distance. 1 +. 1 +f. 1 rv[-2,-1] /[-2,-1] *. ..
20497  +dilate. 0,0,{d} ==[-2,-1] *. ..
20498
20499  1,100%,100% rand. 0,{{-2,w}-1} round. ri. .. f. 'if(x==i,1,0)'
20500  distance. 1 +. 1 +f. 1 rv[-2,-1] /[-2,-1] *. ...
20501  +dilate. 0,0,{d} ==[-2,-1] *[-3,-1]
20502
20503  -|[-2,-1]
20504
20505  b. $3 isosurface3d. 25%
20506  c3d. n3d. nm. "[3D text pointcloud]"
20507
20508#@cli text3d : text,_font_height>0,_depth>0,_smoothness
20509#@cli : Input a 3D text object from specified text.
20510#@cli : Default values: 'font_height=53', 'depth=10' and 'smoothness=1.5'.
20511#@cli : $ text3d "G'MIC as a\n3D logo!"
20512text3d : skip ${2=53},${3=10},${4=1.5}
20513  e[^-1] "Input 3D text object '$1' with size $2, depth $3 and smoothness $4."
20514  0 t. "$1",0,0,$2,1,1 autocrop. 0 r. 100%,100%,$3 expand_xyz. 10,0
20515  b. $4 isosurface3d. 40% rv3d. nm. "[3D text '$1']"
20516
20517#@cli t3d : eq. to 'texturize3d'.
20518t3d : check ${"is_image_arg $1"}" && (!narg(${2=}) || "${"is_image_arg $2"}")"
20519  e[^-1] "Texturize 3D object$? with texture $1"\
20520         ${"if narg($2) u \" and texture coordinates $2\" else u \"\" fi"}"."
20521  pass$1 0 slices. 0 if s==1 to_rgb. else channels. 0,2 fi
20522  if narg($2) pass$2 else 0 fi
20523  v + _texturize3d
20524
20525#@cli texturize3d : [ind_texture],_[ind_coords]
20526#@cli : Texturize selected 3D objects with specified texture and coordinates.
20527#@cli : (eq. to 't3d').\n
20528#@cli : When '[ind_coords]' is omitted, default XY texture projection is performed.
20529#@cli : Default value: 'ind_coords=(undefined)'.
20530#@cli : $ image.jpg torus3d 100,30 texturize3d[-1] [-2] keep[-1]
20531texturize3d : check ${"is_image_arg $1"}" && (!narg(${2=}) || "${"is_image_arg $2"}")"
20532  e[^-1] "Texturize 3D object$? with texture $1"\
20533         ${"if narg($2) u \" and texture coordinates $2\" else u \"\" fi"}"."
20534  pass$1 0 slices. 0 if s==1 to_rgb. else channels. 0,2 fi
20535  if narg($2) pass$2 else 0 fi
20536  v + _$0
20537
20538_texturize3d :
20539  repeat $!-2 l[$>,-2,-1]
20540    np={f2ui(i[7])}
20541    s3d[0]
20542
20543    # Retrieve texture coordinates for each vertex.
20544    if !w
20545      +r[2] 3,{2,round(h/3)},1,1,-1 s. x,3 rm.
20546      n.. 0,{6,w-1} n. 0,{6,h-1} a[-2,-1] x
20547      mv. -2
20548    fi
20549
20550    # Texturize 3D object
20551    1,{5,2*h}
20552    1,{5,3*h}
20553    1,{5,h},1,1,1
20554    eval "
20555      add_material() = (
20556        ind_tex>=0?( # Shared texture
20557          copy(i[#-2,qc],[ -128,ind_tex,0,0 ],4); qc+=4
20558        ):( # Non-shared texture
20559          qc + whds#6 + 4>=h(#-2)?resize(#-2,1,int(1.5*qc + whds#6 + 4),1,1,0,0);
20560          copy(i[#-2,qc],[ -128,w#6,h#6,s#6 ],4); qc+=4;
20561          copy(i[#-2,qc],i(#6),whds#6); qc+=whds#6;
20562          ind_tex = np;
20563        );
20564      );
20565
20566      ind_tex = -1;
20567      for (pp = pc = qp = qc = np = 0, pp<h#3, ++np,
20568        qp + 13>=h(#-3)?resize(#-3,1,int(1.5*qp + 13),1,1,0,0);
20569        qc + 3>=h(#-2)?resize(#-2,1,int(1.5*qc + 3),1,1,0,0);
20570        N = i[#3,pp++];
20571
20572        N==1?( # Colored point
20573          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
20574          R = i(#6,tx0,ty0,0,0); G = i(#6,tx0,ty0,0,1); B = i(#6,tx0,ty0,0,2);
20575          copy(i[#-3,qp],[ 1,v0 ],2); qp+=2;
20576          copy(i[#-2,qc],[ R,G,B ],3); qc+=3;
20577
20578        ):(N==2 || N==6)?( # Colored segment
20579          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
20580          v1 = i[#3,pp++]; tx1 = i(#7,0,v1); ty1 = i(#7,1,v1);
20581          N==6?(pp+=4);
20582          copy(i[#-3,qp],[ 6,v0,v1,tx0,ty0,tx1,ty1 ],7); qp+=7;
20583          add_material();
20584
20585        ):(N==3 || N==9)?( # Colored triangle
20586          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
20587          v1 = i[#3,pp++]; tx1 = i(#7,0,v1); ty1 = i(#7,1,v1);
20588          v2 = i[#3,pp++]; tx2 = i(#7,0,v2); ty2 = i(#7,1,v2);
20589          N==9?(pp+=6);
20590          copy(i[#-3,qp],[ 9,v0,v1,v2,tx0,ty0,tx1,ty1,tx2,ty2 ],10); qp+=10;
20591          add_material();
20592
20593        ):(N==4 || N==12)?( # Colored quadrangle
20594          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
20595          v1 = i[#3,pp++]; tx1 = i(#7,0,v1); ty1 = i(#7,1,v1);
20596          v2 = i[#3,pp++]; tx2 = i(#7,0,v2); ty2 = i(#7,1,v2);
20597          v3 = i[#3,pp++]; tx3 = i(#7,0,v3); ty3 = i(#7,1,v3);
20598          N==12?(pp+=8);
20599          copy(i[#-3,qp],[ 12,v0,v1,v2,v3,tx0,ty0,tx1,ty1,tx2,ty2,tx3,ty3 ],13); qp+=13;
20600          add_material();
20601
20602        ):N==5?( # Colored sphere
20603          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
20604          v1 = i[#3,pp++]; tx1 = i(#7,0,v1); ty1 = i(#7,1,v1);
20605          v2 = i[#3,pp++]; pp+=2;
20606          (tx0+=tx1)/=2; (ty0+=ty1)/=2;
20607          R = i(#6,tx0,ty0,0,0); G = i(#6,tx0,ty0,0,1); B = i(#6,tx0,ty0,0,2);
20608          copy(i[#-3,qp],[ 5,v0,v1,v2,0,0 ],6); qp+=6;
20609          copy(i[#-2,qc],[ R,G,B ],3); qc+=3;
20610
20611        );
20612      );
20613      resize(#-3,1,qp,1,1,0,0);
20614      resize(#-2,1,qc,1,1,0,0)"
20615
20616    rm[3-5] mv[-3--1] 3
20617    if !w rm.. fi
20618    a[0-5] y
20619  endl done rm[-2,-1]
20620
20621#@cli torus3d : _radius1,_radius2,_nb_subdivisions1>2,_nb_subdivisions2>2
20622#@cli : Input 3D torus at (0,0,0), with specified geometry.
20623#@cli : Default values: 'radius1=1', 'radius2=0.3', 'nb_subdivisions1=24' and 'nb_subdivisions2=12'.
20624#@cli : $ torus3d 10,3 +primitives3d 1 color3d[-2] ${-rgb}
20625torus3d : check "${3=24}>2 && ${4=12}>2" skip ${1=1},${2=0.3}
20626  e[^-1] "Input 3D torus, with radii ($1,$2) and subdivisions ($3,$4)."
20627  # Header.
20628  nbp={$3*$4}
20629  1,8,1,1,67.5,73.5,109.5,103.5,51.5,100.5,$nbp,{$4*$3}
20630
20631  # Vertices.
20632  (0;{2*pi}) +y. x
20633  r.. 1,{$3+1},1,1,3 z.. 0,0,0,{$3-1}
20634  r. {$4+1},1,1,1,3 z. 0,{$4-1}
20635  +sin[-2,-1] cos[-4,-3] r[-4--1] $4,$3
20636  *... $2 +... $1 *. $2 *[-4] ... *[-3,-2]
20637  y[-3--1] a[-3--1] x
20638
20639  # Primitives.
20640  1,$3,1,1,'y' *. $4 +shift. 0,-1 $4,1,1,1,'x' +shift. -1 r[-4--1] $4,$3
20641  ++[-4,-1] +.. [-4] +[-5] ... +[-4,-3] y[-4--1] i[-5] 1,{h},1,1,4 a[-5--1] x
20642
20643  # Colors / opacities.
20644  3,{h},1,1,200 1,{h},1,1,1 y[-4--2] a[-5--1] y
20645  nm. [3D\ torus]
20646
20647#@cli triangle3d : x0,y0,z0,x1,y1,z1,x2,y2,z2
20648#@cli : Input 3D triangle at specified coordinates.
20649#@cli : $ repeat 100 a={$>*pi/50} triangle3d 0,0,0,0,0,3,{cos(3*$a)},{sin(2*$a)},0 color3d[-1] ${-rgb} done add3d
20650triangle3d :
20651  e[^-1] "Input 3D triangle ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)."
20652  1,25,1,1,67.5,73.5,109.5,103.5,51.5,100.5,3,1,${1-9},3,0,1,2,200,200,200,1 nm. [3D\ triangle]
20653
20654#@cli volume3d
20655#@cli : Transform selected 3D volumetric images as 3D parallelepipedic objects.
20656#@cli : $ image.jpg animate blur,0,5,30 append z volume3d
20657volume3d :
20658  e[^-1] "Transform image$? as 3D parallelepipedic objects."
20659  repeat $! l[$>]
20660    w={w} h={h} d={d}
20661    +slices[0] 0
20662    +columns[0] 0 permute. zyxc mirror. x
20663    +slices[0] 100% mirror. x
20664    +columns[0] 100% permute. zyxc
20665    +rows[0] 100% permute. xzyc
20666    +rows[0] 0 permute. xzyc mirror. y
20667    rm[0] image6cube3d *3d $w,$h,$d
20668  endl done
20669
20670#@cli weird3d : _resolution>0
20671#@cli : Input 3D weird object at (0,0,0), with specified resolution.
20672#@cli : Default value: 'resolution=32'.
20673#@cli : $ weird3d 48 +primitives3d 1 color3d[-2] ${-rgb}
20674weird3d : skip ${1=32}
20675  e[^-1] "Input 3D weird object, with resolution $1."
20676  isosurface3d '"
20677    T = 1.61803399;
20678    2 - (cos(x + T*y) + cos(x - T*y) + cos(y + T*z) + cos(y - T*z) + cos(z - T*x) + cos(z + T*x))"',\
20679    0,-4.7,-4.7,-4.7,4.7,4.7,4.7,$1,$1,$1
20680  c3d. n3d. nm. [3D\ weird]
20681
20682#-------------------------------
20683#
20684#@cli :: Control Flow
20685#
20686#-------------------------------
20687
20688#@cli ap : eq. to 'apply_parallel'.
20689ap :
20690  _gmic_s="$?" v + _apply_parallel "$*"
20691
20692#@cli apply_parallel : "command"
20693#@cli : Apply specified command on each of the selected images, by parallelizing it for all image of the list.
20694#@cli : (eq. to 'ap').
20695#@cli : $ image.jpg +mirror x +mirror y apply_parallel "blur 3"
20696apply_parallel :
20697  _gmic_s="$?" v + _$0 "$*"
20698
20699_apply_parallel :
20700  e[0--3] "Apply command '$*' on all image"$_gmic_s" in parallel, using "$_cpus" threads."
20701  if $!" && "narg("$*")
20702    m "_ap : repeat $! l[$>] $* if $! k[0] else 0 fi endl done"
20703    N={min($!,$_cpus)}
20704    commands= sep= repeat $N commands=$commands${sep}_ap[$>--1:$N] sep=, done
20705    parallel $commands
20706    um _ap
20707  fi
20708
20709#@cli apc : eq. to 'apply_parallel_channels'.
20710apc :
20711  _gmic_s="$?" v + _apply_parallel_channels "$*"
20712
20713#@cli apply_parallel_channels : "command"
20714#@cli : Apply specified command on each of the selected images, by parallelizing it for all channel
20715#@cli : of the images independently.
20716#@cli : (eq. to 'apc').
20717#@cli : $ image.jpg apply_parallel_channels "blur 3"
20718apply_parallel_channels :
20719  _gmic_s="$?" v + _$0 "$*"
20720
20721_apply_parallel_channels :
20722  e[0--3] "Apply command '$*' on all channels of image"$_gmic_s" in parallel, using "$_cpus" threads."
20723  N=$! repeat $N s$>={$>,s} done s c
20724  ap "$1"
20725  repeat $N a[$>-{$>+${s$>}-1}] c done
20726
20727#@cli apo : eq. to 'apply_parallel_overlap'.
20728apo : check "${2=0}>=0 && isint(${3=0}) && $3>=0"
20729  _gmic_s="$?" v + _apply_parallel_overlap "$1",${2--1}
20730
20731#@cli apply_parallel_overlap : "command",overlap[%],nb_threads={ 0=auto | 1 | 2 | 4 | 8 | 16 }
20732#@cli : Apply specified command on each of the selected images, by parallelizing it on 'nb_threads'
20733#@cli : overlapped sub-images.
20734#@cli : (eq. to 'apo').\n
20735#@cli : 'nb_threads' must be a power of 2.
20736#@cli : Default values: 'overlap=0','nb_threads=0'.
20737#@cli : $ image.jpg +apply_parallel_overlap "smooth 500,0,1",1
20738apply_parallel_overlap : check "${2=0}>=0 && isint(${3=0}) && $3>=0"
20739  _gmic_s="$?" v + _$0 "$1",${2--1}
20740
20741_apply_parallel_overlap : check "${2=0}>=0 && isint(${3=0}) && $3>=0"
20742  N={if($3,max(1,round($3)),$_cpus)} N={2^int(log2(min(16,$N)))}
20743  e[0--3] "Apply parallelized command '$1' on image"$_gmic_s", with overlap $2 and "$N" threads."
20744  __apo_exception=""
20745  m "_check1 : if $!!=1 rm 0 __apo_exception=\"Command 'apply_parallel_overlap': Specified command '$1' changes the
20746     size of the image stack.\" fi"
20747  repeat $! l[$>]
20748    _apply_parallel_overlap$N "$1",$2
20749  endl done
20750  um _check1
20751
20752_apply_parallel_overlap1 :
20753  $1
20754  if narg($__apo_exception) error[0--12] $__apo_exception fi
20755
20756_apply_parallel_overlap2 :
20757  if w>=h
20758    ovx={round(if(${"is_percent $2"},w*$2,$2))} w2={int(w/2)}
20759    +z[0] {$w2-$ovx},100% z[0] 0,{$w2+$ovx-1}
20760    parallel "l[0] $1 _check1 endl","l[1] $1 _check1 endl"
20761    if narg($__apo_exception) error[0--12] $__apo_exception fi
20762    z[0] 0,{0,w-1-$ovx} z[1] $ovx,100% a x
20763  else
20764    ovy={round(if(${"is_percent $2"},h*$2,$2))} h2={int(h/2)}
20765    +rows[0] {$h2-$ovy},100% rows[0] 0,{$h2+$ovy-1}
20766    parallel "l[0] $1 _check1 endl","l[1] $1 _check1 endl"
20767    if narg($__apo_exception) error[0--12] $__apo_exception fi
20768    rows[0] 0,{0,h-1-$ovy} rows[1] $ovy,100% a y
20769  fi
20770
20771_apply_parallel_overlap4 :
20772  if max(w,h)/min(w,h)>=3
20773    _apply_parallel_overlap2 "_apply_parallel_overlap2 \"$1\",$2",$2
20774  else
20775    ovx={round(if(${"is_percent $2"},w*$2,$2))} w2={int(w/2)}
20776    ovy={round(if(${"is_percent $2"},h*$2,$2))} h2={int(h/2)}
20777    +z[0] {$w2-$ovx},0,100%,{$h2+$ovy-1} +z[0] 0,{$h2-$ovy},{$w2+$ovx-1},100%
20778    +z[0] {$w2-$ovx},{$h2-$ovy},100%,100% z[0] 0,0,{$w2+$ovx-1},{$h2+$ovy-1}
20779    parallel "l[0] $1 _check1 endl","l[1] $1 _check1 endl","l[2] $1 _check1 endl","l[3] $1 _check1 endl"
20780    if narg($__apo_exception) error[0--12] $__apo_exception fi
20781    z[0] 0,0,{0,w-1-$ovx},{0,h-1-$ovy} z[1] $ovx,0,100%,{1,h-1-$ovy}
20782    z[2] 0,$ovy,{2,w-1-$ovx},100% z[3] $ovx,$ovy,100%,100%
20783    a[0,1] x a[1,2] x a y
20784  fi
20785
20786_apply_parallel_overlap8 :
20787  _apply_parallel_overlap2 "_apply_parallel_overlap4 \"$1\",$2",$2
20788
20789_apply_parallel_overlap16 :
20790  _apply_parallel_overlap2 "_apply_parallel_overlap8 \"$1\",$2",$2
20791
20792#@cli at : eq. to 'apply_tiles'.
20793at : check "${2=10%}>0 && ${3=10%}>0 && ${4=10%}>0 && ${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 &&
20794            isint(${8=1}) && $8>=0 && $8<=3"
20795  _gmic_s="$?" v + _apply_tiles "$1",${2--1}
20796
20797#@cli apply_tiles : "command",_tile_width[%]>0,_tile_height[%]>0,_tile_depth[%]>0,_overlap_width[%]>=0,\
20798# _overlap_height[%]>=0,_overlap_depth[%]>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
20799#@cli : Apply specified command on each tile (neighborhood) of the selected images, eventually with overlapping tiles.
20800#@cli : (eq. to 'at').
20801#@cli : Default values: 'tile_width=tile_height=tile_depth=10%','overlap_width=overlap_height=overlap_depth=0' \
20802# and 'boundary_conditions=1'.
20803#@cli : $ image.jpg +equalize[0] 256 +apply_tiles[0] "equalize 256",16,16,1,50%,50%
20804apply_tiles : check "${2=10%}>0 && ${3=10%}>0 && ${4=10%}>0 && ${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 &&
20805                     isint(${8=1}) && $8>=0 && $8<=3"
20806  _gmic_s="$?" v + _$0 "$1",${2--1}
20807
20808_apply_tiles :
20809  e[0--3] "Apply command '$1' on $2x$3x$4 tiles of image$?, with overlaps ($5,$6,$7) and "\
20810          ${"arg 1+$8,dirichlet,neumann,periodic,mirror"}" boundary conditions."
20811  repeat $! l[$>]
20812    bw={cut(round(${"is_percent $2"}?w*$2:$2),1,w)}
20813    bh={cut(round(${"is_percent $3"}?h*$3:$3),1,h)}
20814    bd={cut(round(${"is_percent $4"}?d*$4:$4),1,d)}
20815    ow={round(${"is_percent $5"}?$bw*$5:$5)}
20816    oh={round(${"is_percent $6"}?$bh*$6:$6)}
20817    od={round(${"is_percent $7"}?$bd*$7:$7)}
20818    sw={cut($bw-$ow,1,$bw)}
20819    sh={cut($bh-$oh,1,$bh)}
20820    sd={cut($bd-$od,1,$bd)}
20821    100%,100%,100%,{s+1} # Reconstructed image + weights
20822    if $ow>0" || "$oh>0" || "$od>0 l[] # Generate gaussian weight in case of overlap
20823      $bw,1,1 1,$bh,1 1,1,$bd
20824      = 1,50%,50%,50% distance 1
20825      /[0] {0.3*$bw} /[1] {0.3*$bh} /[2] {0.3*$bd}
20826      sqr * -1 exp r $bw,$bh,$bd,1 *
20827    endl else $bw,$bh,$bd,1,1
20828    fi
20829    $bw,$bh,$bd,[0]
20830    m "__at : $1 k. r "$bw,$bh,$bd,{0,s},0
20831    eval "
20832      ref(crop(#2),mask);
20833      for (z = 0, z<d#0, z+="$sd",
20834        for (y = 0, y<h#0, y+="$sh",
20835          for (x = 0, x<w#0, x+="$sw",
20836            draw(crop(#0,x,y,z,w,h,d,$8),0,0,0,0,w,h,d);
20837            run('__at.');
20838            breakpoint();
20839            draw(#1,crop(#-1),x,y,z,0,w,h,d,s#0,-1,mask);
20840            draw(#1,mask,x,y,z,s#0,w,h,d,1,-1);
20841          )
20842        )
20843      )"
20844    rm[-2,-1]
20845    s. c,-{0,s} /[-2,-1] k.
20846    um __at
20847  endl done
20848
20849#@cli apply_timeout : "command",_timeout={ 0=no timeout | >0=with specified timeout (in seconds) }
20850#@cli : Apply a command with a timeout.
20851#@cli : Set variable '$_is_timeout' to '1' if timeout occurred, '0' otherwise.
20852#@cli : Default value: 'timeout=20'.
20853apply_timeout : check "${2=20}>=0"
20854  if $2<=0
20855    e[0--3] "Apply command '$1' on image$?, with no timeout."
20856    $1
20857    _is_timeout=0
20858  else
20859    e[0--3] "Apply command '$1' on image$?, with a timeout of $2 seconds."
20860    l[] ('$/') id={is} rm endl
20861    l
20862      +store initial
20863      __done$id=0 __is_timeout$id=0
20864      parallel "$1 __done"$id"=1",\
20865               "l[] do if $|-"$|">$2 __is_timeout"$id"=1 error \"\" elif $__done"$id" break fi wait 100 while 1 endl"
20866    onfail
20867      rm $initial
20868      _is_timeout=0
20869      if ${__is_timeout$id} _is_timeout=1 error[0--5] "Command '$0': Time out ($2 seconds) for command '$1'."
20870      else error[0--5] "Command '$0': "${}
20871      fi
20872    endl
20873  fi
20874
20875#@cli check : condition : (+)
20876#@cli : Evaluate specified condition and display an error message if evaluated to false.
20877#@cli : If 'expression' is not a math expression, it is regarded as a filename and checked if it exists.
20878
20879#@cli check3d : _is_full_check={ 0 | 1 } : (+)
20880#@cli : Check validity of selected 3D vector objects, and display an error message
20881#@cli : if one of the selected images is not a valid 3D vector object.
20882#@cli : Full 3D object check is slower but more precise.
20883#@cli : Default value: 'is_full_check=1'.
20884
20885# check_display : calling_command_name
20886# Check if a display is available, and throw an error otherwise.
20887check_display : skip "${1=check_display}"
20888  if !{*,u} error[0--3] "Command '$1': No display available." fi
20889
20890# check_opencv : calling_command_name
20891# Check is OpenCV features are available, and throw an error otherwise.
20892check_opencv : skip "${1=check_opencv}"
20893  l[] check_opencv.mp4,0,0,1 rm
20894  onfail if find(['${}'],'-Dcimg_use_opencv')>0
20895    error[0--4] "Command '$1': No OpenCV features available. "\
20896                "Your G'MIC interpreter has not been compiled with OpenCV support."
20897  fi rm endl
20898
20899#@cli continue : (+)
20900#@cli : Go to end of current 'repeat...done', 'do...while' or 'local...endlocal' block.
20901#@cli : $ image.jpg repeat 10 blur 1 if 1==1 continue fi deform 10 done
20902
20903#@cli break : (+)
20904#@cli : Break current 'repeat...done', 'do...while' or 'local...endlocal' block.
20905#@cli : $ image.jpg repeat 10 blur 1 if 1==1 break fi deform 10 done
20906
20907#@cli do : (+)
20908#@cli : Start a 'do...while' block.
20909#@cli : $ image.jpg luminance i={ia+2} do set 255,{u(100)}%,{u(100)}% while ia<$i
20910
20911#@cli done : (+)
20912#@cli : End a 'repeat/for...done' block, and go to associated 'repeat/for' position, if iterations remain.
20913
20914#@cli elif : condition : (+)
20915#@cli : Start a 'elif...[else]...fi' block if previous 'if' was not verified
20916#@cli : and test if specified condition holds
20917#@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0=false | other=true }..
20918#@cli : $$ https://gmic.eu/tutorial/iffi
20919
20920#@cli else : (+)
20921#@cli : Execute following commands if previous 'if' or 'elif' conditions failed.
20922#@cli : $$ https://gmic.eu/tutorial/iffi
20923
20924#@cli fi : (+)
20925#@cli : End a 'if...[elif]...[else]...fi' block.
20926#@cli : (eq. to 'fi').\n
20927#@cli : $$ https://gmic.eu/tutorial/iffi
20928
20929#@cli endl : eq. to 'endlocal'. : (+)
20930
20931#@cli endlocal : (+)
20932#@cli : End a 'local...endlocal' block.
20933#@cli : (eq. to 'endl').
20934
20935#@cli error : message : (+)
20936#@cli : Print specified error message on the standard error (stderr) and exit interpreter, except
20937#@cli : if error is caught by a 'onfail' command.
20938#@cli : Command selection (if any) stands for displayed call stack subset instead of image indices.
20939
20940#@cli eval : expression : (+)
20941#@cli : Evaluate specified math expression.
20942#@cli : - If no command selection is specified, the expression is evaluated once and its result is set to status.
20943#@cli : - If command selection is specified, the evaluation is looped over selected images. Status is not modified.
20944#@cli :   (in this latter case, 'eval' is similar to 'fill' without assigning the image values).
20945
20946#@cli x : eq. to 'exec'. : (+)
20947
20948#@cli exec : _is_verbose={ 0 | 1 },"command" : (+)
20949#@cli : Execute external command using a system call.
20950#@cli : The status value is then set to the error code returned by the system call.
20951#@cli : If 'is_verbose=1', the executed command is allowed to output on stdout/stderr.
20952#@cli : (eq. to 'x').
20953#@cli : Default value: 'is_verbose=1'.
20954
20955#@cli xo : eq. to 'exec_out'.
20956xo :
20957  v + _exec_out $"*"
20958
20959#@cli exec_out : _mode,"command"
20960#@cli : Execute external command using a system call, and return resulting `stdout` and/or `stderr`.
20961#@cli : 'mode' can be { 0=stdout | 1=stderr | 2=stdout+stderr }.
20962exec_out :
20963  v + _exec_out $"*"
20964
20965_exec_out :
20966  l[]
20967    if "isint($1) && isin($1,0,1,2)" mode=$1 command="${2--1}"
20968    else mode=0 command="$*"
20969    fi
20970  onfail mode=0 command="$*" endl
20971  s0,s1,s2=stdout,stderr,stdout+stderr
20972  e[0--3] "Execute external command '"$command"', and return "${s$mode}" output."
20973  file_rand filename=${}
20974  if $mode==0 x $command" > "$filename
20975  elif $mode==1 x $command" 2> "$filename
20976  else x $command"  >"$filename" 2>&1"
20977  fi
20978  it $filename u {t} rm. delete $filename
20979
20980#@cli for : condition : (+)
20981#@cli : Start a 'for...done' block.
20982#@cli : $ image.jpg resize2dy 32 400,400,1,3 x=0 for $x<400 image[1] [0],$x,$x x+=40 done
20983
20984#@cli if : condition : (+)
20985#@cli : Start a 'if...[elif]...[else]...fi' block and test if specified condition holds.
20986#@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0=false | other=true }.
20987#@cli : $ image.jpg if ia<64 add 50% elif ia<128 add 25% elif ia<192 sub 25% else sub 50% fi cut 0,255
20988#@cli : $$ https://gmic.eu/tutorial/iffi
20989
20990#@cli l : eq. to 'local'. : (+)
20991
20992#@cli local : (+)
20993#@cli : Start a 'local...[onfail]...endlocal' block, with selected images.
20994#@cli : (eq. to 'l').
20995#@cli : $ image.jpg local[] 300,300,1,3 rand[0] 0,255 blur 4 sharpen 1000 endlocal
20996#@cli : $ image.jpg +local repeat 3 deform 20 done endlocal
20997#@cli : $$ https://gmic.eu/oldtutorial/_local
20998
20999#@cli mutex : index,_action={ 0=unlock | 1=lock } : (+)
21000#@cli : Lock or unlock specified mutex for multi-threaded programming.
21001#@cli : A locked mutex can be unlocked only by the same thread. All mutexes are unlocked by default.
21002#@cli : 'index' designates the mutex index, in [0,255].
21003#@cli : Default value: 'action=1'.
21004
21005#@cli noarg : (+)
21006#@cli : Used in a custom command, 'noarg' tells the command that its argument list have not been used
21007#@cli : finally, and so they must be evaluated next in the G'MIC pipeline, just as if the custom
21008#@cli : command takes no arguments at all.
21009#@cli : Use this command to write a custom command which can decide if it takes arguments or not.
21010
21011#@cli onfail : (+)
21012#@cli : Execute following commands when an error is encountered in the body of the 'local...endlocal' block.
21013#@cli : The status value is set with the corresponding error message.
21014#@cli : $ image.jpg +local blur -3 onfail mirror x endlocal
21015
21016#@cli parallel : _wait_threads,"command1","command2",... : (+)
21017#@cli : Execute specified commands in parallel, each in a different thread.
21018#@cli : Parallel threads share the list of images.
21019#@cli : 'wait_threads' can be { 0=when current environment ends | 1=immediately }.
21020#@cli : Default value: 'wait_threads=1'.
21021#@cli : $ image.jpg [0] parallel "blur[0] 3","mirror[1] c"
21022
21023# The implementation below allows to use parallel as a regular command with selections.
21024parallel : skip "${1=},${2=},${3=},${4=},${5=},${6=},${7=},${8=},${9=},${10=},${11=},${12=},${13=},${14=},${15=}"
21025  if $1==0||$1==1||$1==2 e[0--3] "Execute "{$#-1}" commands '${2--1}' in parallel on image$?."
21026  else e[0--3] "Execute "$#" commands '$*' in parallel on image$?."
21027  fi
21028  parallel $"*"
21029
21030#@cli progress : 0<=value<=100 : -1 : (+)
21031#@cli : Set the progress index of the current processing pipeline.
21032#@cli : This command is useful only when G'MIC is used by an embedding application.
21033
21034#@cli q : eq. to 'quit'. : (+)
21035
21036#@cli quit : (+)
21037#@cli : Quit G'MIC interpreter.
21038#@cli : (eq. to 'q').
21039
21040#@cli repeat : nb_iterations,_variable_name : (+)
21041#@cli : Start 'nb_iterations' iterations of a 'repeat...done' block.
21042#@cli : 'nb_iterations' is a mathematical expression that will be evaluated.
21043#@cli : $ image.jpg split y repeat $!,n shift[$n] $<,0,0,0,2 done append y
21044#@cli : $ image.jpg mode3d 2 repeat 4 imagecube3d rotate3d 1,1,0,40 snapshot3d 400,1.4 done
21045#@cli : $$ https://gmic.eu/oldtutorial/_repeat
21046
21047#@cli return : (+)
21048#@cli : Return from current custom command.
21049
21050#@cli rprogress : 0<=value<=100 | -1 | "command",0<=value_min<=100,0<=value_max<=100
21051#@cli : Set the progress index of the current processing pipeline (relatively to
21052#@cli : previously defined progress bounds), or call the specified command with
21053#@cli : specified progress bounds.
21054rprogress : skip ${2=""}
21055  if !narg($_progress_bounds) _progress_bounds=0,100 fi
21056  m={arg(-2,$_progress_bounds)} M={arg(-1,$_progress_bounds)}
21057  if $#==2&&!narg($2) # 1 argument -> Set progress bar.
21058    e[0--3] "Set relative progress index to $1%."
21059    progress {if($1<0,-1,min(100,max(0,$m+($M-$m)*$1%)))}
21060  elif $#==3 # 3 arguments -> Call command with specified bounds.
21061    nm={min($2,$-1)} nM={max($2,$-1)}
21062    e[0--3] "Call command '$1' with progress bounds ["$nm,$nM"]."
21063    progress $m _progress_bounds=$_progress_bounds,{$m+$nm*($M-$m)/100},{$m+$nM*($M-$m)/100}  # Push new bounds.
21064    run "$1"
21065    progress $M ($_progress_bounds) _progress_bounds={@0--3} rm. # Pop bounds.
21066  else error[0--3] "Command '$0': Invalid argument '$*'."
21067  fi
21068
21069#@cli run : "G'MIC pipeline"
21070#@cli : Run specified G'MIC pipeline.
21071#@cli : This is only useful when used from a shell, e.g. to avoid shell substitutions to happen in argument.
21072run :
21073  $*
21074
21075#@cli skip : item : (+)
21076#@cli : Do nothing but skip specified item.
21077
21078#@cli u : eq. to 'status'. : (+)
21079
21080#@cli status : status_string : (+)
21081#@cli : Set the current status. Used to define a returning value from a function.
21082#@cli : (eq. to 'u').
21083#@cli : $ image.jpg command "foo : u0=Dark u1=Bright status ${u{ia>=128}}" text_outline ${-foo},2,2,23,2,1,255
21084
21085#@cli while : condition : (+)
21086#@cli : End a 'do...while' block and go back to associated 'do' if specified condition holds.
21087#@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0=false | other=true }.
21088
21089#-------------------------
21090#
21091#@cli :: Neural Networks
21092#
21093#-------------------------
21094
21095#@cli nn_lib :
21096#@cli : Return the list of library functions that has to be included in a math expression,\
21097# in order to use the neural network library.
21098nn_lib :
21099  u "
21100    #---------------------------------------------------------------------------
21101    # Definition of the library environment variables and convenience functions.
21102    #---------------------------------------------------------------------------
21103    begin(
21104      const nn_is_training = isnan($_nn_is_training)?0:$_nn_is_training;
21105      const nn_optimizer = isnan($_nn_optimizer)?2:$_nn_optimizer; # Adam optimizer by default
21106      const nn_iteration = isnan($_nn_iteration)?0:$_nn_iteration;
21107      const nn_batch_iteration = isnan($_nn_batch_iteration)?0:$_nn_batch_iteration;
21108      const nn_nb_threads_max = n;
21109    );
21110
21111    begin_t(
21112      nn_thread = t;
21113      nn_nb_threads_used = nn_thread + 1;
21114      nn_nb_samples = 0;
21115    );
21116
21117    ++nn_nb_samples;
21118
21119    end(
21120      merge(nn_nb_threads_used,max);
21121      merge(nn_nb_samples,+);
21122    );
21123
21124    nn_update() = ( # This has to be the last 'end()' bloc encountered in $_nn_update
21125      set(nn_iteration + 1,'_nn_iteration');
21126      set(nn_batch_iteration + 1,'_nn_batch_iteration');
21127    );
21128
21129    nn_display(L) = display(L#_out,L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum);
21130    nn_display_deriv(L) = display(L#_deriv_out,L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum);
21131
21132    #--------------------------------------------
21133    # Activation functions and their derivatives.
21134    #--------------------------------------------
21135    _nn_activate_pointwise(Z,activation) = (fill(#Z,k,nn_activation_#activation(#Z[k])));
21136    nn_activate(Z,activation) = nn_activate_#activation(#Z);
21137
21138    # ELU: Exponential Linear Unit.
21139    #-------------------------------
21140    nn_activation_elu(z) = (z<0?exp(z) - 1:z);
21141    nn_activation_deriv_elu(z) = (z<0?exp(z):1);
21142    nn_activate_elu(Z) = _nn_activate_pointwise(Z,elu);
21143    nn_activate_deriv_elu(Z) = _nn_activate_pointwise(Z,deriv_elu);
21144
21145    # GELU: Gaussian Error Linear Unit.
21146    #-----------------------------------
21147    nn_activation_gelu(z) = (0.5*z*(1 + erf(z/sqrt(2))));
21148    nn_activation_deriv_gelu(z) = (0.5 + 0.5*erf(z/sqrt(2)) + z*exp(-z^2/2)/sqrt(2*pi));
21149    nn_activate_gelu(Z) = _nn_activate_pointwise(Z,gelu);
21150    nn_activate_deriv_gelu(Z) = _nn_activate_pointwise(Z,deriv_gelu);
21151
21152    # LeakyRELU: Leaky Rectified Linear Unit.
21153    #-----------------------------------------
21154    nn_activation_leakyrelu(z) = (z<0?0.05*z:z);
21155    nn_activation_deriv_leakyrelu(z) = (z<0?0.05:1);
21156    nn_activate_leakyrelu(Z) = _nn_activate_pointwise(Z,leakyrelu);
21157    nn_activate_deriv_leakyrelu(Z) = _nn_activate_pointwise(Z,deriv_leakyrelu);
21158
21159    # Linear.
21160    #--------
21161    nn_activate_linear(Z) = (0);
21162    nn_activate_deriv_linear(Z) = (#Z = 1);
21163
21164    # RELU: Rectified Linear Unit.
21165    #------------------------------
21166    nn_activation_relu(z) = (z<0?0:z);
21167    nn_activation_deriv_relu(z) = (z<0?0:1);
21168    nn_activate_relu(Z) = _nn_activate_pointwise(Z,relu);
21169    nn_activate_deriv_relu(Z) = _nn_activate_pointwise(Z,deriv_relu);
21170
21171    # Sigmoid.
21172    #---------
21173    nn_activation_sigmoid(z) = (0.5 + 0.5*tanh(z/2));
21174    nn_activation_deriv_sigmoid(z) = (_nn_sig = nn_activation_sigmoid(z); _nn_sig*(1-_nn_sig));
21175    nn_activate_sigmoid(Z) = _nn_activate_pointwise(Z,sigmoid);
21176    nn_activate_deriv_sigmoid(Z) = _nn_activate_pointwise(Z,deriv_sigmoid);
21177
21178    # Sqr: Square.
21179    #-------------
21180    nn_activate_sqr(Z) = (Z*=Z);
21181    nn_activate_deriv_sqr(Z) = (Z*=2);
21182
21183    # Sqrt: sqrt(|z|).
21184    #-----------------
21185    nn_activation_sqrt(z) = (sqrt(abs(z)));
21186    nn_activation_deriv_sqrt(z) = (0.5*sign(x)/sqrt(max(1e-8,abs(z))));
21187    nn_activate_sqrt(Z) = _nn_activate_pointwise(Z,sqrt);
21188    nn_activate_deriv_sqrt(Z) = _nn_activate_pointwise(Z,deriv_sqrt);
21189
21190    # Tanh.
21191    #------
21192    nn_activation_tanh(z) = (tanh(z));
21193    nn_activation_deriv_tanh(z) = (1 - tanh(z)^2);
21194    nn_activate_tanh(Z) = _nn_activate_pointwise(Z,tanh);
21195    nn_activate_deriv_tanh(Z) = _nn_activate_pointwise(Z,deriv_tanh);
21196
21197    #------------
21198    # Optimizers.
21199    #------------
21200
21201    # Stochastic gradient descent.
21202    #-----------------------------
21203    nn_optimizer_sgd_update_layer(L_params,dL_params) = (
21204      L_params#-=nn_learning_rate*#dL_params;
21205    );
21206
21207    # David optimizer.
21208    #-----------------
21209    nn_optimizer_david_update_layer(L_params,dL_params) = (
21210      _dt = nn_learning_rate/abs(maxabs(1e-9,#dL_params));
21211      L_params#-=_dt*#dL_params;
21212    );
21213
21214    # Adam optimizer.
21215    #----------------
21216    nn_optimizer_adam_init_update_layer() = (
21217      begin(
21218        const nn_optimizer_adam_beta1 = 0.9;
21219        const nn_optimizer_adam_beta2 = 0.999;
21220        const nn_optimizer_adam_beta1_t = nn_iteration>200?0:nn_optimizer_adam_beta1^(nn_iteration + 1);
21221        const nn_optimizer_adam_beta2_t = nn_iteration>200?0:nn_optimizer_adam_beta2^(nn_iteration + 1);
21222        const nn_optimizer_adam_alpha_t = sqrt(1 - nn_optimizer_adam_beta2_t)/(1 - nn_optimizer_adam_beta1_t);
21223      );
21224    );
21225
21226    nn_optimizer_adam_update_layer(L_params,dL_params) = (
21227      dL_params#_name_m = '_nn_#dL_params#_m';
21228      dL_params#_name_v = '_nn_#dL_params#_v';
21229      !nn_iteration || isnan($_nn_#dL_params#_m)?(
21230        dL_params#_m = dL_params#_v = vector(#size(dL_params));
21231      ):(
21232        dL_params#_m = get(dL_params#_name_m,size(dL_params));
21233        dL_params#_v = get(dL_params#_name_v,size(dL_params));
21234      );
21235      dL_params#_m = lerp(dL_params,dL_params#_m,nn_optimizer_adam_beta1);
21236      dL_params#_v = lerp(dL_params^2,dL_params#_v,nn_optimizer_adam_beta2);
21237      store(dL_params#_m,dL_params#_name_m,size(dL_params));
21238      store(dL_params#_v,dL_params#_name_v,size(dL_params));
21239
21240      L_params#-=nn_learning_rate*nn_optimizer_adam_alpha_t*dL_params#_m/sqrt(1e-8 + dL_params#_v);
21241    );
21242
21243    # Adamax optimizer.
21244    #------------------
21245    nn_optimizer_adamax_init_update_layer() = (
21246      begin(
21247        const nn_optimizer_adamax_beta1 = 0.9;
21248        const nn_optimizer_adamax_beta2 = 0.999;
21249        const nn_optimizer_adamax_ombeta1_t = nn_iteration>200?1:(1 - nn_optimizer_adamax_beta1^(nn_iteration + 1));
21250      );
21251    );
21252
21253    nn_optimizer_adamax_update_layer(L_params,dL_params) = (
21254      dL_params#_name_m = '_nn_#dL_params#_m';
21255      dL_params#_name_v = '_nn_#dL_params#_v';
21256      !nn_iteration || isnan($_nn_#dL_params#_m)?(
21257        dL_params#_m = dL_params#_v = vector(#size(dL_params));
21258      ):(
21259        dL_params#_m = get(dL_params#_name_m,size(dL_params));
21260        dL_params#_v = get(dL_params#_name_v,size(dL_params));
21261      );
21262      dL_params#_m = lerp(dL_params,dL_params#_m,nn_optimizer_adamax_beta1);
21263      dL_params#_v = vmax(abs(dL_params),nn_optimizer_adamax_beta2*dL_params#_v);
21264      store(dL_params#_m,dL_params#_name_m,size(dL_params));
21265      store(dL_params#_v,dL_params#_name_v,size(dL_params));
21266
21267      L_params#-=(nn_learning_rate/nn_optimizer_adamax_ombeta1_t)*dL_params#_m/sqrt(1e-8 + dL_params#_v);
21268    );
21269
21270    # Generic function that dispatches to selected optimizer.
21271    #--------------------------------------------------------
21272    begin(
21273      nn_optimizer==2?nn_optimizer_adam_init_update_layer():    # Adam
21274      nn_optimizer==3?nn_optimizer_adamax_init_update_layer();  # Adamax
21275    );
21276    nn_optimizer_update_layer(L,dL) = (
21277      !nn_optimizer?nn_optimizer_sgd_update_layer(#L,#dL):      # Stochastic gradient descent
21278      nn_optimizer==1?nn_optimizer_david_update_layer(#L,#dL):  # David
21279      nn_optimizer==2?nn_optimizer_adam_update_layer(#L,#dL):   # Adam
21280      nn_optimizer==3?nn_optimizer_adamax_update_layer(#L,#dL); # Adamax
21281    );
21282
21283    # Strategy to adapt learning rate regarding loss decrease/increase.
21284    #-------------------------------------------------------------------
21285    nn_optimizer_update_learning_rate(L) = (
21286      nn_batch_iteration?(
21287        # Compute trend: percentage of loss decrease(<0) or increase(>0).
21288        L#_trend = (L#_out - L#_previous_out)/max(1e-8,L#_previous_out);
21289        L#_trend_moment = lerp(sign(L#_trend),L#_trend_moment,0.75);
21290
21291        # Adapt strategy regarding to trend.
21292        L#_trend>=0.3?( # Large local loss increase -> drastically reduce learning rate
21293          L#_learning_rate = max(0.25*L#_learning_rate,1e-11);
21294          L#_nb_increases = L#_nb_decreases = 0;
21295        ):L#_trend_moment>=0?( # Global loss increase
21296          ++L#_nb_increases;
21297          L#_nb_decreases = 0;
21298          L#_nb_increases>=2?( # Global loss increase -> slightly reduce learning rate
21299            L#_learning_rate = max(0.75*L#_learning_rate,1e-11);
21300            L#_nb_increases = 0;
21301          );
21302        ):L#_trend_moment<0?( # Global loss decrease
21303          ++L#_nb_decreases;
21304          L#_nb_decreases>=4?( # Global loss decrease -> slightly increase learning rate
21305            L#_learning_rate = min(L#_learning_rate*1.15,1);
21306            L#_nb_decreases = 0;
21307          );
21308        );
21309      ):(L#_trend = 0);
21310    );
21311
21312    #----------------
21313    # Network layers.
21314    #----------------
21315
21316    # add: Add two inputs.
21317    #---------------------
21318    nn_layer_add_init_forward(L,IN) = (
21319      const L#_out_width = IN#_out_width;
21320      const L#_out_height = IN#_out_height;
21321      const L#_out_depth = IN#_out_depth;
21322      const L#_out_spectrum = IN#_out_spectrum;
21323    );
21324
21325    nn_layer_add_forward(L,IN0,IN1) = (
21326      L#_out = IN0#_out + IN1#_out;
21327    );
21328
21329    nn_layer_add_backward(L,IN0,IN1) = (
21330      IN0#_deriv_out = IN1#_deriv_out = L#_deriv_out/2;
21331    );
21332
21333    # append: Append two inputs as a new output.
21334    #--------------------------------------------
21335    nn_layer_append_init_forward(L,IN0,IN1) = (
21336      const L#_out_width = IN0#_out_width;
21337      const L#_out_height = IN0#_out_height;
21338      const L#_out_depth = IN0#_out_depth;
21339      const L#_out_spectrum = IN0#_out_spectrum + IN1#_out_spectrum;
21340    );
21341
21342    nn_layer_append_forward(L,IN0,IN1) = (
21343      L#_out = vector(#size(IN0#_out) + size(IN1#_out));
21344      copy(L#_out,IN0#_out,size(IN0#_out));
21345      copy(L#_out[size(IN0#_out)],IN1#_out,size(IN1#_out));
21346    );
21347
21348    nn_layer_append_backward(L,IN0,IN1) = (
21349      IN0#_deriv_out = L#_deriv_out[0,size(IN0#_out)];
21350      IN1#_deriv_out = L#_deriv_out[size(IN0#_out),size(IN1#_out)];
21351    );
21352
21353    # avgpool2d : 2D average pooling.
21354    #--------------------------------
21355    nn_layer_avgpool2d_init_forward(L,IN) = (
21356      const L#_out_width = int(IN#_out_width/2);
21357      const L#_out_height = int(IN#_out_height/2);
21358      const L#_out_depth = IN#_out_depth;
21359      const L#_out_spectrum = IN#_out_spectrum;
21360      nn_avgpool2d_kernel_forward = vector4(0.25);
21361      nn_avgpool2d_kernel_backward = [ 1,0,0,0 ];
21362    );
21363
21364    nn_layer_avgpool2d_forward(L,IN) = (
21365      L#_out = correlate(IN#_out,
21366                         IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
21367                         nn_avgpool2d_kernel_forward,2,2,1,1,
21368                         1,0,1,
21369                         0,0,0,
21370                         0,0,0,
21371                         L#_out_width - 1,L#_out_height - 1,L#_out_depth - 1,
21372                         2,2,1);
21373    );
21374
21375    nn_layer_avgpool2d_backward(L,IN) = (
21376      IN#_deriv_out = correlate(L#_deriv_out,
21377                                L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
21378                                nn_avgpool2d_kernel_backward,2,2,1,1,
21379                                0,0,1,
21380                                0,0,0,
21381                                0,0,0,
21382                                IN#_out_width - 1,IN#_out_height - 1,IN#_out_depth - 1,
21383                                0.5,0.5,1,
21384                                1,1,1,
21385                                0);
21386    );
21387
21388    # batchnorm: Batch-normalization layer.
21389    #---------------------------------------
21390    nn_layer_batchnorm_init_forward(L,IN) = (
21391      const L#_ind = $L#;
21392      const L#_out_width = IN#_out_width;
21393      const L#_out_height = IN#_out_height;
21394      const L#_out_depth = IN#_out_depth;
21395      const L#_out_spectrum = IN#_out_spectrum;
21396      const L#_base_col = nn_is_training==1?0:2;
21397      L#_mu = crop(##L#_ind,L#_base_col,0,1,h##L#_ind);
21398      L#_sigma = sqrt(crop(##L#_ind,L#_base_col + 1,0,1,h##L#_ind));
21399      isnan(L#_mu[0])?(L#_mu = 0; L#_sigma = 1);
21400      L#_gamma = crop(##L#_ind,4,0,1,h##L#_ind);
21401      L#_beta = crop(##L#_ind,5,0,1,h##L#_ind);
21402      L#_out = vector(#size(IN#_out));
21403    );
21404
21405    nn_layer_batchnorm_forward(L,IN) = (
21406      L#_z = IN#_out;
21407      L#_z-=L#_mu;
21408      L#_z/=1e-9 + L#_sigma;
21409      L#_out = L#_gamma*L#_z;
21410      L#_out+=L#_beta;
21411    );
21412
21413    nn_layer_batchnorm_init_backward(L,IN) = (
21414      L#_threads_nb_samples = vector(#nn_nb_threads_max);
21415      L#_threads_in_mean = L#_threads_in_variance = vector(#nn_nb_threads_max*size(IN#_out));
21416      L#_nb_samples = 0;
21417      L#_in_mean = L#_in_variance = vector(#size(IN#_out));
21418      L#_batch_deriv_gamma = vector(#size(L#_gamma));
21419      L#_batch_deriv_beta = vector(#size(L#_beta));
21420    );
21421
21422    nn_layer_batchnorm_backward(L,IN,learning_mode) = (
21423      ++L#_nb_samples;
21424      L#_pim = IN#_out - L#_in_mean;
21425      L#_in_mean+=L#_pim/L#_nb_samples;
21426      L#_in_variance+=(IN#_out - L#_in_mean)*L#_pim;
21427      learning_mode&1?(
21428        L#_batch_deriv_gamma+=L#_deriv_out*L#_z;
21429      );
21430      learning_mode&2?(
21431        L#_batch_deriv_beta+=L#_deriv_out;
21432      );
21433      IN#_deriv_out = L#_deriv_out*L#_gamma/(1e-9 + L#_sigma);
21434    );
21435
21436    nn_layer_batchnorm_end_t_backward(L) = (
21437      L#_in_variance/=L#_nb_samples;
21438      copy(L#_threads_nb_samples[nn_thread],L#_nb_samples);
21439      copy(L#_threads_in_mean[nn_thread*size(L#_in_mean)],L#_in_mean);
21440      copy(L#_threads_in_variance[nn_thread*size(L#_in_variance)],L#_in_variance);
21441      L#_in_mean*=L#_nb_samples;
21442    );
21443
21444    nn_layer_batchnorm_end_backward(L) = (
21445      merge(L#_nb_samples,+);
21446      merge(L#_in_mean,+);
21447      merge(L#_threads_nb_samples,+);
21448      merge(L#_threads_in_mean,+);
21449      merge(L#_threads_in_variance,+);
21450      merge(L#_batch_deriv_gamma,+);
21451      merge(L#_batch_deriv_beta,+);
21452      L#_in_mean/=L#_nb_samples;
21453      L#_in_variance = 0;
21454      repeat (nn_nb_threads_used,_k,
21455        L#_in_variance+=L#_threads_nb_samples[_k]*
21456          (L#_threads_in_variance[_k*size(L#_in_variance),size(L#_in_variance)] +
21457          (L#_threads_in_mean[_k*size(L#_in_mean),size(L#_in_variance)] - L#_in_mean)^2);
21458      );
21459      L#_in_variance/=L#_nb_samples;
21460    );
21461
21462    nn_layer_batchnorm_update(L,learning_mode) = (
21463      draw(##L#_ind,L#_in_mean,0,0,0,0,1,h##L#_ind,1,1); # Update batch mean and batch variance
21464      draw(##L#_ind,L#_in_variance,1,0,0,0,1,h##L#_ind,1,1);
21465      !$nn_batch_iteration?(
21466        momentum = isnan(i[##L#_ind,2])?1:0.01;
21467        draw(##L#_ind,L#_in_mean,2,0,0,0,1,h##L#_ind,1,1,momentum); # Global mean and variance
21468        draw(##L#_ind,L#_in_variance,3,0,0,0,1,h##L#_ind,1,1,momentum);
21469      );
21470      learning_mode&1?(
21471        nn_optimizer_update_layer(L#_gamma,L#_batch_deriv_gamma);
21472        draw(##L#_ind,L#_gamma,4,0,0,0,1,h##L#_ind);
21473      );
21474      learning_mode&2?(
21475        nn_optimizer_update_layer(L#_beta,L#_batch_deriv_beta);
21476        draw(##L#_ind,L#_beta,5,0,0,0,1,h##L#_ind);
21477      );
21478    );
21479
21480    # clone: Duplicate input as two new outputs.
21481    #-------------------------------------------
21482    nn_layer_clone_init_forward(L0,L1,IN) = (
21483      const L0#_out_width = IN#_out_width;
21484      const L0#_out_height = IN#_out_height;
21485      const L0#_out_depth = IN#_out_depth;
21486      const L0#_out_spectrum = IN#_out_spectrum;
21487      const L1#_out_width = IN#_out_width;
21488      const L1#_out_height = IN#_out_height;
21489      const L1#_out_depth = IN#_out_depth;
21490      const L1#_out_spectrum = IN#_out_spectrum;
21491    );
21492
21493    nn_layer_clone_forward(L0,L1,IN) = (
21494      L0#_out = L1#_out = IN#_out;
21495    );
21496
21497    nn_layer_clone_backward(L0,L1,IN) = (
21498      IN#_deriv_out = L0#_deriv_out + L1#_deriv_out;
21499    );
21500
21501    # conv2d: 2D convolutional layer.
21502    #--------------------------------
21503    nn_layer_conv2d_init_forward(L,IN,size,stride,dilation) = (
21504      const L#_ind = $L#;
21505      const L#_out_width = max(1,int(IN#_out_width/stride));
21506      const L#_out_height = max(1,int(IN#_out_height/stride));
21507      const L#_out_depth = IN#_out_depth;
21508      const L#_out_spectrum = h##L#_ind;
21509      const L#_kernel_size = size;
21510      const L#_kernel_center = L#_kernel_size - 1 - int(L#_kernel_size/2);
21511      const L#_stride = stride;
21512      const L#_dilation = dilation;
21513
21514      L#_weights = crop(##L#_ind,0,0,w##L#_ind - 1,h##L#_ind);
21515      L#_biases = crop(##L#_ind,w##L#_ind - 1,0,1,h##L#_ind);
21516      L#_out = vector(##L#_out_width*L#_out_height*L#_out_depth*h##L#_ind);
21517    );
21518
21519    nn_layer_conv2d_forward(L,IN) = (
21520      unref(_sizl);
21521      const _sizl = size(L#_out)/L#_out_spectrum; # Size of output plane
21522      L#_out = convolve(IN#_out,
21523                        IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
21524                        L#_weights,
21525                        L#_kernel_size,L#_kernel_size,1,IN#_out_spectrum*L#_out_spectrum,
21526                        1,0,2,
21527                        L#_kernel_center,L#_kernel_center,0,
21528                        0,0,0,
21529                        L#_out_width - 1,L#_out_height - 1,L#_out_depth - 1,
21530                        L#_stride,L#_stride,1,
21531                        L#_dilation,L#_dilation,1);
21532      repeat (L#_out_spectrum,_l,
21533        copy(L#_out[_l*_sizl],L#_biases[_l],_sizl,1,0,-1); # Add biases
21534      );
21535    );
21536
21537    nn_layer_conv2d_init_backward(L,IN) = (
21538      L#_batch_deriv_weights = vector(#size(L#_weights));
21539      L#_batch_deriv_biases = vector(#size(L#_biases));
21540    );
21541
21542    nn_layer_conv2d_backward(L,IN) = (
21543      unref(_sizk,_sizl,_sizw);
21544      const _sizk = size(IN#_out)/IN#_out_spectrum; # Size of input plane
21545      const _sizl = size(L#_out)/L#_out_spectrum;   # Size of output plane
21546      const _sizw = L#_kernel_size^2;               # Size of kernel^2
21547
21548      # Compute dL/dxk(x,y).
21549      L#_weights_mirrored = vector(#size(L#_weights));
21550      repeat (IN#_out_spectrum,_k,
21551        repeat (L#_out_spectrum,_l,
21552          copy(L#_weights_mirrored[_k*L#_out_spectrum*_sizw + _l*_sizw + _sizw - 1],
21553               L#_weights[_l*(w##L#_ind - 1) + _k*_sizw],_sizw,-1,1);
21554        );
21555      );
21556      IN#_deriv_out = convolve(L#_deriv_out,
21557                               L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
21558                               L#_weights_mirrored,
21559                               L#_kernel_size,L#_kernel_size,1,L#_out_spectrum*IN#_out_spectrum,
21560                               0,0,2,
21561                               L#_kernel_size - 1 - L#_kernel_center,
21562                               L#_kernel_size - 1 - L#_kernel_center,0,
21563                               0,0,0,IN#_out_width - 1,IN#_out_height - 1,IN#_out_depth - 1,
21564                               1/L#_stride,1/L#_stride,1,
21565                               L#_dilation/L#_stride,L#_dilation/L#_stride,1);
21566
21567      # Compute dL/dbl.
21568      L#_deriv_biases = vector(#size(L#_biases));
21569      fill(L#_deriv_biases,_l,sum(L#_deriv_out[_l*_sizl,_sizl]));
21570      L#_batch_deriv_biases+=L#_deriv_biases;
21571
21572      # Compute dL/dwlk.
21573      L#_xk_mirrored = vector(#_sizk);
21574      L#_deriv_weights = vector(#size(L#_weights));
21575      repeat (L#_out_spectrum,_l,
21576        repeat (IN#_out_spectrum,_k,
21577          copy(L#_xk_mirrored[_sizk - 1],IN#_out[_k*_sizk],_sizk,-1,1);
21578          L#_dwlk = convolve(L#_xk_mirrored,
21579                             IN#_out_width,IN#_out_height,IN#_out_depth,1,
21580                             L#_deriv_out[_l*_sizl,_sizl],
21581                             L#_out_width,L#_out_height,L#_out_depth,1,
21582                             0,0,1,
21583                             1/L#_stride*(IN#_out_width - 1 - L#_dilation*L#_kernel_center),
21584                             1/L#_stride*(IN#_out_height - 1 - L#_dilation*L#_kernel_center),0,
21585                             0,0,0,L#_kernel_size - 1,L#_kernel_size - 1,0,
21586                             L#_dilation,L#_dilation,1,
21587                             L#_stride,L#_stride,1);
21588          copy(L#_deriv_weights[_l*(w##L#_ind - 1) + _k*_sizw],L#_dwlk,_sizw);
21589        );
21590      );
21591      L#_batch_deriv_weights+=L#_deriv_weights;
21592    );
21593
21594    nn_layer_conv2d_end_backward(L) = (
21595      merge(L#_batch_deriv_weights,+);
21596      merge(L#_batch_deriv_biases,+);
21597      L#_batch_deriv_weights/=nn_nb_samples;
21598      L#_batch_deriv_biases/=nn_nb_samples;
21599    );
21600
21601    nn_layer_conv2d_update(L) = (
21602      nn_optimizer_update_layer(L#_weights,L#_batch_deriv_weights);
21603      nn_optimizer_update_layer(L#_biases,L#_batch_deriv_biases);
21604      draw(##L#_ind,L#_weights,0,0,0,0,w##L#_ind - 1,h##L#_ind);
21605      draw(##L#_ind,L#_biases,w##L#_ind - 1,0,0,0,1,h##L#_ind);
21606    );
21607
21608    # fc: Fully-connected layer.
21609    #---------------------------
21610    nn_layer_fc_init_forward(L) = (
21611      const L#_ind = $L#;
21612      const L#_out_width = 1;
21613      const L#_out_height = 1;
21614      const L#_out_depth = 1;
21615      const L#_out_spectrum = h##L#_ind;
21616
21617      L#_weights = crop(##L#_ind,0,0,w##L#_ind - 1,h##L#_ind);
21618      L#_biases = crop(##L#_ind,w##L#_ind - 1,0,1,h##L#_ind);
21619      L#_out = vector(#h##L#_ind);
21620    );
21621
21622    nn_layer_fc_forward(L,IN) = (
21623      L#_out = mul(L#_weights,IN#_out);
21624      L#_out+=L#_biases;
21625    );
21626
21627    nn_layer_fc_init_backward(L) = (
21628      L#_batch_deriv_weights = vector(#size(L#_weights));
21629      L#_batch_deriv_biases = vector(#size(L#_biases));
21630    );
21631
21632    nn_layer_fc_backward(L,IN) = (
21633      IN#_deriv_out = mul(transpose(L#_weights,size(IN#_out)),L#_deriv_out);
21634      L#_deriv_weights = mul(L#_deriv_out,IN#_out,size(IN#_out));
21635      L#_batch_deriv_weights+=L#_deriv_weights;
21636      L#_batch_deriv_biases+=L#_deriv_out;
21637    );
21638
21639    nn_layer_fc_end_backward(L) = (
21640      merge(L#_batch_deriv_weights,+);
21641      merge(L#_batch_deriv_biases,+);
21642      L#_batch_deriv_weights/=nn_nb_samples;
21643      L#_batch_deriv_biases/=nn_nb_samples;
21644    );
21645
21646    nn_layer_fc_update(L) = (
21647      nn_optimizer_update_layer(L#_weights,L#_batch_deriv_weights);
21648      nn_optimizer_update_layer(L#_biases,L#_batch_deriv_biases);
21649      draw(##L#_ind,L#_weights,0,0,0,0,w##L#_ind - 1,h##L#_ind);
21650      draw(##L#_ind,L#_biases,w##L#_ind - 1,0,0,0,1,h##L#_ind);
21651    );
21652
21653    # input: Input image or vector data.
21654    #-----------------------------------
21655    nn_layer_input_init_forward(L,w,h,d,s) = (
21656      const L#_out_width = w;
21657      const L#_out_height = h;
21658      const L#_out_depth = d;
21659      const L#_out_spectrum = s;
21660      ref(#L,L#_out);
21661    );
21662
21663    # maxpool2d : 2D max pooling.
21664    #----------------------------
21665    nn_layer_maxpool2d_init_forward(L,IN) = (
21666      const L#_out_width = int(IN#_out_width/2);
21667      const L#_out_height = int(IN#_out_height/2);
21668      const L#_out_depth = IN#_out_depth;
21669      const L#_out_spectrum = IN#_out_spectrum;
21670      nn_avgpool2d_kernel_forward = [ 0.25,0.25,0.25,0.25 ];
21671      nn_avgpool2d_kernel_backward = [ 1,0,0,0 ];
21672    );
21673
21674    nn_layer_maxpool2d_forward(L,IN) = (
21675      L#_out = vector(##L#_out_width*L#_out_height*L#_out_depth*L#_out_spectrum);
21676      L#_from = vector(#size(L#_out));
21677      repeat (size(L#_out),_l,
21678        _x = _l%L#_out_width;
21679        _y = int(_l/L#_out_width)%L#_out_height;
21680        _z = int(_l/(L#_out_width*L#_out_height))%L#_out_depth;
21681        _c = int(_l/(L#_out_width*L#_out_height*L#_out_depth));
21682        _k = 2*_x + IN#_out_width*(2*_y + IN#_out_height*(_z + IN#_out_depth*_c));
21683        _k+=arg0(argmax([ IN#_out[_k,2],IN#_out[_k + IN#_out_width,2] ]),0,1,IN#_out_width,IN#_out_width + 1);
21684        L#_from[_l] = _k;
21685        L#_out[_l] = IN#_out[_k];
21686      );
21687    );
21688
21689    nn_layer_maxpool2d_backward(L,IN) = (
21690      IN#_deriv_out = vector(#size(IN#_out),0);
21691      repeat (size(L#_out),_l,
21692        IN#_deriv_out[L#_from[_l]] = L#_deriv_out[_l];
21693      );
21694    );
21695
21696    # nl: Non-linearity.
21697    #-------------------
21698    nn_layer_nl_init_forward(L,IN) = (
21699      const L#_out_width = IN#_out_width;
21700      const L#_out_height = IN#_out_height;
21701      const L#_out_depth = IN#_out_depth;
21702      const L#_out_spectrum = IN#_out_spectrum;
21703
21704      L#_out = vector(#size(IN#_out));
21705    );
21706
21707    nn_layer_nl_forward(L,IN,activation) = (
21708      L#_out = IN#_out;
21709      nn_activate(L#_out,#activation);
21710    );
21711
21712    nn_layer_nl_backward(L,IN,activation) = (
21713      IN#_deriv_out = IN#_out;
21714      nn_activate(IN#_deriv_out,deriv_#activation);
21715      IN#_deriv_out*=L#_deriv_out;
21716    );
21717
21718    # rename: Rename input.
21719    #----------------------
21720    nn_layer_rename_init_forward(L,IN) = (
21721      const L#_out_width = IN#_out_width;
21722      const L#_out_height = IN#_out_height;
21723      const L#_out_depth = IN#_out_depth;
21724      const L#_out_spectrum = IN#_out_spectrum;
21725      ref(IN#_out,L#_out);
21726    );
21727
21728    nn_layer_rename_backward(L,IN) = (
21729      ref(L#_deriv_out,IN#_deriv_out);
21730    );
21731
21732    # reshape: Reshape input to output with compatible size.
21733    #-------------------------------------------------------
21734    nn_layer_reshape_init_forward(L,IN,w,h,d,s) = (
21735      const L#_out_width = w;
21736      const L#_out_height = h;
21737      const L#_out_depth = d;
21738      const L#_out_spectrum = s;
21739      ref(IN#_out,L#_out);
21740    );
21741
21742    nn_layer_reshape_init_backward(L,IN) = (
21743      ref(L#_deriv_out,IN#_deriv_out);
21744    );
21745
21746    # # softmax : Compute the softmax function.
21747    # #----------------------------------------
21748    # nn_layer_softmax_init_forward(L,IN) = (
21749    #   const L#_out_width = IN#_out_width;
21750    #   const L#_out_height = IN#_out_height;
21751    #   const L#_out_depth = IN#_out_depth;
21752    #   const L#_out_spectrum = IN#_out_spectrum;
21753    # );
21754
21755    # nn_layer_softmax_forward(L,IN) = (
21756    #   L#_out = exp(IN#_out - max(IN#_out));
21757    #   L#_sum = resize(correlate(L#_out,L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
21758    #                             [ 1 ],1,1,1,1,
21759    #                             0,0,3),size(L#_out),0,2);
21760    #   L#_out/=L#_sum;
21761    # );
21762
21763    # nn_layer_softmax_backward(L,IN) = (
21764    #   IN#_deriv_out = L#_deriv_out;   # TODO !
21765    # );
21766
21767    # split: Split input as two outputs.
21768    #-----------------------------------
21769    nn_layer_split_init_forward(L0,L1,IN,nb_channels0) = (
21770      const L0#_out_width = IN#_out_width;
21771      const L0#_out_height = IN#_out_height;
21772      const L0#_out_depth = IN#_out_depth;
21773      const L0#_out_spectrum = nb_channels0;
21774      const L1#_out_width = IN#_out_width;
21775      const L1#_out_height = IN#_out_height;
21776      const L1#_out_depth = IN#_out_depth;
21777      const L1#_out_spectrum = IN#_out_spectrum - nb_channels0;
21778    );
21779
21780    nn_layer_split_forward(L0,L1,IN) = (
21781      unref(_siz0);
21782      const siz0 = L0#_out_width*L0#_out_height*L0#_out_depth*L0#_out_spectrum;
21783      L0#_out = IN#_out[0,siz0];
21784      L1#_out = IN#_out[siz0,L1#_out_width*L1#_out_height*L1#_out_depth*L1#_out_spectrum];
21785    );
21786
21787    nn_layer_split_backward(L0,L1,IN) = (
21788      IN#_deriv_out = vector(#size(IN#_out));
21789      copy(IN#_deriv_out,L0#_deriv_out,size(L0#_out));
21790      copy(IN#_deriv_out[size(L0#_out)],L1#_deriv_out,size(L1#_out));
21791    );
21792
21793    # upsample2d : 2D upsampling.
21794    #----------------------------
21795    nn_layer_upsample2d_init_forward(L,IN) = (
21796      const L#_out_width = 2*IN#_out_width;
21797      const L#_out_height = 2*IN#_out_height;
21798      const L#_out_depth = IN#_out_depth;
21799      const L#_out_spectrum = IN#_out_spectrum;
21800      nn_upsample2d_kernel_forward = [ 1,0,0,0 ];
21801      nn_upsample2d_kernel_backward = [ 0.25,0.25,0.25,0.25 ];
21802    );
21803
21804    nn_layer_upsample2d_forward(L,IN,interpolation) = (
21805      L#_out = correlate(IN#_out,
21806                         IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
21807                         nn_upsample2d_kernel_forward,2,2,1,1,
21808                         1,0,1,
21809                         0,0,0,
21810                         0,0,0,
21811                         L#_out_width - 1,L#_out_height - 1,L#_out_depth - 1,
21812                         0.5,0.5,1,
21813                         1,1,1,
21814                         interpolation);
21815    );
21816
21817    nn_layer_upsample2d_backward(L,IN) = (
21818      IN#_deriv_out = correlate(L#_deriv_out,
21819                                L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
21820                                nn_upsample2d_kernel_backward,2,2,1,1,
21821                                0,0,1,
21822                                0,0,0,
21823                                0,0,0,
21824                                IN#_out_width - 1,IN#_out_height - 1,IN#_out_depth - 1,
21825                                2,2,1,
21826                                1,1,1,
21827                                0);
21828    );
21829
21830    #----------------
21831    # Loss functions.
21832    #----------------
21833
21834    # mse : Mean-squared error.
21835    #--------------------------
21836    nn_loss_mse_init_backward(L) = (
21837      L#_batch_out = 0;
21838    );
21839
21840    nn_loss_mse_backward(L,IN,TRUTH) = (
21841      IN#_deriv_out = 2*(IN#_out - TRUTH);
21842      L#_out = norm(IN#_deriv_out/2)^2/size(IN#_deriv_out);
21843      L#_batch_out+=L#_out;
21844    );
21845
21846    nn_loss_mse_end_backward(L) = (
21847      merge(L#_batch_out,+);
21848      L#_batch_out/=nn_nb_samples;
21849      L#_out = L#_batch_out;
21850    );
21851
21852    nn_loss_mse_update(L,is_adaptive_learning_rate) = (
21853      const L#_ind = $L#;
21854      L#_iteration = i[##L#_ind,0];
21855      L#_learning_rate = i[##L#_ind,1];
21856      L#_previous_out = i[##L#_ind,2];
21857      L#_trend_moment = i[##L#_ind,3];
21858      L#_nb_decreases = i[##L#_ind,4];
21859      L#_nb_increases = i[##L#_ind,5];
21860      is_adaptive_learning_rate?nn_optimizer_update_learning_rate(#L);
21861      copy(i[##L#_ind,0],[ L#_iteration + 1,L#_learning_rate,L#_out,L#_trend_moment,L#_nb_decreases,L#_nb_increases ]);
21862      nn_learning_rate = L#_learning_rate;
21863    );
21864    "
21865
21866#@cli nn_init
21867#@cli : Initialize a new network.
21868nn_init :
21869  e[^-1] "[nn_lib] Initialize new network."
21870  _nn_forward,_nn_backward,_nn_parameters,_nn_iteration,_nn_batch_iteration,_nn_is_training,_nn_optimizer=
21871  _nn_init="$0 "
21872  _nn_update="end(nn_update());"
21873
21874#@cli nn_check_layer : name
21875#@cli : Check that the layer with specified name exists in the network.
21876nn_check_layer :
21877  if !${"is_variable_name $1"}
21878    error[0--3] "nn_check_layer: Invalid layer name '$1'."
21879  elif narg($_nn_$1_out_size)!=4
21880    error[0--3] "nn_check_layer: Layer with name '$1' does not exist."
21881  fi
21882
21883#@cli nn_save : 'filename.gmz'
21884#@cli : Save current network as a .gmz file.
21885#@cli : Neural network files can be only saved in .gmz format.
21886nn_save : check "s=['$1']; find(s,'.gmz')==size(s) - 4"
21887  e[^-1] "[nn_lib] Save current network as file '$1'."
21888  ('$_nn_parameters') autocrop. {','} sel={t} rm.
21889  ('$_nn_init') autocrop. {'" "'}
21890  nm. _nn_init
21891  o[$sel,-1] "$1"
21892  rm.
21893
21894#@cli nn_load : 'filename.gmz'
21895#@cli : Load and initialize network saved as a .gmz file.
21896#@cli : Neural network files can be only loaded in .gmz format.
21897nn_load : check "s=['$1']; find(s,'.gmz')==size(s) - 4"
21898  e[^-1] "[nn_lib] Load network from file '$1'."
21899  i "$1" run {t} rm.
21900
21901#@cli nn_layer_input : name,width,_height,_depth,_spectrum
21902#@cli : Add an 'input' layer to the network.
21903#@cli : Default values: 'height=1', 'depth=1' and 'spectrum=1'.
21904nn_layer_input : check ${"is_variable_name $1"}" && isint($2) && $2>0 && isint(${3=1}) && $3>0 && "\
21905                 "isint(${4=1}) && $4>0 && isint(${5=1}) && $5>0"
21906  e[^-1] "[nn_lib] Add input layer '$1', with size ($2,$3,$4,$5)."
21907  _nn_$1_out_size=$2,$3,$4,$5
21908  _nn_forward.="begin(nn_layer_input_init_forward($1,$2,$3,$4,$5));"
21909  _nn_init.="$0 $* "
21910
21911#@cli nn_layer_add : name,in0,in1
21912#@cli : Add an 'add' layer to the network.
21913nn_layer_add : nn_check_layer "$2" nn_check_layer "$3"
21914               check ${"is_variable_name $1"}" && [$_nn_$2_out_size]==[$_nn_$3_out_size]"
21915  e[^-1] "[nn_lib] Add 'add' layer '$1', with inputs '$2' and '$3'."
21916  _nn_$1_out_size=$_nn_$2_out_size
21917  _nn_forward.="begin(nn_layer_add_init_forward($1,$2));"\
21918               "nn_layer_add_forward($1,$2,$3);"
21919  _nn_backward..="nn_layer_add_backward($1,$2,$3);"
21920  _nn_init.="$0 $* "
21921
21922#@cli nn_layer_append : name,in0,in1
21923#@cli : Add an 'append' layer to the network.
21924nn_layer_append : nn_check_layer "$2" nn_check_layer "$3" check ${"is_variable_name $1"}
21925  e[^-1] "[nn_lib] Add 'append' layer '$1', with inputs '$2' and '$3'."
21926  if s1=[$_nn_$2_out_size];s2=[$_nn_$3_out_size];s1[0,3]!=s2[0,3]
21927    error "nn_layer_append: Cannot append inputs $2("$_nn_$2_out_size") and $3("$_nn_$3_out_size") together."
21928  fi
21929  _nn_$1_out_size={[$_nn_$2_out_size]+[0,0,0,[$_nn_$3_out_size][3]]}
21930  _nn_forward.="begin(nn_layer_append_init_forward($1,$2,$3));"\
21931               "nn_layer_append_forward($1,$2,$3);"
21932  _nn_backward..="nn_layer_append_backward($1,$2,$3);"
21933  _nn_init.="$0 $* "
21934
21935#@cli nn_layer_avgpool2d : name,in
21936#@cli : Add a 'avgpool2d' layer (2d average pooling) to the network.
21937nn_layer_avgpool2d : nn_check_layer "$2" check ${"is_variable_name $1"}
21938  e[^-1] "[nn_lib] Add 'avgpool2d' layer '$1', with input '$2'."
21939  _nn_$1_out_size={s=[$_nn_$2_out_size];[int(s[0,2]/2),s[2,2]]}
21940  _nn_forward.="begin(nn_layer_avgpool2d_init_forward($1,$2));"\
21941               "nn_layer_avgpool2d_forward($1,$2);"
21942  _nn_backward..="nn_layer_avgpool2d_backward($1,$2);"
21943  _nn_init.="$0 $* "
21944
21945#@cli nn_layer_batchnorm : name,in,learning_mode.
21946#@cli : Add a 'batchnorm' layer to the network.
21947#@cli : 'learning_mode' can be { 0=no parameters learnt | 1=gamma-only | 2=beta-only | 3=gamma & beta}.
21948nn_layer_batchnorm : nn_check_layer "$2" check ${"is_variable_name $1"}" && isint(${3=0}) && inrange($3,0,3)"
21949  s0,s1,s2,s3="no parameters","gamma-only","beta-only","gamma & beta"
21950  e[^-1] "[nn_lib] Add 'batchnorm' layer '$1' to the network, with input '$2' and learning mode '"${s$3}"'."
21951  _nn_$1_out_size=$_nn_$2_out_size
21952  if !isint($$1) 6,{prod($_nn_$2_out_size)} f. nan,nan,nan,nan,1,0 nm. $1 fi
21953  _nn_forward.="begin(nn_layer_batchnorm_init_forward($1,$2));"\
21954               "nn_layer_batchnorm_forward($1,$2);"
21955  _nn_backward..="begin(nn_layer_batchnorm_init_backward($1,$2));"\
21956                 "nn_layer_batchnorm_backward($1,$2,$3);"\
21957                 "end_t(nn_layer_batchnorm_end_t_backward($1));"\
21958                 "end(nn_layer_batchnorm_end_backward($1));"
21959  _nn_update..="end(nn_layer_batchnorm_update($1,$3));"
21960  _nn_init.="$0 $* "
21961  _nn_parameters.="$1,"
21962
21963#@cli nn_layer_clone : name0,name1,in
21964#@cli : Add a 'clone' layer to the network.
21965nn_layer_clone : nn_check_layer "$3" check ${"is_variable_name $1"}" && "${"is_variable_name $2"}
21966  e[^-1] "[nn_lib] Add 'clone' layer with input '$3' and outputs '$1' and '$2'."
21967  _nn_$1_out_size=$_nn_$3_out_size
21968  _nn_$2_out_size=$_nn_$3_out_size
21969  _nn_forward.="begin(nn_layer_clone_init_forward($1,$2,$3));"\
21970               "nn_layer_clone_forward($1,$2,$3);"
21971  _nn_backward..="nn_layer_clone_backward($1,$2,$3);"
21972  _nn_init.="$0 $* "
21973
21974#@cli nn_layer_conv2d : name,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation>0
21975#@cli : Add a 'conv2d' layer (2D convolutional layer) to the network.
21976#@cli : Default values: 'kernel_size=3', 'stride=1' and 'dilation=1'.
21977nn_layer_conv2d : nn_check_layer "$2"
21978                  check ${"is_variable_name $1"}" && isint($3) && $3>0 && isint(${4=3}) && $4>0 && ${5=1}>0"
21979                  skip "${6=1}"
21980  e[^-1] "[nn_lib] Add 'conv2d' layer '$1', with input '$2', $3 channels, $4x$4 kernels, stride $5 and dilation $6."
21981  _nn_$1_out_size={s=[$_nn_$2_out_size];[max(1,int(s[0]/$5)),max(1,int(s[1]/$5)),s[2],$3]}
21982  if !isint($$1) {[$_nn_$2_out_size][3]*$4^2+1},$3 if $4>1 f. 4*g/(w-1) else f. 1 fi nm. $1 fi
21983  _nn_forward.="begin(nn_layer_conv2d_init_forward($1,$2,$4,$5,$6));"\
21984               "nn_layer_conv2d_forward($1,$2);"
21985  _nn_backward..="begin(nn_layer_conv2d_init_backward($1,$2));"\
21986                 "nn_layer_conv2d_backward($1,$2);"\
21987                 "end(nn_layer_conv2d_end_backward($1));"
21988  _nn_update..="end(nn_layer_conv2d_update($1));"
21989  _nn_init.="$0 $* "
21990  _nn_parameters.="$1,"
21991
21992#@cli nn_layer_conv2dnl : name,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation>0,_activation
21993#@cli : Add a 'con2dnl' (2D convolutional layer followed by a non-linearity), to the network.
21994#@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1' and 'activation=leakyrelu'.
21995nn_layer_conv2dnl : nn_check_layer "$2"
21996                    check ${"is_variable_name $1"}" && isint($3) && $3>0 && isint(${4=3}) && $4>0 && ${5=1}>0"
21997                    skip "${6=1}","${7=leakyrelu}"
21998  e[^-1] "[nn_lib] Add 'conv2d+nl' layer '$1', with input '$2', $3 channels, $4x$4 kernels, "\
21999         "stride $5, dilation $6 and '$7' activation."
22000  nn_layer_conv2d $1_0,$2,$3,$4,$5,$6
22001  nn_layer_nl $1,$1_0,$7
22002
22003#@cli nn_layer_fc : name,in,nb_channels>0
22004#@cli : Add a 'fc' layer (fully connected layer) to the network.
22005nn_layer_fc : nn_check_layer "$2" check ${"is_variable_name $1"}" && isint($3) && $3>0"
22006  e[^-1] "[nn_lib] Add 'fc' layer '$1', with input '$2' and $3 channels."
22007  _nn_$1_out_size=1,1,1,$3
22008  if !isint($$1) {prod($_nn_$2_out_size)+1},$3 f. 4*g/(w-1) nm. $1 fi
22009  _nn_forward.="begin(nn_layer_fc_init_forward($1));"\
22010               "nn_layer_fc_forward($1,$2);"
22011  _nn_backward..="begin(nn_layer_fc_init_backward($1));"\
22012                 "nn_layer_fc_backward($1,$2);"\
22013                 "end(nn_layer_fc_end_backward($1));"
22014  _nn_update..="end(nn_layer_fc_update($1));"
22015  _nn_init.="$0 $* "
22016  _nn_parameters.="$1,"
22017
22018#@cli nn_layer_fcnl : name,in,nb_neurons>0,_activation
22019#@cli : Add a 'fcnl' layer (fully connected layer followed by a non-linearity), to the network.
22020#@cli : Default value: 'activation=leakyrelu'.
22021nn_layer_fcnl : nn_check_layer "$2" check ${"is_variable_name $1"}" && isint($3) && $3>0" skip ${4=leakyrelu}
22022  e[^-1] "[nn_lib] Add 'fc+nl' layer '$1' to the network, with input '$2', $3 channels and '$4' activation."
22023  nn_layer_fc $1_0,$2,$3
22024  nn_layer_nl $1,$1_0,$4
22025
22026#@cli nn_layer_maxpool2d : name,in
22027#@cli : Add a 'maxpool2d' layer (2d max pooling) to the network.
22028nn_layer_maxpool2d : nn_check_layer "$2" check ${"is_variable_name $1"}
22029  e[^-1] "[nn_lib] Add 'maxpool2d' layer '$1', with input '$2'."
22030  _nn_$1_out_size={s=[$_nn_$2_out_size];[int(s[0,2]/2),s[2,2]]}
22031  _nn_forward.="begin(nn_layer_maxpool2d_init_forward($1,$2));"\
22032               "nn_layer_maxpool2d_forward($1,$2);"
22033  _nn_backward..="nn_layer_maxpool2d_backward($1,$2);"
22034  _nn_init.="$0 $* "
22035
22036#@cli nn_layer_nl : name,in,activation
22037#@cli : Add a 'nl' layer (non-linearity) to the network.
22038#@cli : Default value: 'activation=leakyrelu'.
22039nn_layer_nl : nn_check_layer "$2" check ${"is_variable_name $1"} skip ${3=leakyrelu}
22040  e[^-1] "[nn_lib] Add 'nl' layer '$1', with input '$2' and '$3' activation."
22041  _nn_$1_out_size=$_nn_$2_out_size
22042  _nn_forward.="begin(nn_layer_nl_init_forward($1,$2));"\
22043               "nn_layer_nl_forward($1,$2,$3);"
22044  _nn_backward..="nn_layer_nl_backward($1,$2,$3);"
22045  _nn_init.="$0 $* "
22046
22047#@cli nn_layer_rename : name,in
22048#@cli : Add a 'rename' layer to the network.
22049nn_layer_rename : nn_check_layer "$2" check ${"is_variable_name $1"}
22050  e[^-1] "[nn_lib] Add 'rename' layer '$1', with input '$2'."
22051  _nn_$1_out_size=$_nn_$2_out_size
22052  _nn_forward.="begin(nn_layer_rename_init_forward($1,$2));"
22053  _nn_backward..="nn_layer_rename_backward($1,$2);"
22054  _nn_init.="$0 $* "
22055
22056#@cli nn_layer_reshape : name,in,width>0,height>0,depth>0,spectrum>0
22057#@cli : Add a 'reshape' layer to the network.
22058nn_layer_reshape : nn_check_layer "$2" check ${"is_variable_name $1"}
22059  e[^-1] "[nn_lib] Add 'reshape' layer '$1', with input '$2' and size ($3,$4,$5,$6)."
22060  _nn_$1_out_size=$3,$4,$5,$6
22061  if prod($_nn_$1_out_size)!=prod($_nn_$2_out_size)
22062    error "nn_layer_reshape: Cannot reshape input ("$_nn_$2_out_size") to output ("$_nn_$1_out_size")."
22063  fi
22064  _nn_forward.="begin(nn_layer_reshape_init_forward($1,$2,$3,$4,$5,$6));"
22065  _nn_backward..="begin(nn_layer_reshape_init_backward($1,$2));"
22066  _nn_init.="$0 $* "
22067
22068# #@cli nn_layer_softmax : name,in
22069# #@cli : Add a 'softmax' layer to the network.
22070# nn_layer_softmax : nn_check_layer "$2" check ${"is_variable_name $1"}
22071#   e[^-1] "[nn_lib] Add 'softmax' layer '$1', with input '$2'."
22072#   _nn_$1_out_size=$_nn_$2_out_size
22073#   _nn_forward.="begin(nn_layer_softmax_init_forward($1,$2));"\
22074#                "nn_layer_softmax_forward($1,$2);"
22075#   _nn_backward..="nn_layer_softmax_backward($1,$2);"
22076#   _nn_init.="$0 $* "
22077
22078#@cli nn_layer_split : name0,name1,in,nb_channels0
22079#@cli : Add a 'split' layer to the network.
22080nn_layer_split : nn_check_layer "$3" check ${"is_variable_name $1"}" && "${"is_variable_name $2"}
22081  e[^-1] "[nn_lib] Add 'split' layer, with input '$3' and outputs '$1' and '$2'."
22082  _nn_$1_out_size={[[$_nn_$3_out_size][0,3],$4]}
22083  _nn_$2_out_size={[$_nn_$3_out_size]-[0,0,0,$4]}
22084  _nn_forward.="begin(nn_layer_split_init_forward($1,$2,$3,$4));"\
22085               "nn_layer_split_forward($1,$2,$3);"
22086  _nn_backward..="nn_layer_split_backward($1,$2,$3);"
22087  _nn_init.="$0 $* "
22088
22089#@cli nn_layer_upsample2d : name,in,_interpolation_type = { 0=nearest-neighbor | 1=linear }
22090#@cli : Add a 'upsample2d' layer to the network.
22091nn_layer_upsample2d : nn_check_layer "$2" check ${"is_variable_name $1"}" && isbool(${3=0})"
22092  e[^-1] "[nn_lib] Add 'upsample2d' layer '$1', with input '$2'."
22093  _nn_$1_out_size={s=[$_nn_$2_out_size];[2*s[0,2],s[2,2]]}
22094  _nn_forward.="begin(nn_layer_upsample2d_init_forward($1,$2));"\
22095               "nn_layer_upsample2d_forward($1,$2,$3);"
22096  _nn_backward..="nn_layer_upsample2d_backward($1,$2);"
22097  _nn_init.="$0 $* "
22098
22099#@cli nn_loss_mse : name,in,ground_truth,_learning_rate,_is_adaptive_learning_rate={ 0=false | 1=true }
22100#@cli : Add a 'mse' loss to the network.
22101#@cli : Default value: 'learning_rate=1e-6' and 'is_adaptive_learning_rate=1'.
22102nn_loss_mse : nn_check_layer "$2" check ${"is_variable_name $1"}" && "${"is_variable_name $3"}" && "\
22103             "${4=1e-6}>0 && isbool(${5=1})"
22104  s0,s1=fixed,adaptive
22105  e[^-1] "[nn_lib] Add 'mse' loss '$1', with input '$2', ground truth '$3' and "${s$5}" learning rate $4."
22106  _nn_$1_out_size=1,1,1,1
22107  if !isint($$1)
22108  # (iteration,learning_rate,previous_out,trend_moment,nb_decreases,nb_increases)
22109    (0,$4,inf,0,0,0) nm. $1
22110  fi
22111  _nn_backward..="begin(nn_loss_mse_init_backward($1));"\
22112                 "nn_loss_mse_backward($1,$2,$3);"\
22113                 "end(nn_loss_mse_end_backward($1));"
22114  _nn_update..="end(nn_loss_mse_update($1,$5));"
22115  _nn_init.="$0 $* "
22116  _nn_parameters.="$1,"
22117
22118#----------------------------------
22119#
22120#@cli :: Arrays, Tiles and Frames
22121#
22122#----------------------------------
22123
22124#@cli array : M>0,_N>0,_expand_type={ 0=min | 1=max | 2=all }
22125#@cli : Create MxN array from selected images.
22126#@cli : Default values: 'N=M' and 'expand_type=0'.
22127#@cli : $ image.jpg array 3,2,2
22128array : check "isint($1) && $1>0 && isint(${2=$1}) && $2>0" skip ${3=0}
22129  e[^-1] "Create $1x$2 array from image$?, with expand type $3."
22130  r0={100/max($1,$2)} r1={100/min($1,$2)} r2=100
22131  r ${r$3}%,${r$3}%,1,100%,2 r {$1*100}%,{$2*100}%,1,100%,0,2
22132
22133#@cli array_fade : M>0,_N>0,0<=_fade_start<=100,0<=_fade_end<=100,_expand_type={0=min | 1=max | 2=all}
22134#@cli : Create MxN array from selected images.
22135#@cli : Default values: 'N=M', 'fade_start=60', 'fade_end=90' and 'expand_type=1'.
22136#@cli : $ image.jpg array_fade 3,2
22137array_fade : skip ${2=$1},${3=60},${4=90},${5=1}
22138  e[^-1] "Create $1x$2 array of ($3%,$4%) faded tiles from image$?, with expand type $5."
22139  repeat $! l[$>] . shift.. {round(w/2)},{round(h/2)},1,1,2 fade_diamond $3,$4 endl done
22140  array $1,$2,$5
22141
22142#@cli array_mirror : N>=0,_dir={ 0=x | 1=y | 2=xy | 3=tri-xy },_expand_type={ 0 | 1 }
22143#@cli : Create 2^Nx2^N array from selected images.
22144#@cli : Default values: 'dir=2' and 'expand_type=0'.
22145#@cli : $ image.jpg array_mirror 2
22146array_mirror : skip ${2=2},${3=0}
22147  e[^-1] "Create a 2^$1x2^$1 mirrored-array from image$?, with expand type $2."
22148  repeat $1
22149    if $3==0
22150      if $2>=3 r 33%,33%,100%,100%,2
22151      else r 50%,50%,100%,100%,2
22152      fi
22153    fi
22154    repeat $! l[$>]
22155      if $2==0 +mirror x a x
22156      elif $2==1 +mirror y a y
22157      else +mirror x a x +mirror y a y if $2==3 r 150%,150%,1,100%,0,2,1,1 fi
22158      fi
22159    endl done
22160  done
22161
22162#@cli array_random : Ms>0,_Ns>0,_Md>0,_Nd>0
22163#@cli : Create MdxNd array of tiles from selected MsxNs source arrays.
22164#@cli : Default values: 'Ns=Ms', 'Md=Ms' and 'Nd=Ns'.
22165#@cli : $ image.jpg +array_random 8,8,15,10
22166array_random : skip ${2=$1},${3=$1},${4=$2}
22167  e[^-1] "Create $3x$4 array of tiles from $1x$2 array$?."
22168  repeat $! l[$>] nm={0,n}
22169    split_tiles $1,$2
22170    repeat $3 repeat $4 [{u($1*$2-1)}] done done
22171    rm[0-{$1*$2-1}] append_tiles $3,$4
22172  nm $nm endl done
22173
22174#@cli frame : eq. to 'frame_xy'.
22175frame : skip ${2=$1}>=0,${3=255},${4=$3},${5=$4},${6=255}
22176  _gmic_s="$?" v + _frame_xy ${1--1}
22177
22178#@cli frame_blur : _sharpness>0,_size>=0,_smoothness,_shading,_blur
22179#@cli : Draw RGBA-colored round frame in selected images.
22180#@cli : Default values: 'sharpness=10', 'size=30', 'smoothness=0', 'shading=1' and 'blur=3%'.
22181#@cli : $ image.jpg frame_blur 3,30,8,10%
22182frame_blur : skip ${1=10},${2=30},${3=0},${4=1},${5=3%}
22183  e[^-1] "Draw round frame on image$?, with sharpness $1, size $2, smoothness $3, shading $4 and blur $5."
22184  to_rgba repeat $! l[$>] nm={0,n}
22185    100%,100%,1,1,"-(abs(x/w-0.5)^$1 + abs(y/h-0.5)^$1)^(1/$1)" >=. $2%
22186    if $4 distance. 1 n. 0,1 *. -1 +. 1 ^. {1/$4} fi
22187    b. $3 +b.. $5 mv. -3 blend_fade[0,1] . rm.
22188  nm $nm endl done
22189
22190#@cli frame_cube : _depth>=0,_centering_x,_centering_y,_left_side={0=normal | 1=mirror-x | 2=mirror-y | 3=mirror-xy},\
22191# _right_side,_lower_side,_upper_side
22192#@cli : Insert 3D frames in selected images.
22193#@cli : Default values: 'depth=1', 'centering_x=centering_y=0' and 'left_side=right_side,lower_side=upper_side=0'.
22194#@cli : $ image.jpg frame_cube ,
22195frame_cube : check "${1=1}>=0" skip ${2=0},${3=0},${4=0},${5=0},${6=0},${7=0}
22196  e[^-1] "Insert 3D frame in image$?, with depth $1, centering point ($2,$3) and orientations (${4--1})."
22197  repeat $! l[$>] nm={0,n} split_opacity
22198    if $!==2 frame_cube ${1--1} a c  # Manage image with alpha-channel.
22199    else
22200      m={max(w,h)} w={w} h={h} s={s}
22201      imageplane3d c3d /3d. $w,$h,1
22202      +_frame_cube[0] $4 r3d. 0,1,0,-90 +3d. -0.5,0,-0.5  # Left side.
22203      +_frame_cube[0] $5 r3d. 0,1,0,90 +3d. 0.5,0,-0.5  # Right side.
22204      +_frame_cube[0] $6 r3d. 1,0,0,-90 +3d. 0,0.5,-0.5   # Lower side.
22205      +_frame_cube[0] $7 r3d. 1,0,0,90 +3d. 0,-0.5,-0.5 # Upper side.
22206      +3d 0,0,1 +3d *3d $w,$h,$m  # Append sides together.
22207      f=1000
22208      cx=$2*$w/2*($f+$m*$1)/$f
22209      cy=$3*$h/2*($f+$m*$1)/$f
22210      s3d r[2] 3,{{2,h}/3},1,1,-1
22211      f[2] "if(i(2,y)<0.5,i,i+if(x==0,"$cx",if(x==1,"$cy",($1-1)*"$m")))"
22212      y[2] a y
22213      *3d 2 {2*$w},{2*$h},1,$s f3d {2*$f}
22214      j3d. ..,50%,50%,0,1,2,0,0 rm..
22215      r $w,$h,1,100%,2
22216    fi
22217  nm $nm endl done
22218
22219_frame_cube :
22220  if $1==1 r3d. 0,1,0,180 rv3d.
22221  elif $1==2 r3d. 1,0,0,180 rv3d.
22222  elif $1==3 r3d. 0,0,1,180
22223  fi
22224
22225#@cli frame_fuzzy : size_x[%]>=0,_size_y[%]>=0,_fuzzyness>=0,_smoothness[%]>=0,_R,_G,_B,_A
22226#@cli : Draw RGBA-colored fuzzy frame in selected images.
22227#@cli : Default values: 'size_y=size_x', 'fuzzyness=5', 'smoothness=1' and 'R=G=B=A=255'.
22228#@cli : $ image.jpg frame_fuzzy 20
22229frame_fuzzy : skip ${2=$1},${3=5},${4=1},${5=255},${6=$5},${7=$6},${8=255}
22230  e[^-1] "Draw $1x$2 fuzzy frame on image$?, with fuzzyness $3, smoothness $4 and RGBA color ($5,$6,$7,$8)."
22231  to_rgba repeat $! l[$>]
22232    100%,100%,1,1,1
22233    padx={if(${"is_percent $1"},$1*(w-1)/2,$1)}
22234    pady={if(${"is_percent $2"},$2*(h-1)/2,$2)}
22235    rectangle. $padx,$pady,{w-1-$padx},{h-1-$pady}
22236    spread. $3 b. $4 100%,100%,1,4 fc. ${5-8}
22237    j[0] [2],0,0,0,0,1,[1] k[0]
22238  endl done
22239
22240#@cli frame_painting : _size[%]>=0,0<=_contrast<=1,_profile_smoothness[%]>=0,_R,_G,_B,_vignette_size[%]>=0,\
22241# _vignette_contrast>=0,_defects_contrast>=0,0<=_defects_density<=100,_defects_size>=0,_defects_smoothness[%]>=0,\
22242# _serial_number
22243#@cli : Add a painting frame to selected images.
22244#@cli : Default values: 'size=10%', 'contrast=0.4', 'profile_smoothness=6%', 'R=225', 'G=200', 'B=120', \
22245# 'vignette_size=2%', 'vignette_contrast=400', 'defects_contrast=50', 'defects_density=10', 'defects_size=1', \
22246# 'defects_smoothness=0.5%' and 'serial_number=123456789'.
22247#@cli : $ image.jpg frame_painting ,
22248frame_painting :
22249  check "${1=10%}>=0 && ${2=0.4}>=0 && $2<=1 && ${3=6%}>=0 && ${7=2%}>=0 && ${8=400}>=0 && ${9=50}>=0 &&
22250         ${10=10}>=0 && $10<=100 && ${11=1}>=0 && ${12=0.5%}>=0"
22251  skip ${4=225},${5=200},${6=120},${13=123456789}
22252  e[^-1] "Add painting frame to image$?, with size $1, contrast $2, profile smoothness $3, color (${4-6}),
22253          vignette size $7, vignette strength $8, defects contrast $9, defects density $10, defects size $11,
22254          defects smoothness $12 and serial number $13."
22255  if !$1 return fi
22256  repeat $! l[$>]
22257    $1,$1 s={max(w,h)} rm.                             # Determine size of the frame
22258    ('${dec2bin\ $13}') -. {'0'} r. $s                 # Generate frame profile from serial number
22259    transpose. b. $3 n. {1-$2},{1+$2}
22260    +r. {{-2,w}+2*$s},100%,1,1                         # Upper frame
22261    +mirror. y                                         # Lower frame
22262    mv... $! transpose. r. 100%,{-4,h+2*$s},1,1        # Left frame
22263    +mirror. x                                         # Right frame
22264    ...,...,1,1,1
22265    polygon. 3,0,0,{$s-1},{$s-1},0,{$s-1},1,0
22266    polygon. 3,100%,0,{w-$s},100%,100%,100%,1,0   # Upper/lower mask
22267    ..,..,1,1,1
22268    polygon. 3,1,0,100%,{$s-2},100%,0
22269    polygon. 3,1,100%,100%,{h-$s+1},100%,100%,1,0 # Left/right mask
22270    _frame_painting[-6--3] ${4-6},${9-12}         # Add colors + defects
22271
22272    # Build full frame picture.
22273    {-7,w+2*$s},{-7,h+2*$s},1,3
22274    j. [-7],0,0,0,0,1,...,1 rm[-7] mirror... y
22275    j. [-6],0,{h-$s},0,0,1,...,1 rm[-6,-3]
22276    j. [-4],0,0,0,0,1,..,1 rm[-4] mirror.. x
22277    j. ...,{w-$s},0,0,0,1,..,1 rm[-3,-2]
22278    ..,..,1,1,-255 r. ..,..,1,1,0,0,0.5,0.5 +. 255 +b. $7 n. 0,$8 max[-2,-1] c. 0,255 # Frame opacity.
22279    a[-2--1] c
22280    r.. .,.,1,100%,0,0,0.5,0.5 blend alpha # Insert initial image into frame picture.
22281  endl done
22282
22283_frame_painting : # Add color + texture to each frame part.
22284  repeat $! l[$>]
22285    +*. $2 +*.. $3 *... $1 a[-3--1] c
22286    100%,100%
22287    i=0 do rand. 0,1 remove_pixels. {100-$5}% b. $6 >=. 50% i+=1 while "m=$5/200;(ia<m-0.2 || ia>m+0.2) && "$i"<10"
22288    b. $7 g. +[-2,-1] n. -$4,$4
22289    +[-2,-1] c. 0,255
22290  endl done
22291
22292#@cli frame_pattern : M>=3,_constrain_size={ 0 | 1 } : M>=3,_[frame_image],_constrain_size={ 0 | 1 }
22293#@cli : Insert selected pattern frame in selected images.
22294#@cli : Default values: 'pattern=0' and 'constrain_size=0'.
22295#@cli : $ image.jpg frame_pattern 8
22296frame_pattern : check $1>=3 skip "${2=0},${3=}"
22297  to_colormode 0
22298  if ${"is_image_arg $2"} # Frame from specified image.
22299    e[^-1] "Insert $1x$1 pattern frame on image$?, using frame image$2."
22300    pass$2 0 repeat $!-1 l[$>,-1]
22301      wh={0,w},{0,h}
22302      +r[1] {0,max(1,w/($1-2))},{0,max(1,h/($1-2))},1,100%,2
22303      r[0] {{0,w}+2*w},{{0,h}+2*h},1,100%,0,0,0.5,0.5
22304      [-1]x{$1+2} a[{-$1-2}--1] x j[0] .,0,0 j[0] .,0,{{0,h}-1-h} rm.
22305      [-1]x{$1+1} a[{-$1-2}--1] y j[0] .,0,0 j[0] .,{{0,w}-1-w} rm.
22306      if $3 r[0] $wh,1,100%,2 fi
22307    endl done rm.
22308  else # Self-frame.
22309    e[^-1] "Insert $1x$1 self-pattern frame on image$?."
22310    repeat $! l[$>]
22311      wh={w},{h}
22312      +r {max(1,w/($1-2))},{max(1,h/($1-2))},1,100%,2 r.. {$1*w},{$1*h},1,100%,0,0,0.5,0.5
22313      [-1]x{$1+2} a[{-$1-2}--1] x j... .,0,0 j... .,0,{{-3,h}-1-h} rm.
22314      [-1]x{$1+1} a[{-$1-2}--1] y j.. .,0,0 j.. .,{{-2,w}-1-w} rm.
22315      if $3 r $wh,1,100%,2 fi
22316    endl done
22317  fi
22318
22319#@cli frame_round : _sharpness>0,_size>=0,_smoothness,_shading,_R,_G,_B,_A
22320#@cli : Draw RGBA-colored round frame in selected images.
22321#@cli : Default values: 'sharpness=10', 'size=10', 'smoothness=0', 'shading=0' and 'R=G=B=A=255'.
22322#@cli : $ image.jpg frame_round 10
22323frame_round : skip ${1=10},${2=10},${3=0},${4=0},${5=255},${6=$5},${7=$6},${8=255}
22324  e[^-1] "Draw round frame on image$?, with sharpness $1, size $2, smoothness $3, shading $4 and
22325          RGBA color ($5,$6,$7,$8)."
22326  to_rgba repeat $! l[$>] nm={0,n}
22327    100%,100%,1,1,"-(abs(x/w-0.5)^$1 + abs(y/h-0.5)^$1)^(1/$1)" >=. $2%
22328    if $4 distance. 1 n. 0,1 *. -1 +. 1 ^. {1/$4} fi
22329    b. $3 i... 100%,100%,1,4 fc... $5,$6,$7,$8 blend_fade[0,1] . rm.
22330  nm $nm endl done
22331
22332#@cli frame_seamless : frame_size>=0,_patch_size>0,_blend_size>=0,\
22333# _frame_direction={ 0=inner (preserve image size) | 1=outer }
22334#@cli : Insert frame in selected images, so that tiling the resulting image makes less visible seams.
22335#@cli : Default values: 'patch_size=7', 'blend_size=5' and 'frame_direction=1'.
22336#@cli : $ image.jpg +frame_seamless 30 array 2,2
22337frame_seamless : check "$1>=0 && isint(${2=7}) && $2>0 && isint(${3=5}) && $3>=0" skip ${4=1}
22338  s0="inner" s1="outer"
22339  e[^-1] "Insert "${s{!!$4}}" seamless frame in image$?, with size $1, patch size $2 and blend size $3."
22340  repeat $! l[$>]
22341    w2={round(w/2)} h2={round(h/2)}
22342    w4={round(w/4)} h4={round(h/4)}
22343    if !$4 r {max(1,w-$1)},{max(1,h-$1)},1,100%,0,0,0.5,0.5 fi
22344    100%,100%,1,1,-1 r[-2,-1] {w+$1},{h+$1},1,100%,0,0,0.5,0.5 n. 0,1
22345    shift -$w2,-$h2,0,0,2
22346    inpaint_matchpatch.. [1],0,$2,10,$3
22347    rectangle. $1,$1,{w-1-$1},{h-1-$1}
22348    shift -$w4,-$h4,0,0,2
22349    inpaint_matchpatch.. [1],0,$2,10,$3
22350    rm.
22351    shift {$w4+$w2},{$h4+$h2},0,0,2
22352  endl done
22353
22354#@cli frame_x : size_x[%],_col1,...,_colN
22355#@cli : Insert colored frame along the x-axis in selected images.
22356#@cli : Default values: 'col1=col2=col3=255' and 'col4=255'.
22357#@cli : $ image.jpg frame_x 20,255,0,255
22358frame_x : skip ${2=255},${3=$2},${4=$3},${5=255}
22359  e[^-1] "Insert $1 outer frame in image$? along the x-axis, with color (${2--1})."
22360  _frame $1,0,0,${2--1}
22361
22362#@cli frame_xy : size_x[%],_size_y[%],_col1,...,_colN
22363#@cli : Insert colored frame along the x-axis in selected images.
22364#@cli : Default values: 'size_y=size_x', 'col1=col2=col3=255' and 'col4=255'.
22365#@cli : (eq. to 'frame').
22366#@cli : $ image.jpg frame_xy 1,1,0 frame_xy 20,10,255,0,255
22367frame_xy : skip ${2=$1},${3=255},${4=$3},${5=$4},${6=255}
22368  _gmic_s="$?" v + _$0 ${1--1}
22369
22370_frame_xy :
22371  e[0--3] "Insert $1x$2 outer frame in image"$_gmic_s" along the xy-axes, with color (${3--1})."
22372  _frame $1,$2,0,${3--1}
22373
22374#@cli frame_xyz : size_x[%],_size_y[%],_size_z[%]_col1,...,_colN
22375#@cli : Insert colored frame along the x-axis in selected images.
22376#@cli : Default values: 'size_y=size_x=size_z', 'col1=col2=col3=255' and 'col4=255'.
22377frame_xyz : skip ${2=$1},${3=$2},${4=255},${5=$4},${6=$5},${7=255}
22378  e[^-1] "Insert $1x$2x$3 outer frame in image$? along the xyz-axes, with color (${4--1})."
22379  _frame $1,$2,$3,${4--1}
22380
22381#@cli frame_y : size_y[%],_col1,...,_colN
22382#@cli : Insert colored frame along the y-axis in selected images.
22383#@cli : Default values: 'col1=col2=col3=255' and 'col4=255'.
22384#@cli : $ image.jpg frame_y 20,255,0,255
22385frame_y : skip ${2=255},${3=$2},${4=$3},${5=255}
22386  e[^-1] "Insert $1 outer frame in image$? along the y-axis, with color (${2--1})."
22387  _frame 0,$1,0,${2--1}
22388
22389_frame :
22390  repeat $! l[$>]
22391    nm={0,n}
22392    w={round($1*if(${is_percent\ $1},w,1))}
22393    h={round($2*if(${is_percent\ $2},h,1))}
22394    d={round($3*if(${is_percent\ $3},d,1))}
22395    {w+2*$w},{h+2*$h},{d+2*$d},100% fc[1] ${4--1}
22396    j[1] [0],$w,$h,$d rm[0] nm $nm
22397  endl done
22398
22399#@cli img2ascii : _charset,_analysis_scale>0,_analysis_smoothness[%]>=0,_synthesis_scale>0,_output_ascii_filename
22400#@cli : Render selected images as binary ascii art.
22401#@cli : This command returns the corresponding the list of widths and heights (expressed as a number of characters)
22402#@cli : for each selected image.
22403#@cli : Default values: 'charset=[ascii charset]', 'analysis_scale=16', 'analysis_smoothness=20%', \
22404# 'synthesis_scale=16' and '_output_ascii_filename=[undefined]'.
22405#@cli : $ image.jpg img2ascii ,
22406img2ascii : check "${2=16}>0 && ${3=20%}>=0 && ${4=16}>0"
22407            skip "${1= !\042#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\133\\\135^_\140abcdefghijk"\
22408                 "lmnopqrstuvwxyz\173|\174~}","${5=}"
22409  e[^-1] "Render image$? as binary ascii art, with charset '$1', analysis scale $2, analysis smoothness $3,
22410          synthesis scale $4 and output ascii filename '$5'."
22411  is_multi={$!>1}
22412
22413  # Generate dictionaries for image analysis and synthesis.
22414  l[]
22415    ('"$1"') repeat w
22416      C={`92`}${dec2oct\ {0,@$>}}
22417      0 t. $C,0,0,$2,1,1
22418      0 t. $C,0,0,$4,1,1
22419    done rm[0]
22420    = -1 = -1,0,100% autocrop = 0 = 0,0,100%
22421    l[0--2:2] r {${-max_w}+1},100%,1,1,0,0,0.5 b $3 n 0,255 a z endl
22422    l[1--1] r {${-max_w}+1},100%,1,1,0,0,0.5 a z endl
22423  endl
22424  w={-2,w} h={-2,h}
22425
22426  # Transform selected images to ascii art.
22427  repeat $!-2 l[$>,-2,-1]
22428    luminance[0] n[0] 0,255
22429    nw={0,round(w/$w,1,1)}
22430    nh={0,round(h/$h,1,1)}
22431    if $> list_wh=$list_wh,$nw,$nh else list_wh=$nw,$nh fi
22432
22433    s[0] y,-$h s[0--3] x,-$w r[0--3] $w,$h,1,1,0,0
22434    repeat $!-2 l[$>,-2,-1]
22435      rprogress {$>*100/($!-2)}
22436      ri[0] [1] -[0] [1] sqr[0] r[0] 1,1,100%,1,2 y[0]
22437      C={0,ym} rm[0]
22438      +slices[1] $C mv. 0
22439      if narg("$5") +f[0] $C a[0,-1] c fi
22440    endl done
22441    append_tiles[0--2] $nw,$nh
22442
22443    if narg("$5") s[0] c l[1]  # Export as text file.
22444       r $nw,$nh,1,1,1
22445       ('"$1"') map[0] . k[0]
22446       s y i[1-$!] ('\n')
22447       a x
22448       if $is_multi filename=${filename\ "$5",$>} else filename="$5" fi
22449       ot $filename rm
22450    endl fi
22451
22452  endl done
22453  rm[-2,-1] u $list_wh
22454
22455#@cli imagegrid : M>0,_N>0
22456#@cli : Create MxN image grid from selected images.
22457#@cli : Default value: 'N=M'.
22458#@cli : $ image.jpg imagegrid 16
22459imagegrid : skip ${2=$1}
22460  e[^-1] "Create $1x$2 image grid from image$?."
22461  repeat $! l[$>]
22462    ({w},{h}) ($1,$2) /[-2,-1] round. 1 r.. {^},..,..,2 rm.
22463    ({w},{h}) ($1,$2) *[-2,-1] r.. {^},..,..,2 rm.
22464    $1,$2,1,.,1 shift. 1,1 ri. ..,0,2 *
22465  endl done
22466
22467#@cli imagegrid_hexagonal : _resolution>0,0<=_outline<=1
22468#@cli : Create hexagonal grids from selected images.
22469#@cli : Default values: 'resolution=32', 'outline=0.1' and 'is_antialiased=1'.
22470#@cli : $ image.jpg imagegrid_hexagonal 24
22471imagegrid_hexagonal : check "isint(${1=32}) && $1>0 && ${2=0.1}>=0 && $2<=1"
22472  e[^-1] "Create hexagonal grid(s) from image$?, with resolution $1 and outline $2."
22473  repeat $! l[$>]
22474
22475    # Generate hexagonal grid.
22476    l[]
22477      # Generate hexagon.
22478      ({'CImg3d'},6,6)
22479      (0;{2*pi}) -. {pi/2} r. 1,7,1,1,3 +sin. cos.. a[-2,-1] x rows. 0,5 z. 0,2
22480      2,6,1,1,3,0 1,100%,1,1,y ++. 1 %. 6 rv[-2,-1] a[-3--1] x
22481      3,100%,1,1,1 1,100%,1,1,1 y a y
22482      *3d. {1-$2}
22483
22484      # Generate minimal pattern (2x2 hexagons).
22485      ++3d {sqrt(3)} ++3d {sqrt(3)/2},1.5
22486      col3d... 2 col3d.. 3 col3d. 4 +3d
22487      /3d 1.5
22488    endl
22489
22490    # Duplicate it to get a grid with correct size.
22491    ny={1+round(0.5*$1,1,1)}
22492    nx={0,1+round($1*w/h*3/(sqrt(3)*4),1,1)}
22493    array3d. $nx,$ny,1,{4*sqrt(3)/3},2
22494    c3d. *3d. {0,h/$1}
22495
22496    # Fill grid with image colors.
22497    [0],[0] j3d. ..,50%,50%,0,1,2,0,0 rm..
22498    blend shapeaverage0
22499
22500  endl done
22501
22502#@cli imagegrid_triangular : pattern_width>=1,_pattern_height>=1,_pattern_type,0<=_outline_opacity<=1,\
22503# _outline_color1,...
22504#@cli : Create triangular grids from selected images.
22505#@cli : 'pattern type' can be { 0=horizontal | 1=vertical | 2=crossed | 3=cube | 4=decreasing | 5=increasing }.
22506#@cli : Default values: 'pattern_width=24', 'pattern_height=pattern_width', 'pattern_type=0', 'outline_opacity=0.1' \
22507# and 'outline_color1=0'.
22508#@cli : $ image.jpg imagegrid_triangular 6,10,3,0.5
22509imagegrid_triangular : check "$1>=1 && ${2=$1}>=1 && isint(${3=0}) && $3>=0 && $3<=5" skip ${4=0},${5=0}
22510  s0="horizontal" s1="vertical" s2="crossed" s3="cube"
22511  e[^-1] "Create triangular grid(s) from image$?, with pattern width $1, height $2, pattern type '"${s$3}"', "\
22512          "outline opacity $4 and outline color (${5--1})."
22513
22514  # Create triangular patterns and outlines (always square!).
22515  M={max($1,$2)}
22516  if $3==4" || "$3==5 # Decreasing/Increasing.
22517    $M,$M,1,1,x>y ++. 2 a[-2,-1] x ++. 4 a[-2,-1] y
22518    $M,$M,1,1,"!x || !y || x==y" r. 200%,200%,1,1,0,2
22519    a[-2,-1] c
22520    if $3==5 mirror. y fi
22521  elif $3==3 # Cube.
22522    $M,$M,1,1,x>y 100%,100%,1,1,w-1-x>=y a[-2,-1] x ++. 2 mirror. y a[-2,-1] y
22523    ++. 4 =. 4,50%,50% =.. 2 a[-2,-1] x label. 0,0
22524    (2,2,2,0,1,2,1,1,3,3,3,1,1,0) map.. . rm.
22525    100%,100%,1,1
22526    line. 0,0,{$M-1},{$M-1},1,1 line. {$M-1},$M,0,100%,1,1
22527    line. {$M-1},{$M-1},{3*$M-1},{$M-1},1,1 line. {2*$M},0,0,0,1,1
22528    line. {2*$M},0,100%,100%,1,1 line. {2*$M},100%,100%,0,1,1
22529    a[-2,-1] c
22530  elif $3==2 # Horizontal + vertical.
22531    $M,$M,1,1,x>y ++. 2 mirror. x a[-2,-1] x ++. 4 mirror. y a[-2,-1] y
22532    100%,100%,1,1,"!x || !y || x==int(w/2) || y==int(h/2) || x==y || w-1-x==y"
22533    a[-2,-1] c
22534  elif $3==1 # Vertical.
22535    $M,$M,1,1,x>y 100%,100%,1,1,w-1-x<=y a[-2,-1] y ++. 2 mirror. x a[-2,-1] x
22536    100%,100%,1,1,"!x || x==int(w/2) || x==y || w-1-x==y"
22537    a[-2,-1] c
22538  else # Horizontal.
22539    $M,$M,1,1,x>y 100%,100%,1,1,w-1-x>=y a[-2,-1] x ++. 2 mirror. y a[-2,-1] y
22540    100%,100%,1,1,"!y || y==int(h/2) || x==y || w-1-x==y"
22541    a[-2,-1] c
22542  fi
22543
22544  # Apply grid on images.
22545  repeat $!-1
22546    wh={$>,w},{$>,h}
22547    if $1>$2 r[$>] 100%,{$>,$1*h/$2} elif $1<$2 r[$>] {$>,$2*w/$1} fi
22548    +r. [$>],[$>],1,2,0,2,0.5,0.5
22549    s. c
22550    blend[$>,-2] shapeaverage
22551    +fc[$>] ${5--1} j[$>] .,0,0,0,0,$4,.. rm[-2,-1]
22552    r[$>] $wh,1,100%,2
22553  done
22554  rm.
22555
22556#@cli linearize_tiles : M>0,_N>0
22557#@cli : Linearize MxN tiles on selected images.
22558#@cli : Default value: 'N=M'.
22559#@cli : $ image.jpg +linearize_tiles 16
22560linearize_tiles : check "$1>0 && ${2=$1}>0"
22561  e[^-1] "Linearize $1x$2 tiles on image$?."
22562  repeat $! l[$>] nm={0,n}
22563    s={s} split_tiles $1,$2 s c # Split as tiles for all channels.
22564    repeat $! l[$>]
22565      wh={w},{h}
22566      +f x +f. y +f. 1 y a[^0] x solve.. . rm.
22567      $wh,1,1,{@0}"*x + "{@1}"*y + "{@2} rm..
22568    endl done
22569    repeat int($!/$s) a[-$s--1] c mv. 0 done append_tiles $1,$2
22570  nm $nm endl done
22571
22572#@cli map_sprites : _nb_sprites>=1,_allow_rotation={ 0=none | 1=90 deg. | 2=180 deg. }
22573#@cli : Map set of sprites (defined as the 'nb_sprites' latest images of the selection) to other selected images,
22574#@cli : according to the luminosity of their pixel values.
22575#@cli : $ image.jpg resize2dy 48 repeat 16 ball {8+2*$>},${-rgb} mul[-1] {(1+$>)/16} done map_sprites 16
22576map_sprites : check "isint($1) && $1>0 && isint(${2=0}) && $2>=0 && $2<=2"
22577  e[^-1] "Map set of $1 sprites to image selection$?."
22578  norm[0--{$1+1}] quantize[0--{$1+1}] $1,0,1
22579  slices[-$1--1] 0 r[-$1--1] ${max_wh[-$1--1]},1,100%,0,0,0.5,0.5
22580  if $2==1
22581    N={4*$1}
22582    repeat $!-$1 *[$>] 4 +rand[$>] 0,3 round. +[$>,-1] done
22583    repeat $1 l[{1+$<}] +mirror xy +rotate 90 endl done
22584  elif $2==2
22585    N={2*$1}
22586    repeat $!-$1 *[$>] 2 +rand[$>] 0,1 round. +[$>,-1] done
22587    repeat $1 l[{1+$<}] +mirror xy endl done
22588  else N=$1 fi
22589  r[-$N--1] 100%,100%,1,${max_s[-$N--1]} w={w} h={h} a[-$N--1] x
22590  r[^-1] ${w}00%,${h}00%,1,1 *[^-1] $w
22591  (0,{$w-1};0,{$w-1}^0,0;{$h-1},{$h-1}) r. $w,$h,1,2,3 round.
22592  repeat $!-2 +r. [$>],[$>],1,2,0,2 r[$>] 100%,100%,1,2,0 +[$>,-1] +warp.. [$>],0,0 rv[$>,-1] rm. done rm[-2,-1]
22593
22594#@cli pack : is_ratio_constraint={ 0 | 1 },_sort_criterion
22595#@cli : Pack selected images into a single image.
22596#@cli : The returned status contains the list of new (x,y) offsets for each input image.
22597#@cli : Parameter 'is_ratio_constraint' tells if the resulting image must tend to a square image.
22598#@cli : Default values: 'is_ratio_constraint=0' and 'sort_criterion=max(w,h)'.
22599#@cli : $ image.jpg repeat 10 +resize2dx[-1] 75% balance_gamma[-1] ${-rgb} done pack 0
22600pack : skip ${1=0},${2=max(w,h)}
22601  e[^-1] "Pack image$? into a single image."
22602  if $!<2 return fi
22603  if ${-max_d}>1 error[0--3] "Command '$0': Selected images contain at least one volumetric image (depth>1).
22604                             Should all be 2D." fi
22605  nm={0,n} to_colormode 0
22606
22607  # Sort images by decreasing size.
22608  repeat $! nm$>={$>,n} nm[$>] ${nm$>}:$> done
22609  m "_pack : ('{n}') l. s +,{':'} u {t} rm endl"
22610  if ['$2']=='n' sort_list +,n else sort_list -,"$2" fi
22611
22612  # Start packing
22613  offsets${-_pack[0]}=0,0
22614  N=$!
22615  i[0] 0 # List of empty slots.
22616
22617  do l[0,1,2]
22618    w1,h1,w2,h2={[w#1,h#1,w#2,h#2]}
22619
22620    # Search an empty slot that fits.
22621    slot,min_slot_area=-1,inf
22622    repeat h#0
22623      x,y,w,h={0,crop(0,$>,4,1)}
22624      slot_area={$w*$h}
22625      if $w>=$w2" && "$h>=$h2" && "$slot_area<=$min_slot_area # Found a fit.
22626        slot,min_slot_area=$>,$slot_area
22627      fi
22628    done
22629
22630    if $slot>=0 # Empty slot found -> Use it.
22631      x,y,w,h={0,crop(0,$slot,4,1)}
22632      j[1] [2],$x,$y offsets${-_pack[2]}=$x,$y
22633      l[0]
22634        s y rm[$slot]
22635        area1={max(($w-$w2)*$h,$w2*($h-$h2))}
22636        area2={max(($w-$w2)*$h2,$w*($h-$h2))}
22637        if $area1>=$area2 # Split - type1
22638          if $w2<$w i[$slot] ({$x+$w2},$y,{$w-$w2},$h) fi
22639          if $h2<$h i[$slot] ($x,{$y+$h2},$w2,{$h-$h2}) fi
22640        else # Split - type 2
22641          if $w2<$w i[$slot] ({$x+$w2},$y,{$w-$w2},$h2) fi
22642          if $h2<$h i[$slot] ($x,{$y+$h2},$w,{$h-$h2}) fi
22643        fi
22644        a y if !$! 0 fi
22645      endl
22646      rm[2]
22647
22648    else # Empty slot not found -> Append horizontally or vertically.
22649      if $1
22650        metric_h={abs($w1+$w2-max($h1,$h2))}
22651        metric_v={abs($h1+$h2-max($w1,$w2))}
22652      else
22653        metric_h={if($h2<$h1,$w2*($h1-$h2),$w1*($h2-$h1))}
22654        metric_v={if($w2<$w1,($w1-$w2)*$h2,($w2-$w1)*$h1)}
22655      fi
22656
22657      if $metric_h<=$metric_v # Append horizontally.
22658        offsets${-_pack[2]}=$w1,0
22659        a[1,2] x,0
22660        if $h2<$h1 ($w1,$h2,$w2,{$h1-$h2}) a[0,-1] y
22661        elif $h2>$h1 (0,$h1,$w1,{$h2-$h1}) a[0,-1] y
22662        fi
22663      else # Append vertically.
22664        offsets${-_pack[2]}=0,$h1
22665        a[1,2] y,0
22666        if $w2<$w1 ($w2,$h1,{$w1-$w2},$h2) a[0,-1] y
22667        elif $w2>$w1 ($w1,0,{$w2-$w1},$h1) a[0,-1] y
22668        fi
22669      fi
22670    fi
22671
22672  endl while $!>2
22673  rm[0]
22674
22675  # Return offsets.
22676  status=
22677  repeat $N if narg($status) status=$status,${offsets$>} else status=${offsets$>} fi done
22678  nm $nm u $status
22679  um _pack
22680
22681#@cli puzzle : _width>0,_height>0,_M>=1,_N>=1,_curvature,_centering,_connectors_variability,_resolution>=1
22682#@cli : Input puzzle binary mask with specified size and geometry.
22683#@cli : Default values: 'width=height=512', 'M=N=5', 'curvature=0.5', 'centering=0.5', 'connectors_variability=0.5' \
22684# and 'resolution=64'.
22685#@cli : $ puzzle ,
22686puzzle : check "isint(${1=512}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=5}) && $3>0 &&
22687                isint(${4=$3}) && $4>0 && isint(${8=64}) && $8>0"
22688         skip ${5=0.5},${6=0.5},${7=0.5}
22689  e[^-1] "Draw $3x$4 puzzle pattern on image$?, with curvature $5, centering $6, connectors variability $7
22690          and resolution $8."
22691  l[]
22692    if $4>=2 _puzzle[] $3,{$4-1},${5-8} +3d. 0,1 fi
22693    if $3>=2 _puzzle[] $4,{$3-1},${5-8} r3d. 0,0,1,-90 +3d. 1,$4 fi
22694    *3d {$1/$3},{$2/$4} quadrangle3d 0,0,0,{$1-1},0,0,{$1-1},{$2-1},0,0,{$2-1},0 p3d. 1 +3d col3d 1
22695    $1,$2 j3d. ..,0,0,0,1,1,0,0 rm..
22696  endl
22697
22698_puzzle :
22699  R={$6*$1}
22700  repeat $2
22701    ({'CImg3d'},$R,{$R-1})
22702    repeat $1
22703      sign={if(u<=0.5,-1,1)}
22704      center={$4*u(-0.25,0.25)}
22705      knob={$5*u(-0.05,0.12)}
22706      ($>,0;\
22707       {0.2+$center+$>},{-$sign*$3*0.1};\
22708       {0.4+$center+$>},0;\
22709       {0.35+$center+$>},{0.1*$sign};\
22710       {0.45+$center+$>},{(0.15+$knob)*$sign};\
22711       {0.55+$center+$>},{(0.15+$knob)*$sign};\
22712       {0.65+$center+$>},{0.1*$sign};\
22713       {0.6+$center+$>},0;\
22714       {0.8+$center+$>},{-$sign*$3*0.1})
22715    done
22716    ($1,0) a[-{$1+1}--1] y r. 2,$R,1,1,5 z. 0,2
22717    (2,0,1;2,{$R-2},{$R-1}) r. 3,{$R-1},1,1,3 round.
22718    3,{h},1,1,255 1,{h},1,1,255 y[-5--1] y a[-5--1] y +3d. 0,$>
22719  done +3d
22720
22721#@cli quadratize_tiles : M>0,_N>0
22722#@cli : Quadratize MxN tiles on selected images.
22723#@cli : Default value: 'N=M'.
22724#@cli : $ image.jpg +quadratize_tiles 16
22725quadratize_tiles : check "$1>0 && ${2=$1}>0"
22726  e[^-1] "Quadratize $1x$2 tiles on image$?."
22727  repeat $! l[$>] nm={0,n}
22728    s={s} split_tiles $1,$2 s c # Split as tiles for all channels.
22729    repeat $! l[$>]
22730      wh={w},{h}
22731      +f x^2 +f. y^2 +f. x*y +f. x +f. y +f. 1 y a[^0] x
22732      solve.. . rm.
22733      $wh,1,1,{@0}"*x^2 + "{@1}"*y^2 + "{@2}"*x*y +"{@3}"*x + "{@4}"*y + "{@5} rm..
22734    endl done
22735    repeat int($!/$s) a[-$s--1] c mv. 0 done append_tiles $1,$2
22736  nm $nm endl done
22737
22738#@cli rotate_tiles : angle,_M>0,N>0
22739#@cli : Apply MxN tiled-rotation effect on selected images.
22740#@cli : Default values: 'M=8' and 'N=M'.
22741#@cli : $ image.jpg to_rgba rotate_tiles 10,8 drop_shadow 10,10 display_rgba
22742rotate_tiles : skip ${2=8},${3=$2}
22743  e[^-1] "Apply $2x$3 tiled-rotation effect on image$?, with angle $1 deg."
22744  split_tiles $2,$3,1 rotate $1 append_tiles $2,$3
22745
22746#@cli shift_tiles : M>0,_N>0,_amplitude
22747#@cli : Apply MxN tiled-shift effect on selected images.
22748#@cli : Default values: 'N=M' and 'amplitude=20'.
22749#@cli : $ image.jpg +shift_tiles 8,8,10
22750shift_tiles : check "${2=$1}>=0" skip ${3=20}
22751  e[^-1] "Apply $1x$2 tiled-shift effect on image$?, with amplitude $3."
22752  repeat $! l[$>]
22753    $1,$2,1,2 noise. $3 r. ..,..,1,2 warp.. .,1,1,0 rm.
22754  endl done
22755
22756#@cli taquin : M>0,_N>0,_remove_tile={ 0=none | 1=first | 2=last | 3=random },_relief,_border_thickness[%],\
22757# _border_outline[%],_outline_color
22758#@cli : Create MxN taquin puzzle from selected images.
22759#@cli : Default value: 'N=M', 'relief=50', 'border_thickness=5', 'border_outline=0' and 'remove_tile=0'.
22760#@cli : $ image.jpg +taquin 8
22761taquin : check "isint($1) && $1>0 & isint(${2=$1}) && $2>0"
22762         skip ${3=0},${4=50},${5=5%},${6=0},${7=0},${8=$7},${9=$8},${10=255}
22763 e[^-1] "Create $1x$2 taquin puzzle from image$?, with relief $4, border thickness $5, border outline $6 and
22764         outline color (${7--1})."
22765 repeat $! l[$>] nm={0,n}
22766   split_tiles $1,$2 r ${-min_wh},100%,100%,0
22767   100%,100%,1,1,1
22768   if ${"is_percent $5"} rectangle. {100*$5/2}%,{100*$5/2}%,{100-50*$5}%,{100-50*$5}%,1,0
22769   else rectangle. $5,$5,{w-1-$5},{h-1-$5},1,0 fi
22770   *. '1-2*(x/w<y/h)' *. $4
22771   repeat $!-1 l[$>,-1] split_opacity[0] +[0] . a[^-1] c endl done rm. c 0,255
22772   frame $6,$6,${7-10}
22773   if $3==3 f. 0 fi
22774   repeat $! mv[$>] {u($!)} done
22775   if $3==1 f[0] 0 elif $3==2 f. 0 fi
22776   append_tiles $1,$2
22777 nm $nm endl done
22778
22779#@cli tunnel : _level>=0,_factor>0,_centering_x,_centering_y,_opacity,_angle
22780#@cli : Apply tunnel effect on selected images.
22781#@cli : Default values: 'level=9', 'factor=80%', 'centering_x=centering_y=0.5', 'opacity=1' and 'angle=0'
22782#@cli : $ image.jpg tunnel 20
22783tunnel : check "${1=9}>=0 && ${2=80%}>0" skip ${3=0.5},${4=0.5},${5=0.1},${6=0}
22784  e[^-1] "Apply tunnel effect on image$?, with depth $1, factor $2, centering ($3,$4), opacity $5 and angle $6."
22785  repeat $! l[$>]
22786    repeat $1 +r. $2,$2,1,100%,5
22787    if $6 100%,100%,1,1,1 rotate[-2,-1] $6,1,0 erode. 3 j... ..,{({-3,w}-w)*$3},{({-3,h}-h)*$4},0,0,$5,. rm[-2,-1]
22788    else j.. .,{({-2,w}-w)*$3},{({-2,h}-h)*$4},0,0,$5 rm. fi
22789    done
22790  endl done c 0,255
22791
22792#-----------------------------
22793#
22794#@cli :: Artistic
22795#
22796#-----------------------------
22797
22798#@cli boxfitting : _min_box_size>=1,_max_box_size>=0,_initial_density>=0,_nb_attempts>=1
22799#@cli : Apply box fitting effect on selected images, as displayed the web page:
22800#@cli : <http://www.complexification.net/gallery/machines/boxFittingImg/>.
22801#@cli : Default values: 'min_box_size=1', 'max_box_size=0', 'initial_density=0.1' and 'nb_attempts=3'.
22802#@cli : $ image.jpg boxfitting ,
22803boxfitting : check "isint(${1=3}) && $1>=1 && isint(${2=0}) && $2>=0 && ${3=0.1}>=0 && isint(${4=3}) && $4>=1"
22804  e[^-1] "Apply box fitting effect on image$?, with box sizes ($1,$2), density $3 and $4 attempts."
22805  min_size=$1
22806  max_size={if($2,$2,max(w,h))}
22807  repeat $! l[$>]
22808    nb_attempts=0 prec=5
22809    100%,100%
22810    repeat 1e8
22811
22812      # Add random non-intersecting squares with min size.
22813      if $><1 # Takes random points for the first iteration.
22814        100%,100% noise. {max(1e-3,$3)},2 ==. 1
22815      else # Then, try to take points near the median axis of the distance function otherwise.
22816        +distance. 1 +rand. 0,1 *[-2,-1] max_patch. {round($prec*$min_size)}
22817        prec={max(1,$prec*0.9)}
22818      fi
22819
22820      # Discard new squares that intersect something.
22821      dilate. $min_size area_fg. 0,1 ==. {($min_size)^2}
22822      +dilate.. 3 ==. 0 *[-2,-1] area_fg. 0,1 ==. {($min_size)^2}
22823      if !iM nb_attempts+=1 if $nb_attempts>$4 rm. break fi # If no new squares have been placed.
22824      else nb_attempts=0 fi
22825      +[-2,-1]
22826
22827      # Make current squares grown until max square size is reached.
22828      repeat int(($max_size-$min_size)/2)
22829        +dilate. 3 area_fg. 0,1 ==. {($min_size+2*$>+2)^2}
22830        if !iM rm. break fi  # No more squares to grow.
22831        -|[-2,-1]
22832      done
22833
22834    done
22835    blend shapeaverage0
22836  endl done
22837
22838#@cli brushify : [brush],_brush_nb_sizes>=1,0<=_brush_min_size_factor<=1,_brush_nb_orientations>=1,\
22839# _brush_light_type,0<=_brush_light_strength<=1,_brush_opacity,_painting_density[%]>=0,\
22840# 0<=_painting_contours_coherence<=1,0<=_painting_orientation_coherence<=1,_painting_coherence_alpha[%]>=0,\
22841# _painting_coherence_sigma[%]>=0,_painting_primary_angle,0<=_painting_angle_dispersion<=1
22842#@cli : Apply specified brush to create painterly versions of specified images.
22843#@cli : 'brush_light_type' can be { 0=none | 1=flat | 2=darken | 3=lighten | 4=full }.
22844#@cli : Default values: 'brush_nb_sizes=3', 'brush_min_size_factor=0.66', 'brush_nb_orientations=12', \
22845# 'brush_light_type=0', 'brush_light_strength=0.25', 'brush_opacity=0.8', 'painting_density=20%', \
22846# 'painting_contours_coherence=0.9', 'painting_orientation_coherence=0.9', 'painting_coherence_alpha=1', \
22847# 'painting_coherence_sigma=1', 'painting_primary_angle=0', 'painting_angle_dispersion=0.2'
22848#@cli : $ image.jpg 40,40 gaussian[-1] 10,4 spread[-1] 10,0 brushify[0] [1],1
22849brushify : check ${"is_image_arg $1"}" &&"\         # $1: [brush]
22850                    "isint(${2=4}) && $2>=1 &&"\      # $2: brush_nb_sizes
22851                    "${3=0.25}>=0 && $3<=1 &&"\       # $3: brush_min_size_factor
22852                    "isint(${4=12}) && $4>=1 &&"\     # $4: brush_nb_orientations
22853                    "isint(${5=4}) && $5>=0 &&"\      # $5: brush_light_type
22854                    "${6=0.07}>=0 && $6<=1 &&"\       # $6: brush_light_strength
22855                    "isnum(${7=0.75}) &&"\         # $7: brush_opacity
22856                    "${8=40%}>=0 && $8>=0 &&"\        # $8: painting_density[%]
22857                    "${9=0.7}>=0 && $9<=1 &&"\        # $9: painting_contours_coherence
22858                    "${10=1}>=0 && $10<=1 &&"\        # $10: painting_orientation_coherence
22859                    "${11=1}>=0 && ${12=0.5%}>=0 &&"\ # $11 and $12: painting_coherence_alpha and sigma
22860                    "isnum(${13=45}) &&"\          # $13: painting_primary_angle
22861                    "${14=0.2}>=0 && $14<=1"          # $14: painting_angle_dispersion
22862  e[^-1] "Brushify image$?, with brush $1."
22863
22864  # Precompute the set of oriented/resized brushes.
22865  pass$1 0 l.
22866    slices 0 max 1e-8 norm n 0,1 threshold 0.1,1 autocrop.
22867    repeat $4 +rotate[0] {360*$>/$4} done
22868    rm[0] n 0,1 threshold 0.1,1
22869    autocrop r ${-max_wh},1,1,0,0,0.5,0.5
22870    a z nm brush
22871    wb={w} hb={h} whb={wh} ls={255*$6}
22872    if $5==0 +f. 0
22873    elif $5==1 +n. -$ls,0
22874    elif $5==2 +g xy +[-2,-1] min. 0 n. -$ls,0
22875    elif $5==3 +g xy +[-2,-1] max. 0 n. 0,$ls
22876    else +g xy +[-2,-1] n. -$ls,$ls
22877    fi
22878    nm. brushlight
22879
22880    repeat $2-1
22881      ratio={v=(1+$>)/max(1,$2-1);100*((1-v)+$3*v)}%
22882      +r[brush,brushlight] $ratio,$ratio,100%,1,2
22883      ri[-2,-1] [brush],0,0,0.5,0.5
22884    done
22885    a[^:2] z a[^0] z
22886  endl
22887
22888  # Generate images with brushes.
22889  repeat $!-2 l[$>,brush,brushlight]
22890    s={0,s} nm={0,n} to_rgb[0] nm[0] img
22891
22892    # Generate set of random points with orientations.
22893    +diffusiontensors[img] $9,$10,$11,$12 nm. geometry
22894    +channels[geometry] 0 sh[geometry] 2 +[-2,-1] ^. 0.3 quantize. $2,0 *. -1 +. $2 -. 1 nm. contours
22895    1,{img,max(1,${"is_percent $8"}?wh*$8:$8)} rand. 0,{img,w-1} +rand. 0,{img,h-1} a[-2,-1] c nm. pts
22896    +to_rgba[img] nm. res
22897
22898    # Render filter.
22899    f[pts] "*
22900      begin(
22901        S2 = round(0.5*["$wb","$hb"]);
22902        brush_r = brush_g = brush_b = brush_a = vector"$whb"(255);
22903        ang = $13*pi/180;
22904        cu = [ cos(ang),sin(ang) ];
22905        cv = [ -cu[1],cu[0] ];
22906        T = mul(cu,cu,2) + $14*mul(cv,cv,2);
22907      );
22908      P = I;
22909      G = I(#"$geometry",P);
22910      ang = u(pi);
22911      V = [ G[0],G[1],G[1],G[2] ]*(T*[ cos(ang),sin(ang) ]);
22912      amp = i(#"$contours",P); #cut($2-round(5*$2*sqrt(G[0] + G[2])),0,$2-1);
22913      ang = round(((atan2(V[1],V[0])%(2*pi))*$4/(2*pi)))%$4;
22914      col = I(#"$img",P);
22915      ind = amp*$4 + ang;
22916      ref(crop(#"$brush",0,0,ind,0,"$wb","$hb",1,1),brush);
22917      ref(crop(#"$brushlight",0,0,ind,0,"$wb","$hb",1,1),brushlight);
22918      brush_r = cut(col[0] + brushlight,0,255);
22919      brush_g = cut(col[1] + brushlight,0,255);
22920      brush_b = cut(col[2] + brushlight,0,255);
22921      draw(#"$res",[brush_r,brush_g,brush_b,brush_a],P - S2,"$wb","$hb",1,4,$7,brush,1);
22922      P"
22923    k[res,brush,brushlight] mv[res] 0 nm[0] $nm to_colormode[0] {$s+($s%2)}
22924  endl done rm[brush,brushlight]
22925
22926#@cli cartoon : _smoothness,_sharpening,_threshold>=0,_thickness>=0,_color>=0,quantization>0
22927#@cli : Apply cartoon effect on selected images.
22928#@cli : Default values: 'smoothness=3', 'sharpening=150', 'threshold=20', 'thickness=0.25', 'color=1.5' \
22929# and 'quantization=8'.
22930#@cli : $ image.jpg cartoon 3,50,10,0.25,3,16
22931cartoon : skip ${1=3},${2=150},${3=20},${4=0.25},${5=1.5},${6=8}
22932  e[^-1] "Apply cartoon effect on image$?, with smoothness $1, sharpening $2, threshold $3, thickness $4, color $5
22933          and quantization $6."
22934  repeat $! l[$>] split_opacity l[0] to_rgb
22935  b $1 sharpen $2,1 c 0,255 n 0,255
22936  if $4 +edges $3 b. $4 >=. 0.9 else 100%,100%,1,1,1 fi
22937  rgb2lab.. s.. c *[-3,-2] $5 a[-4--2] c lab2rgb..
22938  quantize.. $6,1,-1
22939  n.. 0,255 *
22940  endl a c endl done
22941
22942#@cli color_ellipses : _count>0,_radius>=0,_opacity>=0
22943#@cli : Add random color ellipses to selected images.
22944#@cli : Default values: 'count=400', 'radius=5' and 'opacity=0.1'.
22945#@cli : $ image.jpg +color_ellipses ,,0.15
22946color_ellipses : skip ${1=1400},${2=5},${3=0.1}
22947  e[^-1] "Add $1 random color ellipses to image$?, with maximum radius $2 and opacity $1."
22948  repeat $1
22949    ellipse {u(0,100)}%,{u(0,100)}%,{u(0,$2)}%,{u(0,$2)}%,{u(0,360)},$3,{u(60,255)},{u(60,255)},{u(60,255)},255
22950  done
22951
22952#@cli cubism : _density>=0,0<=_thickness<=50,_max_angle,_opacity,_smoothness>=0
22953#@cli : Apply cubism effect on selected images.
22954#@cli : Default values: 'density=50', 'thickness=10', 'max_angle=75', 'opacity=0.7' and 'smoothness=0'.
22955#@cli : $ image.jpg cubism ,
22956cubism : check "${1=50}>=0 && ${2=10}>=0 && $2<=50 && ${5=0}>=0" skip ${3=75},${4=0.7}
22957  e[^-1] "Apply cubism effect on image$?, with density $1, thickness $2, maximum angle $3 deg., opacity $4 and
22958          smoothness $5."
22959  if "!$1 || !$2 || !$3 || !$4" return fi
22960  repeat $! l[$>]
22961    w={w} h={h} s={s}
22962    P={round($2*max(w,h)/200)}
22963    N={round(1.5*$1*w*h/(4*$P)/100)}
22964
22965    # Define Header + nb vertices / primitives.
22966    ('CImg3d') +. 0.5 ({4*$N};$N)
22967
22968    # Generate list of random points.
22969    1,$N rand. $P,{$w-1-$P} +rand. $P,{$h-1-$P} a[-2,-1] x round.
22970
22971    # Generate list of primitives.
22972    ++. '-$P,-$P' ++.. '$P,-$P' ++... '$P,$P' ++[-4] '-$P,$P'
22973    a[-4--1] x i.. (12,0,1,2,3;12,{4*($N-1)},{4*($N-1)+1},{4*($N-1)+2},{4*($N-1)+3})
22974    r.. 5,$N,1,1,3 round.. 1 a[-2,-1] x
22975
22976    # Generate list of vertices.
22977    1,$N rand. {225-$3},{225+$3} *. {pi/180}
22978    +sin. cos.. +*. -1 ... a[-4--3] x a[-2,-1] x z[-4,-2,-1] 0,2 +*[-2,-1] -1 a[-4--1] x *. {sqrt(2)*$P}
22979    r... 400%,100%,1,1,0,2 +[-3,-1]
22980
22981    # Generate materials.
22982    (-128;$w;$h;$s) +b[0] $5
22983    if $N>1 4,{$N-1},1,1,-128,0,0,0 fi
22984    1,$N,1,1,1
22985
22986    # Apply effect on current image.
22987    y[1--1] a[1--1] y rv3d.
22988    if $4>=1 j3d[0] [1],0,0,0,1,2,0,0 rm[1]
22989    else +j3d[0] [1],0,0,0,1,2,0,0 rm[1] blend alpha,$4
22990    fi
22991
22992  endl done
22993
22994#@cli draw_whirl : _amplitude>=0
22995#@cli : Apply whirl drawing effect on selected images.
22996#@cli : Default value: 'amplitude=100'.
22997#@cli : $ image.jpg draw_whirl ,
22998draw_whirl : skip ${1=100}
22999  e[^-1] "Apply whirl drawing effect on image$? with amplitude $1."
23000  repeat $! l[$>]
23001    100%,100% noise. 70,2 ==. 1 *. 255 ri. .. &[-1,-2] smooth. $1,0,1,2,2
23002    sqrt. n. 0,255 equalize.
23003  endl done
23004
23005#@cli drawing : _amplitude>=0
23006#@cli : Apply drawing effect on selected images.
23007#@cli : Default value: 'amplitude=200'.
23008#@cli : $ image.jpg +drawing ,
23009drawing : skip ${1=200}
23010  e[^-1] "Apply drawing effect on image$? with amplitude $1."
23011  repeat $! l[$>] split_opacity l[0] to_rgb
23012    smooth $1,0.2,1,3,3 b 2 sharpen 1000 [0]
23013    r[0] 20,20,1,3,2 equalize[0] index[1] [0],1,1
23014    nm[1] {0,n},1 rm[0]
23015  endl a c endl done
23016
23017#@cli drop_shadow : _offset_x[%],_offset_y[%],_smoothness[%]>=0,0<=_curvature<=1,_expand_size={ 0 | 1 }
23018#@cli : Drop shadow behind selected images.
23019#@cli : Default values: 'offset_x=20', 'offset_y=offset_x', 'smoothness=5', 'curvature=0' and 'expand_size=1'.
23020#@cli : $ image.jpg drop_shadow 10,20,5,0.5 expand_xy 20,0 display_rgba
23021drop_shadow : check "${3=5}>=0 && ${4=0}>=0 && $4<=1" skip ${1=20},${2=$1},${5=1}
23022  e[^-1] "Drop shadow behind image$?, with offsets ($1,$2), smoothness $3 and curvature $4."
23023  to_a repeat $! l[$>]
23024    nm={0,n}
23025    dx={if(${is_percent\ $1},w*$1,$1)}
23026    dy={if(${is_percent\ $2},h*$2,$2)}
23027    sigma={if(${is_percent\ $3},max(w,h)*$3,$3)}
23028    w={w} h={h} s={s} +channels 100% coords=${autocrop_coords.\ 0} rm. z $coords  # Crop part with opaque pixels.
23029    r {w+abs($dx)},{h+abs($dy)},1,100%,0,0,{if($dx>0,0,1)},{if($dy>0,0,1)}
23030    r. {w+4*$sigma},{h+4*$sigma},1,100%,0,0,0.5,0.5
23031    +channels. 100%
23032    if !$4 shift. $dx,$dy # Flat shadow.
23033    else # Curved shadow.
23034      (0;{pi}) ri. ..,3 sin. *. -$4 +. 1 *. $dx
23035      (0,{pi}) ri. ..,3 sin. *. -$4 +. 1 *. $dy
23036      a[-2,-1] c warp.. .,1,0,0 rm.
23037    fi
23038    b. $sigma,0
23039    r. 100%,100%,1,2,0,0,0,0,0,1 mv. 0 blend alpha
23040    +channels. 100% >=. 1 * autocrop 0
23041    if !$5 $w,$h,1,$s j. ..,{arg(1,$coords)},{arg(2,$coords)} rm.. fi
23042    nm $nm
23043  endl done
23044
23045#@cli ellipsionism : _R>0[%],_r>0[%],_smoothness>=0[%],_opacity,_outline>0,_density>0
23046#@cli : Apply ellipsionism filter to selected images.
23047#@cli : Default values: 'R=10', 'r=3', 'smoothness=1%', 'opacity=0.7', 'outline=8' and 'density=0.6'.
23048#@cli : $ image.jpg ellipsionism ,
23049ellipsionism : check "${1=10}>0 && ${2=3}>0 && ${5=8}>0 && ${6=0.6}>0" skip ${3=1%},${4=0.7}
23050  e[^-1] "Apply ellipsionism filter to image$?, with radii ($1,$2), smoothness $3, opacity $4 and outline $5."
23051  repeat $! l[$>] to_color
23052
23053    # Compute contour angle.
23054    +luminance g. xy a[-2,-1] c b. $3 orientation.
23055    sh. 0 sh.. 1 atan2. .. *. {180/pi} +. 90 rm[-2,-1] channels. 1,1
23056
23057    # Render ellipses.
23058    100%,100%,1,4
23059    eval "
23060      const interpolation = 1;
23061      const N = $6*wh/max($1,$2);
23062      repeat (N,n,
23063        x = round(u(w-1)); y = round(u(h-1));
23064        ellipse(x,y,$1,$2,i(#-2,x,y)°,$4,I(#0,x,y),255);
23065        ellipse(x,y,-$1,-$2,i(#-2,x,y)°,$4,0xFFFFFFFF,I(#0,x,y)/$5,255);
23066      )"
23067    rm.. blend alpha
23068  endl done
23069
23070#@cli fire_edges : _edges>=0,0<=_attenuation<=1,_smoothness>=0,_threshold>=0,_nb_frames>0,_starting_frame>=0,\
23071# frame_skip>=0
23072#@cli : Generate fire effect from edges of selected images.
23073#@cli : Default values: 'edges=0.7', 'attenuation=0.25', 'smoothness=0.5', 'threshold=25', 'nb_frames=1', \
23074# 'starting_frame=20' and 'frame_skip=0'.
23075#@cli : $ image.jpg fire_edges ,
23076fire_edges : check "${1=0.7}>=0 && ${2=0.25}>=0 && $2<=1 && ${3=0.5}>=0 && ${4=25}>=0 && ${5=1}>0 &&
23077                    ${6=20}>=0 && ${7=0}>=0"
23078  e[^-1] "Generate fire effect from edges of image$?, with edges $1, attenuation $2, smoothness $3, threshold $4,
23079          $5 frames, starting frame $6 and frame skip $7."
23080  repeat $! l[$>] nm={0,n}
23081    norm +gradient_norm n. 0,1 roundify. $1 f[0] 0
23082    (0,0,0;0,0,0;1,1,1;0,1,0) *. {(1-$2^4)/4}
23083    repeat $5*(1+$7)+$6
23084      {0,w},{0,h} rand. 0,255 *. [1]
23085      b. $3
23086      if $4 >=. $4% else equalize. fi
23087      n. 0,255
23088      j[0] .,0,0,0,0,1,[1],1 rm.
23089      correlate[0] [2]
23090      if $>>=$6" && "($>-$6)%($7+1)==0 [0] fi
23091    done rm[0-2]
23092  nm $nm endl done
23093  (0,255,255,255,255^0,0,255,255,255^0,0,0,128,255) r. 256,1,1,3,3
23094  map[^-1] . rm.
23095
23096#@cli fractalize : 0<=detail_level<=1
23097#@cli : Randomly fractalize selected images.
23098#@cli : Default value: 'detail_level=0.8'
23099#@cli : $ image.jpg fractalize ,
23100fractalize : check "${1=0.8}>=0 && $1<=1"
23101  e[^-1] "Randomly fractalize image$?, with detail level $1."
23102  xc=0.4433
23103  yc=0.2645
23104  delta=0.1
23105  c0r=0.317
23106  c0i=0.03
23107  repeat $! l[$>] nm={0,n}
23108    luminance equalize 256 b 0.25% n 0,255
23109    100%,100%
23110    dx={$delta*w/max(w,h)}
23111    dy={$delta*h/max(w,h)}
23112    x0={$xc-$dx/2}
23113    y0={$yc-$dy/2}
23114    x1={$xc+$dx/2}
23115    y1={$yc+$dy/2}
23116    mandelbrot. $x0,$y0,$x1,$y1,256,1,$c0r,$c0i
23117    +==. 0 inpaint.. . rm.
23118    n. 0,256
23119    16,1,1,3 rand. 0,255 r. 256,1,1,3,3 map.. . rm.
23120    s. c
23121    i[2,3] [0]
23122    s={0.1*(1-$1)}
23123    parallel "register_nonrigid[1] [0],"$s",5","register_nonrigid[3] [2],"$s",5","register_nonrigid[5] [4],"$s",5"
23124    rm[0,2,4] a c nm $nm
23125  endl done
23126
23127#@cli glow : _amplitude>=0
23128#@cli : Add soft glow on selected images.
23129#@cli : Default value: 'amplitude=1%'.
23130#@cli : $ image.jpg glow ,
23131glow : skip ${1=1%}
23132  e[^-1] "Add soft glow on image$?, with amplitude $1."
23133  repeat $! l[$>] split_opacity +b[0] $1 n. [0] blend_edges[0,-1] 1 a c endl done
23134
23135#@cli halftone : nb_levels>=2,_size_dark>=2,_size_bright>=2,_shape={ 0=square | 1=diamond | 2=circle | \
23136# 3=inv-square | 4=inv-diamond | 5=inv-circle },_smoothness[%]>=0
23137#@cli : Apply halftone dithering to selected images.
23138#@cli : Default values: 'nb_levels=5', 'size_dark=8', 'size_bright=8', 'shape=5' and 'smoothnesss=0'.
23139#@cli : $ image.jpg halftone ,
23140halftone : check "${1=5}>=2 && ${2=8}>=2 && ${3=8}>=2 && ${5=0}>=0" skip ${4=5}
23141  s0="square" s1="diamond" s2="circle" s3="inv-square" s4="inv-diamond" s5="inv-circle"
23142  e[^-1] "Apply halftone dithering to image$?, with $1 levels, dark size $3, bright size $4, "\
23143         ${s$4}" shape and smoothness $5."
23144  repeat $! l[$>] s c repeat $! l[$>]
23145    (0,255) a y quantize $1,0 rows 0,{h-2}
23146    repeat $1
23147      s={round(($2*$<+$3*$>)/($1-1))}
23148      $s,$s =. 1,50%,50% distance. 1,{$4%3} +shift. {round(w/2)},{round(h/2)},0,0,2 min[-2,-1]
23149      if $4>=3 <. {100*$</($1-1.1)}% *. {-255} +. {255-$>}
23150      else <. {100*$>/($1-1.1)}% *. 255 -. $>
23151      fi
23152      ri. ..,0,2 b. $5 +==.. $> *[-2,-1] +[-2,-1]
23153    done endl done a c
23154  endl done
23155
23156#@cli hardsketchbw : _amplitude>=0,_density>=0,_opacity,0<=_edge_threshold<=100,_is_fast={ 0 | 1 }
23157#@cli : Apply hard B&W sketch effect on selected images.
23158#@cli : Default values: 'amplitude=1000', 'sampling=3', 'opacity=0.1', 'edge_threshold=20' and 'is_fast=0'.
23159#@cli : $ image.jpg +hardsketchbw 200,70,0.1,10 median[-1] 2 +local reverse blur[-1] 3 blend[-2,-1] overlay endlocal
23160hardsketchbw : skip ${1=300},${2=50},${3=0.1},${4=20},${5=0}
23161  e[^-1] "Apply hard B&W sketch effect on image$?, with amplitude $1, density $2, opacity $3 and edge threshold $4."
23162  if !$2 channels 0 f 255 return fi
23163  luminance n 0,1
23164
23165  if $5 # Fast version.
23166    repeat $! l[$>] nm={0,n}
23167      g xy rv *.. -1 a c
23168      if $4 +norm >=. $4% * fi
23169      100%,100%,1,1,255 quiver. ..,{max(1,10-$2/6)},$1,0,$3 rm..
23170    nm $nm endl done
23171
23172  else # Slower version.
23173    repeat $! l[$>] nm={0,n}
23174
23175      # Isolate starting points and tangents.
23176      w={w} h={h} g xy rv *.. -1 a c * $1
23177      +norm >=. $4% 100%,100% noise. $2,2 ==. 1 *[-2,-1]
23178
23179      # Retrieve points coordinates and corresponding tangents.
23180      pointcloud3d. s3d. rm[-6--5,-3--1] r. 3,{h/3},1,1,-1 s. x rm. a[-2,-1] c
23181      warp.. .,0,0 +-. .. +[-3,-2] s[-2,-1] c
23182
23183      # Convert as a 3d object and render on white background.
23184      i... 1,{h} 1,{h} a[-6--1] x
23185      i.. ('CImg3d') i.. ({2*h},{h})
23186      1,{h},1,1,2 1,{h},1,1,2*y ++. 1 a[-3--1] x
23187      3,{h} 1,{h},1,1,$3 y[-6--1] a[-6--1] y
23188      $w,$h,1,1,255 j3d. ..,0,0,0,1,1,0,0 rm..
23189    nm $nm endl done
23190  fi
23191
23192#@cli hearts : _density>=0
23193#@cli : Apply heart effect on selected images.
23194#@cli : Default value: 'density=10'.
23195#@cli : $ image.jpg hearts ,
23196hearts : skip ${1=10}
23197  e[^-1] "Apply heart filter on image$?, with density $1."
23198  repeat $! l[$>]
23199    100%,100%,1 noise. $1,2 ==. 1 ri. .. n. 0,1 *[-1,-2] _heart9x7
23200    dilate.. . rm.
23201  endl done
23202
23203_heart9x7 :
23204  (9,7,1,1,0,1,-1,2,-3,2,-1,4,-1,13,-1,7,-3,5,-5,3,-7,1,-4)
23205  decompress_rle.
23206
23207#@cli houghsketchbw : _density>=0,_radius>0,0<=_threshold<=100,0<=_opacity<=1,_votesize[%]>0
23208#@cli : Apply hough B&W sketch effect on selected images.
23209#@cli : Default values: 'density=100', 'radius=3', 'threshold=100', 'opacity=0.1' and 'votesize=100%'.
23210#@cli : $ image.jpg +houghsketchbw ,
23211houghsketchbw : check "${1=100}>=0 && ${2=3}>=0 && ${3=100}>=0 && $3<=100 && ${4=0.1}>=0 && $4<=1 && ${5=100%}>0"
23212  e[^-1] "Apply hough B&W sketch effect on image$?, with density $1, radius $2, threshold $3, opacity $4
23213          and votesize $5."
23214  luminance repeat $! l[$>] nm={0,n}
23215
23216    # Compute normalized Hough transform.
23217    res={round(if(${is_percent\ $5},$5*max(w,h),$5))} w={w} h={h} rhomax={sqrt(w^2+h^2)/2}
23218    hough $res,$res n 0,255
23219
23220    # Retrieve coordinates of maximas in hough space.
23221    normalize_local. $1,$2 >=. $3% pointcloud3d.
23222    s3d. rm[-6--5,-3--1] r. 3,{h/3},1,1,-1 columns. 0,1
23223
23224    # Convert to (x0,y0)-(x1,y1) line coordinates.
23225    s. x,2
23226    *.. {2*pi/$res}                 # theta
23227    *. {$rhomax/$res}               # rho
23228    +cos.. *. .. +. {$w/2}          # x
23229    +sin... *. ... +. {$h/2}        # y
23230    rm...                           # Remove rho
23231    i... ...
23232    cos[-4] sin... *[-4,-3] 10000   # cos(t) sin(t)
23233    ++.. ...                        # x + sin(t)
23234    +-.. [-5]                       # y - cos(t)
23235    -[-4] [-5]                      # x - sin(t)
23236    +... [-6]                       # y + cos(t)
23237    rm[-6,-5]
23238
23239    # Transform as a 3D object.
23240    i... 1,{h} 1,{h} a[-6--1] x                 # Vertices
23241    i.. ('CImg3d') i.. ({2*h},{h})              # Header and size.
23242    1,{h},1,1,2 1,{h},1,1,2*y ++. 1 a[-3--1] x  # Primitives.
23243    3,{h},1,1,0 1,{h},1,1,$4                    # Colors and opacities
23244    y[-6--1] a[-6--1] y
23245
23246    # Render on a white image.
23247    $w,$h,1,1,255 j3d. ..,0,0,0,1,1,0,0 rm..
23248  nm $nm endl done
23249
23250#@cli lightrays : 100<=_density<=0,_center_x[%],_center_y[%],_ray_length>=0,_ray_attenuation>=0
23251#@cli : Generate ray lights from the edges of selected images.
23252#@cli : Default values: 'density=50%', 'center_x=50%', 'center_y=50%', 'ray_length=0.9' and 'ray_attenuation=0.5'.
23253#@cli : $ image.jpg +lightrays , + cut 0,255
23254lightrays : check "${1=50}>=0 && $1<=100 && ${4=1}>=0 && ${5=1}>=0" skip ${2=50%},${3=50%}
23255  e[^-1] "Generate ray lights from image$?, with density $1, center point ($2,$3), ray length $4 and attenuation $5."
23256  repeat $! l[$>]
23257    gradient_norm >= $1% euclidean2polar $2,$3
23258    repeat log2(w) +shift. {2^$>} +[-2,-1] done
23259    function1d 0.5,0,1,{$4*w},1,{1+($4+1-$5)*w},0 r. {-2,w},1,1,1,0
23260    (1,{w}) r. {-2,w},1,1,1,3 /[-2,-1]
23261    ri. .. *[-2,-1] polar2euclidean $2,$3 n 0,255
23262  endl done
23263
23264#@cli light_relief : _ambient_light,_specular_lightness,_specular_size,_darkness,_light_smoothness,_xl,_yl,_zl,\
23265# _zscale,_opacity_is_heightmap={ 0 | 1 }
23266#@cli : Apply relief light to selected images.
23267#@cli : Default values(s) : 'ambient_light=0.3', 'specular_lightness=0.5', 'specular_size=0.2', 'darkness=0', \
23268# 'xl=0.2', 'yl=zl=0.5',
23269#@cli : 'zscale=1', 'opacity=1' and 'opacity_is_heightmap=0'.
23270#@cli : $ image.jpg blur 2 light_relief 0.3,4,0.1,0
23271light_relief : skip ${1=0.3},${2=0.5},${3=0.2},${4=0},${5=0.2},${6=0.5},${7=0.5},${8=1},${9=1},${10=0}
23272  e[^-1] "Apply relief light to image$?."
23273  repeat $! l[$>]
23274    ({-$6},{1-$6};{-$6},{1-$6}^{-$7},{-$7};{1-$7},{1-$7}^$8,$8;$8,$8) r. ..,..,1,3,3  # Create light vector field.
23275    if $10 +channels.. 3 to_rgb... else +to_rgb.. norm. fi
23276    b. $5% g. xy 100%,100%,1,1,$9 a[-3--1] c                                          # Create normal vector field.
23277    orientation[-2,-1] *[-2,-1] s. c +[-3--1]                                         # Normalized scalar product.
23278    100%,100% =. 1,{$6*100}%,{$7*100}% distance. 1 sqr. *. -1                         # Compute specular attenuation.
23279    /. {($3*max(w,h))^2} exp. *. $2 +. $1
23280    *[-2,-1] -. $4 *. {-2,iM}
23281    split_opacity[0] +[0,-1] a c c 0,255
23282  endl done
23283
23284#@cli linify : 0<=_density<=100,_spreading>=0,_resolution[%]>0,_line_opacity>=0,_line_precision>0,\
23285# _mode={ 0=subtractive | 1=additive }
23286#@cli : Apply linify effect on selected images.
23287#@cli : The algorithm is inspired from the one described on the webpage <http://linify.me/about>.
23288#@cli : Default values: 'density=50', 'spreading=2', 'resolution=40%', 'line_opacity=10', 'line_precision=24' \
23289# and 'mode=0'.
23290#@cli : $ image.jpg linify 60
23291linify : check "${1=40}>=0 && $1<=100 && ${2=2}>=0 && ${3=40%}>0 && ${4=10}>=0 && isint(${5=24}) && $5>0 &&
23292                isbool(${6=0})"
23293  e[^-1] "Apply linify effect on image$?, with density $1, spreading $2, resolution $3, line opacity $4,
23294          line precision $5 and "${"-arg 1+$6,subtractive,additive"}" mode."
23295  repeat $! l[$>] remove_opacity nm={n}
23296    100%,100%,1,{s},$6?0:255
23297    if {0,w>h} r2dx[0] {${"-is_percent $3"}?max(1,$3*w):min(w,$3)}
23298    else r2dy[0] {${"-is_percent $3"}?max(1,$3*h):min(h,$3)}
23299    fi
23300    n[0] 0,100
23301    if narg($_debug)" && "!{*,w} w[] ${-fitscreen[]\ {1,[w,h]}} fi
23302    eval "
23303      is_in(ind,P) = (P[0]>=0 && P[0]<w#ind && P[1]>=0 && P[1]<h#ind);
23304      const add = $6;
23305      const density = add?100 - $1:$1;
23306      const spreading = max(0.1,$2);
23307      const opacity = $4;
23308      const precision = $5;
23309      const om2add = 1 - 2*add;
23310
23311      fact = [ w/w#0, h/h#0,1,1 ];
23312      nb_lines = 0;
23313      ref0 = add?iM#0:im#0;
23314      do (
23315        S = stats(#0);
23316        P0 = add?[ S[8],S[9],0,S[11] ]:[ S[4],S[5],0,S[7] ]; # coords of min or max intensity
23317        ref = S[add];
23318        best_ang = best_avg = add?0:inf;
23319        repeat (precision,k,
23320          ang = u(360)*pi/180;
23321          dP = [ cos(ang), sin(ang),0,0 ];
23322          dP/=max(abs(dP));
23323          N = avg = 0;
23324          P = P0; while (is_in(#0,P), avg+=i(#0,P); ++N; P+=dP);
23325          P = P0; while (is_in(#0,P), avg+=i(#0,P); ++N; P-=dP);
23326          avg/=N;
23327          if (add?(avg>best_avg):(avg<best_avg), best_avg = avg; best_ang = ang);
23328        );
23329        dP = [ cos(best_ang), sin(best_ang),0,0 ];
23330        dP/=max(abs(dP));
23331        P = P0; while (is_in(#0,P), i(#0,P)+=om2add*spreading; P+=dP);
23332        P = P0; while (is_in(#0,P), i(#0,P)+=om2add*spreading; P-=dP);
23333        P = P0*fact; while (is_in(#1,P), i(#1,P)-=om2add*opacity*spreading; P+=dP);
23334        P = P0*fact; while (is_in(#1,P), i(#1,P)-=om2add*opacity*spreading; P-=dP);
23335        if (!(nb_lines%250),
23336          progress = density==ref0?100:round(100*(ref - ref0)/(density - ref0));
23337          run('progress ',vtos(progress));
23338          if (narg("$_debug"),
23339            run('+c[1] 0,255 r. {*,w},{*,h},1,3,2 to. ',vtos(progress),'%,1%,1%,5%,1 w. -1,-1,0 rm.')
23340          );
23341        );
23342        ++nb_lines;
23343        breakpoint();
23344      _(while), add?(ref>density):(ref<density))"
23345    k. c 0,255 nm $nm
23346  endl done
23347
23348#@cli mosaic : 0<=_density<=100
23349#@cli : Create random mosaic from selected images.
23350#@cli : Default values: 'density=30'.
23351#@cli : $ image.jpg mosaic , +fill "I!=J(1) || I!=J(0,1)?[0,0,0]:I"
23352mosaic : check "${1=30}>=0"
23353  e[^-1] "Apply mosaic effect on image$?, with density $1."
23354  repeat $! l[$>]
23355    100%,100%,1,2,'u<0.25*($1%)^4?[u,1]' s. c
23356    distance. 1 *. -1 watershed.. . rm.
23357    blend shapeaverage
23358  endl done
23359
23360#@cli old_photo
23361#@cli : Apply old photo effect on selected images.
23362#@cli : $ image.jpg old_photo
23363old_photo :
23364  e[^-1] "Apply old photo effect on image$?."
23365  noise 20 bilateral 30,60 b 2 sharpen 100 frame_fuzzy 8%,8%,6,3 to_rgb shadow_patch 0.75 n 0,255 sepia
23366
23367#@cli pencilbw : _size>=0,_amplitude>=0
23368#@cli : Apply B&W pencil effect on selected images.
23369#@cli : Default values: 'size=0.3' and 'amplitude=60'.
23370#@cli : $ image.jpg pencilbw ,
23371pencilbw : skip ${1=0.3},${2=60}
23372  e[^-1] "Apply B&W pencil effect on image$?, with size $1 and amplitude $2."
23373  repeat $! l[$>] split_opacity l[0]
23374    norm b $1 sharpen 4000 smooth $2,0,1 equalize sqrt n 0,255
23375  endl a c endl done
23376
23377#@cli pixelsort : _ordering={ + | - },_axis={ x | y | z | xy | yx },_[sorting_criterion],_[mask]
23378#@cli : Apply a 'pixel sorting' algorithm on selected images, as described in the page :
23379#@cli : <http://satyarth.me/articles/pixel-sorting/>.
23380#@cli : Default values: 'ordering=+', 'axis=x' and 'sorting_criterion=mask=(undefined)'.
23381#@cli : $ image.jpg +norm +ge[-1] 30% +pixelsort[0] +,y,[1],[2]
23382pixelsort : check "(str1='${1=+}'; str1=='+' || str1=='-') && "\
23383                  "(str2='${2=x}'; str2=='x' || str2=='y' || str2=='z' || str2=='xy' || str2=='yx') && "\
23384                  "('${3=}'==0 || "${"is_image_arg $3"}") && "\
23385                  "('${4=}'==0 || "${"is_image_arg $4"}")"
23386  s0="descending" s1="ascending"
23387  if '$3'!=0" && "'$4'!=0
23388    e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2,
23389            with sorting criterion $3 and mask $4."
23390  elif '$3'!=0" && "'$4'==0
23391    e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2,
23392            with sorting criterion $3."
23393  elif '$3'==0" && "'$4'!=0
23394    e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2,
23395            with mask $4."
23396  else
23397    e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2."
23398  fi
23399  repeat $!
23400    if '$3'!=0 pass$3 0 else +compose_channels[$>] + fi
23401    if '$4'!=0 pass$4 0 else [$>],[$>],[$>],1,1 fi
23402    l[$>,-2,-1]
23403      nm={0,n} >=. 50% mv.. 0 a c
23404      order={`";'$1'=='+'?'<':'>'"`}
23405      if '$2'=='x';
23406        _pixelsort $order channels 1,{s-2}
23407      elif '$2'=='y';
23408        permute yxzc _pixelsort $order channels 1,{s-2} permute yxzc
23409      elif '$2'=='z';
23410        permute zxyc _pixelsort $order channels 1,{s-2} permute yzxc
23411      elif '$2'=='xy';
23412        _pixelsort $order permute yxzc _pixelsort $order channels 1,{s-2} permute yxzc
23413      elif '$2'=='yx';
23414        permute yxzc _pixelsort $order permute yxzc _pixelsort $order channels 1,{s-2}
23415      fi
23416      nm $nm
23417    endl
23418  done
23419
23420_pixelsort :
23421  1,{h},{d}
23422  f. ":
23423    quicksort(x0,x1,y,z) = (
23424      stack = vector"{0,2*w}"();
23425      stacksize = 0;
23426      push(elt0,elt1) = (stack[stacksize++] = elt0; stack[stacksize++] = elt1);
23427      pop() = (_s1 = stack[--stacksize]; _s0 = stack[--stacksize]; [_s0,_s1]);
23428      swap(a,b) = (_tmp = a; a = b; b = _tmp);
23429      push(x0,x1);
23430      while (stacksize>0,
23431        range = pop();
23432        lo = range[0];
23433        hi = range[1];
23434        pivot = i(#0,int((lo + hi)/2),y,z,0);
23435        while (lo<=hi,
23436           while (i(#0,lo,y,z,0)$1pivot, ++lo);
23437           while (pivot$1i(#0,hi,y,z,0), --hi);
23438           lo<=hi?(lo!=hi?(swap(I(#0,lo,y,z),I(#0,hi,y,z))); ++lo; --hi);
23439        );
23440        range[0]<hi?push(range[0],hi);
23441        lo<range[1]?push(lo,range[1]);
23442      )
23443    );
23444    s1 = s#0 - 1;
23445    repeat (w#0,x0,
23446      if (i(#0,x0,y,z,s1),
23447        for (x1 = x0, x1<w#0 && i(#0,x1,y,z,s1)==1, ++x1);
23448        quicksort(x0,min(x1,w#0-1),y,z);
23449        x0 = ++x1;
23450      )
23451    )"
23452  rm.
23453
23454#@cli polaroid : _size1>=0,_size2>=0
23455#@cli : Create polaroid effect in selected images.
23456#@cli : Default values: 'size1=10' and 'size2=20'.
23457#@cli : $ image.jpg to_rgba polaroid 5,30 rotate 20 drop_shadow , drgba
23458polaroid : check "${1=10}>=0 && ${2=20}>=0"
23459  e[^-1] "Create polaroid effect in image$?, with borders sizes $1 and $2."
23460  - 255 r {100+$1}%,{100+$1}%,1,100%,0,0,0.5,0.5 r 100%,{100+$2}%,1,100%,0,0,0 + 255
23461
23462#@cli polygonize : _warp_amplitude>=0,_smoothness[%]>=0,_min_area[%]>=0,_resolution_x[%]>0,_resolution_y[%]>0
23463#@cli : Apply polygon effect on selected images.
23464#@cli : Default values: 'warp_amplitude=300', 'smoothness=2%', 'min_area=0.1%', 'resolution_x=resolution_y=10%'.
23465#@cli : $ image.jpg image.jpg polygonize 100,10 +fill "I!=J(1) || I!=J(0,1)?[0,0,0]:I"
23466polygonize : check "${1=300}>=0 && ${2=2%}>=0 && ${3=0.1%}>=0 && ${4=10%}>0 && ${5=$4}>0"
23467  e[^-1] "Polygonize image$? with warp amplitude $1, smoothness $2, minimal area $3 and resolutions ($4,$5)."
23468   repeat $! l[$>]
23469    +b $2 gradient_norm. g. a[-2,-1] c channels. 0,2 *. {1/0.1+max(abs(im),abs(iM))}
23470    resx={max(1,round(if(${is_percent\ $4},w*$4,w/$4)-1))}
23471    resy={max(1,round(if(${is_percent\ $5},h*$5,h/$5)-1))}
23472    plane3d 1,1,$resx,$resy *3d. {0,w-1},{0,h-1},1
23473    s3d. rm.. i.. (0;{h-1}) r.. 3,{h},1,1,3 round.. y..
23474    [-4] a[-7--2] y r. 3,{h/3},1,1,-1 z. 0,1 permute. yzcx
23475    repeat $1 +warp[1] .,0,0 +[-2,-1] done
23476    permute. cxyz z. 0,2 y. j[2] .,0,8 rm[-3,-1]
23477    [0],[0] j3d. [1],0,0,0,1,2 rm[1]
23478    if $3>0
23479      min_area={0,if(${is_percent\ $3},$3*w*h,$3)}
23480      +area. 0,1 >=. $min_area +.. 1 *.. . distance. 1 *. -1 watershed.. . rm.
23481    fi
23482    blend shapeaverage
23483  endl done
23484
23485#@cli poster_edges : 0<=_edge_threshold<=100,0<=_edge_shade<=100,_edge_thickness>=0,_edge_antialiasing>=0,\
23486# 0<=_posterization_level<=15,_posterization_antialiasing>=0
23487#@cli : Apply poster edges effect on selected images.
23488#@cli : Default values: 'edge_threshold=40', 'edge_shade=5', 'edge_thickness=0.5', 'edge_antialiasing=10', \
23489# 'posterization_level=12' and 'posterization_antialiasing=0'.
23490#@cli : $ image.jpg poster_edges ,
23491poster_edges : check "${1=40}>=0 && $1<=100 && ${2=5}>=0 && $2<=100 && ${3=0.5}>=0 && ${4=10}>=0 &&
23492                      ${5=12}>=0 && $5<=15 && ${6=0}>=0"
23493  e[^-1] "Apply poster edge on image$?, with edge threshold $1, edge shade $2, edge thickness $3,
23494          edge antialiasing $4, $5 level of posterization and posterization antialiasing $6."
23495  repeat $! l[$>] split_opacity l[0]
23496    +g xy,1 a[-2,-1] c norm. b. $3 n. 0,255
23497    apply_curve. 1,0,1,{max(0,(100-($1%)^0.1*100)*255%)},0.99,{min(255,(101-($1%)^0.1*100+$2)*255%)},0.01,255,0 c. 0,1
23498    if $4 smooth. {min(50,$4)},0,1,{$4/40},{$4/40},0.8,90 fi
23499    if $5 autoindex[0] {round((4-sqrt($5+1))*32+2)} fi
23500    if $6 smooth[0] {min(50,$6)},0,1,{$6/40},{$6/40},0.8,90 fi
23501    *
23502  endl a c endl done
23503
23504#@cli poster_hope : _smoothness>=0
23505#@cli : Apply Hope stencil poster effect on selected images.
23506#@cli : Default value: 'smoothness=3'.
23507#@cli : $ image.jpg poster_hope ,
23508poster_hope : check "${1=3}>=0"
23509  e[^-1] "Apply Hope stencil poster effect on image$?, with smoothness $1."
23510  repeat $! l[$>] to_rgb
23511    apc "smooth 200,0,1,$1,1"
23512    quantize 7,0 f 'if(i!=5,i,i+1-2*(y%2))'
23513    (0,32,47;0,32,47;209,1,23;209,1,23;90,141,145;-1,-1,-1;253,221,138) permute. yzcx
23514    map[0] [1] rm[1]
23515  endl done
23516
23517#@cli rodilius : 0<=_amplitude<=100,_0<=thickness<=100,_sharpness>=0,_nb_orientations>0,_offset,\
23518# _color_mode={ 0=darker | 1=brighter }
23519#@cli : Apply rodilius (fractalius-like) filter on selected images.
23520#@cli : Default values: 'amplitude=10', 'thickness=10', 'sharpness=400', 'nb_orientations=7', 'offset=0' \
23521# and 'color_mode=1'.
23522#@cli : $ image.jpg rodilius 12,10,300,10 normalize_local 10,6
23523#@cli : $ image.jpg normalize_local 10,16 rodilius 10,4,400,16 smooth 60,0,1,1,4 normalize_local 10,16
23524rodilius : check "${1=10}>=0 && $1<=200 && ${2=10}>=0 && $2<=100 && ${3=400}>=0 && ${4=7}>0" skip ${5=0},${6=1}
23525  e[^-1] "Apply rodilius filter on image$? with amplitude $1, thickness $2, sharpness $3, $4 orientations,
23526          offset $5 and "${arg\ 1+!$6,brighter,darker}" color mode."
23527  repeat $! l[$>] split_opacity rv
23528    if !$6 negate. fi
23529    +f. 0 nm. {-2,n}
23530    repeat round($4)
23531      angle={$5+$>*180/round($4)}
23532      +blur_linear.. $1%,{$1*$2/100}%,$angle,1 b. 0.7 sharpen. $3 max[-2,-1]
23533    done rm..
23534    if !$6 negate. fi
23535  rv a c endl done
23536
23537#@cli sketchbw : _nb_angles>0,_start_angle,_angle_range>=0,_length>=0,_threshold>=0,_opacity,_bgfactor>=0,\
23538# _density>0,_sharpness>=0,_anisotropy>=0,_smoothness>=0,_coherence>=0,_is_boost={ 0 | 1 },_is_curved={ 0 | 1 }
23539#@cli : Apply sketch effect to selected images.
23540#@cli : Default values: 'nb_angles=2', 'start_angle=45', 'angle_range=180', 'length=30', 'threshold=3', \
23541# 'opacity=0.03', 'bgfactor=0', 'density=0.6', 'sharpness=0.1', 'anisotropy=0.6', 'smoothness=0.25', 'coherence=1', \
23542# 'is_boost=0' and 'is_curved=1'.
23543#@cli : $ image.jpg +sketchbw 1 reverse blur[-1] 3 blend[-2,-1] overlay
23544sketchbw :
23545  check "${1=2}>0 && ${3=180}>=0 && ${4=30}>=0 && ${5=3}>=0 && ${7=0}>=0 && ${8=0.6}>0 && ${9=0.1}>=0 &&
23546         ${10=0.6}>=0 && ${11=0.25}>=0 && ${12=1}>=0"
23547  skip ${2=45},${6=0.03},${13=0},${14=0}
23548  e[^-1] "Apply B&W sketch effect on image$?."
23549  nb_angles,start_angle,angle_range,length,threshold,opacity,bgfactor,density,sharpness,\
23550  anisotropy,smoothness,coherence,is_boost,is_curved=${1-14}
23551  length={max($length,1)}
23552  repeat $! l[$>]
23553    {0,[w,h,1,1,0]}                                                    # [1] = canvas to draw onto
23554    +gradient_norm[0] sqrt.
23555    diffusiontensors[0] $sharpness,$anisotropy,$smoothness,$coherence
23556    a[0,-1] c                                                          # [0] = field of stroke tensors + gradient norm
23557    1,{$density*wh/sqrt($length)},1,2,round(u([w#0,h#0]-1))            # [2] = set of random points
23558
23559    repeat $nb_angles
23560
23561      # Compute vector field for considered orientation.
23562      [0],[0],1,2,"
23563        const angle = ("$start_angle" + "$>"*"$angle_range"/"$nb_angles")*pi/180;
23564        const ca = cos(angle);
23565        const sa = sin(angle);
23566        T = I(#0);
23567        U = [ T[0]*ca + T[1]*sa, T[1]*ca + T[2]*sa ];
23568        if ("$is_boost",U/=(1e-8 + norm(U)));
23569        U"
23570
23571      # Draw curved or straight strokes.
23572      if $is_curved
23573        f[2] "*
23574          oub = ovb = ouf = ovf = 0;
23575          oixb = xb = xf = i0;
23576          oiyb = yb = yf = i1;
23577          oixf = oiyf = -1;
23578          op = "$opacity" * (i(#0,xf,yf,0,3)<"$threshold"?"$bgfactor":1);
23579          omop = 1 - op;
23580
23581          if (op>0, repeat ("$length",dl,
23582
23583            # Forward
23584            ixf = round(xf);
23585            iyf = round(yf);
23586            if (ixf!=oixf || iyf!=oiyf, (i(#1,ixf,iyf)*=omop)+=op; oixf = ixf; oiyf = iyf);
23587            uf = i(#-1,xf,yf,0,0,1,1);
23588            vf = i(#-1,xf,yf,0,1,1,1);
23589            if (ouf*uf + ovf*vf<0, uf*=-1; vf*=-1);
23590            xf+=uf;
23591            yf+=vf;
23592            ouf = uf;
23593            ovf = vf;
23594
23595            # Backward
23596            ub = i(#-1,xb,yb,0,0,1,1);
23597            vb = i(#-1,xb,yb,0,1,1,1);
23598            if (oub*ub + ovb*vb<0, ub*=-1; vb*=-1);
23599            xb-=ub;
23600            yb-=vb;
23601            oub = ub;
23602            ovb = vb;
23603            ixb = round(xb);
23604            iyb = round(yb);
23605            if (ixb!=oixb || iyb!=oiyb, (i(#1,ixb,iyb)*=omop)+=op; oixb = ixb; oiyb = iyb);
23606          ));
23607          I"
23608      else
23609        f[2] "*
23610          const l = "$length";
23611          x = i0;
23612          y = i1;
23613          u = i(#-1,x,y,0,0);
23614          v = i(#-1,x,y,0,1);
23615          op = "$opacity" * (i(#0,x,y,0,3)<"$threshold"?"$bgfactor":1);
23616          omop = 1 - op;
23617          polygon(#1,2,x - l*u,y - l*v,x + l*u,y + l*v,op,1);
23618          I"
23619      fi
23620      rm.
23621    done
23622    k.. * -1 n 0,255
23623  endl done
23624
23625#@cli sponge : _size>0
23626#@cli : Apply sponge effect on selected images.
23627#@cli : Default value: 'size=13'.
23628#@cli : $ image.jpg sponge ,
23629sponge : skip ${1=13}
23630  e[^-1] "Apply sponge filter on image$?, with brush size $1."
23631  repeat $! l[$>]
23632    100%,100%,1,1 noise. 20,2 ==. 1 ri. .. n. 0,1 *[-1,-2]
23633    _circle $1 dilate.. . rm.
23634  endl done
23635
23636_circle :
23637  if $1%2==0 2,2 else 1 fi
23638  +. 1 r. $1,$1,1,1,0,0,0.5,0.5 distance. 1 n. 0,1 sqrt. c. 0.85,0.86 *. -1 n. 0,1
23639
23640#@cli stained_glass : _edges[%]>=0, shading>=0, is_thin_separators={ 0 | 1 }
23641#@cli : Generate stained glass from selected images.
23642#@cli : Default values: 'edges=40%', 'shading=0.2' and 'is_precise=0'.
23643#@cli : $ image.jpg stained_glass 20%,1 cut 0,20
23644stained_glass : check "${1=40%}>=0 && ${2=0.2}>=0" skip ${3=0}
23645  e[^-1] "Apply stained glass effect on image$?, with edges $1, shading $2 and thin-separators "\
23646         ${arg\ 1+!$3,enabled,disabled}"."
23647  repeat $! l[$>]
23648    im={im-1} - $im  # Ensure strict positiveness of image labels.
23649    +gradient_norm >=. $1 *.. .
23650    distance. 1 sharpen. 1e10 !=. 0
23651    if $3 skeleton. 0 fi
23652    distance. 1 watershed.. . +.. $im
23653    n. 0,1  ^. $2 *
23654  endl done
23655
23656#@cli stars : _density[%]>=0,_depth>=0,_size>0,_nb_branches>=1,0<=_thickness<=1,_smoothness[%]>=0,_R,_G,_B,_opacity
23657#@cli : Add random stars to selected images.
23658#@cli : Default values: 'density=10%', 'depth=1', 'size=32', 'nb_branches=5', 'thickness=0.38', 'smoothness=0.5', \
23659# 'R=G=B=200' and 'opacity=1'.
23660#@cli : $ image.jpg stars ,
23661stars : check "${1=10%}>=0 && ${2=1}>=0 && ${3=32}>0 && ${4=5}>=1 && ${5=0.38}>=0 && $5<=1 && ${6=0.5}>=0"
23662        skip ${7=200},${8=$7},${9=$8},${10=1}
23663  e[^-1] "Add $1 random stars to image$?, with depth $2, size $3, $4 branches, thickness $5, smoothness $6,
23664          color ($7,$8,$9) and opacity $10."
23665  if !$1 return fi
23666
23667  # Generate star sprites.
23668  star3d $4,$5 col3d. 255 *3d. $3
23669  l. repeat 4 {round(2*$3)},{round(2*$3)} j3d. [0],50%,50%,0,1,2,0,0 r3d[0] 0,0,1,-90 done rm[0] endl
23670  autocrop[-4--1] 0 r2dy[-4--1] $3 b[-4--1] $6,0 r[-4--1] 100%,100%,1,4
23671  repeat 4 sh[{-1-$>}] 0,2 fc. $7,$8,$9 rm. done
23672
23673  # Draw stars on selected images.
23674  repeat $!-1 [-4--1] l[$>,-4--1]
23675    N={round(if(${is_percent\ $1},w*h*$1,$1)/4,1,1)}
23676    repeat 4
23677      2,$N rand. -1,1 1,$N rand. 0,1 a[-2,-1] x
23678      i.. ('CImg3d') +.. 0.5 i.. ($N;$N)
23679      (1,0;1,{$N-1}) r. 2,$N,1,1,3 round. 4,$N,1,1,1 y[-5,-3--1] a[-5--1] y
23680      rv[-2,-1] sprites3d.. .,1 rm. *3d. {0.75*{0,w}},{0.75*{0,h}},{1000*$2}
23681      j3d[0] .,50%,50%,0,$10,0,0,0 rm.
23682    done
23683  endl done
23684  rm[-4--1]
23685
23686#@cli stencil : _radius[%]>=0,_smoothness>=0,_iterations>=0
23687#@cli : Apply stencil filter on selected images.
23688#@cli : Default values: 'radius=3', 'smoothness=1' and 'iterations=8'.
23689#@cli : $ image.jpg +norm stencil. 2,1,4 +mul rm[0]
23690stencil : check "${1=3}>=0 && ${2=1}>=0 && ${3=8}>=0"
23691  e[^-1] "Apply stencil filter on image$?, with radius $1, smoothness $2 and $3 iterations."
23692  n 0,1 repeat $3 b $1 unsharp {$1+$2},1000 c 0,255 done
23693
23694#@cli stencilbw : _edges>=0,_smoothness>=0
23695#@cli : Apply B&W stencil effect on selected images.
23696#@cli : Default values: 'edges=15' and 'smoothness=10'.
23697#@cli : $ image.jpg +stencilbw 40,4
23698stencilbw : skip ${1=15},${2=10}
23699  e[^-1] "Apply B&W stencil effect on image$?, with edges $1 and smoothness $2."
23700  repeat $! l[$>] split_opacity luminance[0] n[0] 0,255
23701    +edges[0] $1 quantize[0] 3,0,1 b[0] $2
23702    sharpen[0] 1000000 n[0] 0,1 *[0,-1] n[0] 0,255
23703  a c endl done
23704
23705#@cli stylize : [style_image],_fidelity_finest,_fidelity_coarsest,_fidelity_smoothness_finest>=0,\
23706# _fidelity_smoothnes_coarsest>=0,0<=_fidelity_chroma<=1,_init_type,_init_resolution>=0,init_max_gradient>=0,\
23707# _patchsize_analysis>0,_patchsize_synthesis>0,_patchsize_synthesis_final>0,_nb_matches_finest>=0,\
23708# _nb_matches_coarsest>=0,_penalize_repetitions>=0,_matching_precision>=0,_scale_factor>1,_skip_finest_scales>=0,\
23709# _"image_matching_command"
23710#@cli : Transfer colors and textures from specified style image to selected images, using a multi-scale \
23711# patch-mathing algorithm.
23712#@cli : If instant display window[0] is opened, the steps of the image synthesis are displayed on it.
23713#@cli : 'init_type' can be { 0=best-match | 1=identity | 2=randomized }.
23714#@cli : Default values: 'fidelity_finest=0.5', 'fidelity_coarsest=2', 'fidelity_smoothness_finest=3', \
23715# 'fidelity_smoothness_coarsest=0.5', 'fidelity_chroma=0.1', 'init_type=0', 'init_resolution=16', \
23716# 'init_max_gradient=0', 'patchsize_analysis=5', 'patchsize_synthesis=5', 'patchsize_synthesis_final=5', \
23717# 'nb_matches_finest=2', 'nb_matchesc_coarsest=30', 'penalize_repetitions=10', 'matching_precision=2', \
23718# 'scale_factor=1.85', 'skip_finest_scales=0' and \
23719# 'image_matching_command'="s c,-3 transfer_pca[0] [2] b[0,2] xy,0.7 n[0,2] 0,255 n[1,2] 0,200 a[0,1] c a[1,2] c"'.
23720stylize :
23721  check ${"is_image_arg $1"}" && isnum(${2=0.5}) && isnum(${3=2}) && ${4=3}>=0 && ${5=0.5}>=0 && ${6=0.1}>=0 && "\
23722        "$6<=1 && isint(${7=0}) && $7>=0 && $7<=3 && isint(${8=16}) && $8>=0 && ${9=0}>=0 && isint(${10=5}) && "\
23723        "$10>0 && isint(${11=5}) && $11>0 && isint(${12=$11}) && $12>0 && isint(${13=2}) && isint(${14=30}) && "\
23724        "${15=10}>=0 && ${16=2}>=0 && ${17=1.85}>1 && isint(${18=0})>=0"
23725  skip "${19=s c,-3 transfer_pca[0] [2] b[0,2] xy,0.7 n[0,2] 0,255 n[1,2] 0,200 a[0,1] c a[1,2] c}"
23726  e[^-1] "Stylize image$? with style image $1."
23727  fidelity_finest,\ # $2
23728  fidelity_coarsest,\ # $3
23729  fidelity_smoothness_finest,\ # $4
23730  fidelity_smoothness_coarsest,\ # $5
23731  fidelity_chroma,\ # $6
23732  init_type,\ # $7
23733  init_resolution,\ # $8
23734  init_max_gradient,\ # $9
23735  patchsize_analysis,\ # $10
23736  patchsize_synthesis,\ # $11
23737  patchsize_synthesis_final,\ # $12
23738  nb_matches_finest,\ # $13
23739  nb_matches_coarsest,\ # $14
23740  penalize_repetitions,\ # $15
23741  matching_precision,\ # $16
23742  scale_factor,\ # $17
23743  skip_finest_scales=${2-18} \ # $18
23744  m "stylize_match : $19"
23745
23746  init_resolution={max(2*$patchsize_analysis,$init_resolution)}
23747  mprec0={round(2+1.5*$matching_precision)}
23748  mprec1={1+round(4*$matching_precision)}
23749  is_window={*}
23750
23751  pass$1 repeat $!-1 l[$>,-1]
23752    to_colormode.. {s}
23753    nb_scales={1+round(log(min(w#0,h#0,w#1,h#1)/$init_resolution)/log($scale_factor),1,-1)}
23754    if {*} wsiz=${"fitscreen "{0,[w,h]}} w[0] $wsiz,0,"[G'MIC Stylize]" fi
23755
23756    repeat $nb_scales,scale
23757      size_factor={100/($scale_factor^$<)}
23758      if !$scale
23759
23760        # Initialization.
23761        +r[0,1] $size_factor%,$size_factor%,100%,100%,2 ws,hs={-2,[w,h]}
23762        +to_color[0,1] channels[-2,-1] 0,2 gradient_norm[-2,-1]
23763        r. [-3],[-3],[-3],1,2 r.. [-4],[-4],[-4],1,2
23764        a[-3,-1] c a[-3,-1] c # Append gradient information as last channel
23765        stylize_match[-2,-1]
23766        if $init_type==0
23767          +matchpatch.. .,3,3,1,{2*$mprec0},{2*$mprec1},$penalize_repetitions # Initial image match with 3x3 patches
23768        else
23769          ..,..,1,2,"round([x,y]*([w#-1,h#-1]-1)/([w,h]-1))" # Identity
23770          if d#-2>1 channels. 0,2 fi
23771          if $init_type==2 eval. ">P = u([w,h]-1); tmp = I(P); I(P) = I; I() = tmp" fi # Randomize
23772        fi
23773        rm[-3,-2]
23774        if $init_max_gradient>0 # Keep only high gradients of target image
23775          +gradient_norm[0] r. ..,..,1,1,2 gt. $init_max_gradient
23776          +.. 1 *[-2,-1] _inpaint_warping2d. --. 1
23777        fi
23778        if $is_window" && "!{*} break fi
23779
23780      else
23781
23782        # Upscale.
23783        factor={1-($scale-1)/max(1,$nb_scales-2)} # Linear scale factor from 1 to 0
23784        +r[0,1] $size_factor%,$size_factor%,100%,100%,2
23785        +to_color[-2,-1] channels[-2,-1] 0,2 gradient_norm[-2,-1]
23786
23787        a[-3,-1] c a[-3,-1] c # Append gradient information as last channel
23788        stylize_match[-2,-1] mv[-2,-1] -3
23789        sh. 0 *. {-3,w/$ws} rm. sh. 1 *. {-3,h/$hs} rm. round.
23790
23791        # Smart upscale of displacement field.
23792        +. 1 r. ...,...,1,100%,4 -. 1
23793        do
23794          f. "begin(const boundary = 1; nx = ny = vectors(); nx[0] = ny[1] = 1);
23795              i>=0?I:(
23796                j(-1)>=0?J(-1) + nx:
23797                j(0,-1)>=0?J(0,-1) + ny:
23798                j(1)>0?J(1) - nx:
23799                j(0,1)>0?J(0,1) - ny:I)"
23800        while im<0
23801        ws,hs={-2,[w,h]}
23802
23803        if $<<$skip_finest_scales rm[-3,-2] continue fi  # Skip finest scales
23804        +warp_patch.. .,$patchsize_synthesis,$patchsize_synthesis,1
23805        if {*} w. fi
23806
23807        # Inject gradients from target (in Lab colorspace).
23808        fidelity={max(0,$fidelity_finest+($fidelity_coarsest-$fidelity_finest)*$factor)}
23809        fidelity_smoothness={$fidelity_smoothness_finest+\
23810                             ($fidelity_smoothness_coarsest-$fidelity_smoothness_finest)*$factor}
23811
23812        if $fidelity>0.1
23813          sh. 0,2 sh[-5] 0,2 +gradient_norm[-2,-1] rm[-4,-3] # Gradient norm on colors only
23814          *. $fidelity argmax[-2,-1] b. xy,$fidelity_smoothness n. 0,{min(1,$fidelity)}
23815          sh[-5,-2] 0,2 srgb2lab[-2,-1] rm[-2,-1]
23816          +*. $fidelity_chroma r. 100%,100%,1,2,1 a[-2,-1] c j.. [-5],0,0,0,0,1,. rm.
23817          sh. 0,2 lab2srgb. rm.
23818        fi
23819        rm[-4]
23820
23821        # Iterate patch-matching steps.
23822        nb_matches={max(0,round($nb_matches_finest+($nb_matches_coarsest-$nb_matches_finest)*$factor^2))}
23823        nb_scales1={$nb_scales-1}
23824        nb_matches1={$nb_matches-1}
23825        if {*} +r. $wsiz,1,100% to. "Scale "$scale/$nb_scales1": 0%",5,2,24 w. -1,-1,0 rm. fi
23826
23827        repeat $nb_matches
23828          matchpatch. ...,$patchsize_analysis,$patchsize_analysis,1,$mprec0,$mprec1,$penalize_repetitions,0,..
23829          -.. . abs.. diff={-2,ia} rm..
23830          +warp_patch.. .,$patchsize_synthesis,$patchsize_synthesis,1
23831          if {*}" && "(!($>%5)" || "$nb_matches<=10)
23832            +r. $wsiz,1,100%
23833            to. "Scale "$scale/$nb_scales1": "{round(100*($>+1)/$nb_matches)}%,5,2,24 w. -1,-1,0 rm.
23834          fi
23835          if $is_window" && "!{*} break fi
23836          if $diff<1 break fi
23837        done
23838        rm[-3,-1]
23839      fi
23840      if $is_window" && "!{*} break fi
23841    done
23842    if $is_window" && "!{*} k[0,1] break fi
23843
23844    # Do final rendering.
23845    +warp_patch[1] .,$patchsize_synthesis_final,$patchsize_synthesis_final,1 c. 0,255
23846    rv[0,-1] rm[-2,-1]
23847  endl done rm.
23848  um stylize_match
23849
23850#@cli tetris : _scale>0
23851#@cli : Apply tetris effect on selected images.
23852#@cli : Default value: 'scale=10'.
23853#@cli : $ image.jpg +tetris 10
23854tetris : skip ${1=10}
23855  e[^-1] "Apply tetris effect on image$?, with scale $1."
23856  repeat $! l[$>]
23857    wh={w},{h},1,{s} r $1%,$1%,$1%,100%,2 n 0,255 quantize 10,1,0 r $wh b 2 sharpen 300,1
23858  endl done
23859
23860#@cli warhol : _M>0,_N>0,_smoothness>=0,_color>=0
23861#@cli : Create MxN Andy Warhol-like artwork from selected images.
23862#@cli : Default values: 'M=3', 'N=M', 'smoothness=2' and 'color=20'.
23863#@cli : $ image.jpg warhol 3,3,3,40
23864warhol : skip ${1=3},${2=$1},${3=2},${4=20}
23865  e[^-1] "Create $1x$2 Andy Warhol-like artwork from image$?."
23866  r0={100/max($1,$2)}
23867  repeat $! l[$>]
23868    norm b $3 r $r0%,$r0%,1,100%,2 quantize 6 n 0,5 round 1
23869    repeat $1 repeat $2
23870      (0,1,2,3,4,5) n. 32,224 6,1,1,2,128 noise. $4,0 c. 0,255 a[-2,-1] c ycbcr2rgb. +map[0] . rm..
23871    done done append_tiles[^0] $1,$2 nm[1] {0,n} rm[0]
23872  endl done
23873
23874#@cli weave : _density>=0,0<=_thickness<=100,0<=_shadow<=100,_shading>=0,_fibers_amplitude>=0,_fibers_smoothness>=0,\
23875# _angle,-1<=_x_curvature<=1,-1<=_y_curvature<=1
23876#@cli : Apply weave effect to the selected images.
23877#@cli : 'angle' can be { 0=0 deg. | 1=22.5 deg. | 2=45 deg. | 3=67.5 deg. }.
23878#@cli : Default values: 'density=6', 'thickness=65', 'shadow=40', 'shading=0.5', 'fibers_amplitude=0', _\
23879# 'fibers_smoothness=0', 'angle=0' and 'curvature_x=curvature_y=0'
23880#@cli : $ image.jpg weave ,
23881weave : check "${1=6}>=0 && ${2=65}>=0 && $2<=100 && ${3=40}>=0 && $3<=100 && ${4=0.5}>=0"
23882        check "${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 && $7<=3 && ${8=0}>=-1 && $8<=1 && ${9=0}>=-1 && $9<=1"
23883  e[^-1] "Apply weave effect to image$?, with $1 strips, thickness $2, shadow $3, shading $4, "\
23884          "fibers amplitude $5 and fibers smoothness $6, angle "{$7*22.5}" deg. and curvatures ($8,$9)."
23885  repeat $! l[$>] split_opacity l[0]
23886    w={round(max(w,h)/$1,1,1)} h=$w s={(100-$3)*255%} p={max(0.01,$4)}
23887
23888    # Create patterns.
23889    1,$h =. 1,0,50% distance. 1 ^. $p c. 50%,100% r. {max(1,round($2*$w%))},100%
23890    $w,1 =. 1,50% distance. 1 ^. $p c. 50%,100% *. -1 r. 100%,{max(1,round($2*$h%))}
23891    +*. -1 +*... -1 n[-4,-2] 0,$s n[-3,-1] $s,255
23892    {w},1 1,... rand[-2,-1] 0,1 b[-2,-1] $6% n[-2,-1] -$5,$5 ri. [-4] +[-5] . +[-4,-1] +[-5] . +[-2,-1]
23893    +f... 255 a[-4,-1] c +f. 255 a[-2,-1] c
23894
23895    amp_x={$8*($w-w)/2} amp_y={$9*($w-w)/2}
23896    r[-4--1] $w,$h,1,100%,0,0,0.5,0.5
23897    f[-4] 'i(x+$amp_x*sin(y/h*pi),y,0,c,1,2)' f. 'i(x-$amp_x*sin(y/h*pi),y,0,c,1,2)'
23898    f... 'i(x,y+$amp_y*sin(x/w*pi),0,c,1,2)' f.. 'i(x,y-$amp_y*sin(x/w*pi),0,c,1,2)'
23899    blend[-4,-3] alpha blend[-2,-1] alpha c[-2,-1] 0,255
23900
23901    # Render full pattern and merge.
23902    /[-2,-1] 255 . ... a[-4,-2] x a[-2,-1] x a[-2,-1] y rotate_tileable. {$7*22.5}
23903    r. ..,..,1,1,0,2 *[-2,-1]
23904  endl a c endl done
23905
23906#@cli whirls : _texture>=0,_smoothness>=0,_darkness>=0,_lightness>=0
23907#@cli : Add random whirl texture to selected images.
23908#@cli : Default values: 'texture=3', 'smoothness=6', 'darkness=0.5' and 'lightness=1.8'.
23909#@cli : $ image.jpg whirls ,
23910whirls : skip ${1=3},${2=6},${3=0.5},${4=1.8}
23911  e[^-1] "Add random whirl texture to image$?, with texture $1, smoothness $2, darkness $3 and lightness $4."
23912  repeat $! l[$>]
23913    100%,100% noise. 0.3,2 ==. 1 repeat $1 b. $2 +. 0.1 gradient_norm. ^. 0.2 done
23914    n. $3,$4 ri. .. * c 0,255
23915  endl done
23916
23917#------------------------------------
23918#
23919#@cli :: Warpings
23920#
23921#------------------------------------
23922
23923#@cli deform : _amplitude>=0,_interpolation
23924#@cli : Apply random smooth deformation on selected images.
23925#@cli : 'interpolation' can be { 0=none | 1=linear | 2=bicubic }.
23926#@cli : Default value: 'amplitude=10'.
23927#@cli : $ image.jpg +deform[0] 10 +deform[0] 20
23928deform : skip ${1=10},${2=1}
23929  e[^-1] "Apply random smooth deformation on image$?, with amplitude $1."
23930  repeat $! l[$>]
23931    2%,2%,1,2 noise. $1 r. ..,..,1,2,5 warp.. .,1,$2,1 rm.
23932  endl done
23933
23934#@cli euclidean2polar : _center_x[%],_center_y[%],_stretch_factor>0,\
23935# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
23936#@cli : Apply euclidean to polar transform on selected images.
23937#@cli : Default values: 'center_x=center_y=50%', 'stretch_factor=1' and 'boundary_conditions=1'.
23938#@cli : $ image.jpg +euclidean2polar ,
23939euclidean2polar : skip ${1=50%},${2=50%} check "${3=1}>0 && isint(${4=1}) && $4>=0 && $4<=3"
23940  e[^-1] "Apply euclidean to polar transform on image$?, with center point ($1,$2), stretch factor $3 and "\
23941         ${"arg 1+$4,dirichlet,neumann,periodic,mirror"}" boundary conditions."
23942  repeat $! l[$>]
23943    cx={if(${is_percent\ $1},$1*(w-1),$1)}
23944    cy={if(${is_percent\ $2},$2*(h-1),$2)}
23945    R={sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2))}
23946    f 'r=$R*(x/(w-1))^$3;a=y*2*pi/(h-1);i($cx+r*cos(a),$cy+r*sin(a),z,c,1,$4)'
23947  endl done
23948
23949#@cli equirectangular2nadirzenith
23950#@cli : Transform selected equirectangular images to nadir/zenith rectilinear projections.
23951equirectangular2nadirzenith :
23952  e[^-1] "Transform equirectangular image$? to nadir/zenith rectilinear projections."
23953  repeat $! l[$>]
23954    100%,100%,1,2
23955    sh. 100%
23956    f. "
23957      X = 2*x/(w-1) - 1;
23958      Y = y/(h-1) - 0.5;
23959      if (X<0,
23960        sinphi1 = 1; X+=0.5,
23961        sinphi1 = -1; X-=0.5
23962      );
23963      rr = sqrt(X*X + Y*Y);
23964      cc = atan(2*rr);
23965      phi = rr==0?0:asin(cos(cc)*sinphi1);
23966      X = atan2(X,-Y*sinphi1)/pi;
23967      Y = phi/pi;
23968      (++X)*=0.5*w;
23969      (Y+=0.5)*=h;
23970      i(#-2) = X; Y;"
23971    warp[0] [1],0,0,1 k...
23972  endl done
23973
23974#@cli fisheye : _center_x,_center_y,0<=_radius<=100,_amplitude>=0
23975#@cli : Apply fish-eye deformation on selected images.
23976#@cli : Default values: 'x=y=50', 'radius=50' and 'amplitude=1.2'.
23977#@cli : $ image.jpg +fisheye ,
23978fisheye : skip ${1=50},${2=50},${3=50},${4=1.2}
23979  e[^-1] "Apply Fish-eye effect on image$?, centered at ($1%,$2%) with radius $3% and amplitude $4."
23980  if $4==0 return fi
23981  repeat $! l[$>]
23982    100%,100%,1,1 =. 1,$1%,$2% distance. 1 c. 0,$3% *. -1 n. 0,1 ^. {1/$4}
23983    i.. ({-$1/100},{1-$1/100};{-$1/100},{1-$1/100}^{-$2/100},{-$2/100};{1-$2/100},{1-$2/100}) r.. .,.,1,2,3
23984    n. 0,{max(w,h)} *[-2,-1]
23985    warp.. .,1,1,1 rm.
23986  endl done
23987
23988#@cli flower : _amplitude,_frequency,_offset_r[%],_angle,_center_x[%],_center_y[%],\
23989# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror}
23990#@cli : ~~~\ntoto\n~~~
23991#@cli : Apply flower deformation on selected images.
23992#@cli : Default values: 'amplitude=30', 'frequency=6', 'offset_r=0', 'angle=0', 'center_x=center_y=50%' \
23993# and 'boundary_conditions=3'.
23994#@cli : $ image.jpg +flower ,
23995flower : skip ${1=30},${2=6},${3=0},${4=0},${5=50%},${6=50%},${7=3}
23996  e[^-1] "Apply flower deformation on image$?, with amplitude $1, frequency $2, offset $3, angle $4 deg. and
23997          center point ($1,$2)."
23998  if ${"is_percent $3"}
23999    transform_polar "r + (R*$3) + R*$1/100*cos(a*$2+$4*pi/180)","a",$5,$6,$7
24000  else
24001    transform_polar "r + $3 + R*$1/100*cos(a*$2+$4*pi/180)","a",$5,$6,$7
24002  fi
24003
24004#@cli kaleidoscope : _center_x[%],_center_y[%],_radius,_angle,\
24005# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
24006#@cli : Create kaleidoscope effect from selected images.
24007#@cli : Default values: 'center_x=center_y=50%', 'radius=100', 'angle=30' and 'boundary_conditions=3'.
24008#@cli : $ image.jpg kaleidoscope ,
24009kaleidoscope : skip ${1=50%},${2=50%},${3=100},${4=30},${5=3}
24010  e[^-1] "Create kaleidoscope effect from image$?, with center point ($1,$2), radius $3, angle $4 deg."
24011  euclidean2polar $1,$2,1,$5 repeat $! l[$>]
24012    +columns 0,$3% rows. 0,$4% ri. ..,0,2 nm[1] {0,n} rm[0]
24013  endl done polar2euclidean $1,$2,1,$5
24014
24015#@cli map_sphere : _width>0,_height>0,_radius,_dilation>0,_fading>=0,_fading_power>=0
24016#@cli : Map selected images on a sphere.
24017#@cli : Default values: 'width=height=512', 'radius=100', 'dilation=0.5', 'fading=0' and 'fading_power=0.5'.
24018#@cli : $ image.jpg map_sphere ,
24019map_sphere : check "${1=512}>0 && ${2=512}>0 && ${5=0}>=0 && ${6=0.5}>=0" skip ${3=100},${4=0.5}
24020   e[^-1] "Map image$? on spheres in $1x$2 images, with radius $3, dilation $4 and fading $5."
24021   r2={($3*min($1,$2)/200)^2} # Compute squared radius.
24022   repeat $! l[$>]
24023     i.. 100%,1,1,100%,0 nm[0] {1,n} a y # Add one border line to have a sphere exterior.
24024     ({-$1/2},{$1/2}) ({-$2/2};{$2/2}) r[-2,-1] $1,$2,1,1,3 atan2. .. rm.. # Compute theta angle.
24025     $1,$2 =. 1,50%,50% distance. 1,3 /. $r2 sqrt. c. 0,1
24026     asin. # Compute phi angle.
24027     +.. {pi} *.. {({-3,w}-1)/(2*pi)} # Normalize theta to X-coordinates
24028     *. {2/pi} ^. $4 *. {{-3,h}-1} *. -1 +. {{-3,h}-1} # Normalize phi to Y-coordinates
24029     if $5 +>=. 1 distance. 1 c. 0,$5% n. 0,1 ^. $6 c.. 1,100% -[-2,-1] fi
24030     r[-1,-2] 100%,100%,{-3,d}
24031     +f. z a[-3--1] c
24032     warp.. .,0,1,1 rm. # Apply image warping
24033   endl done
24034
24035#@cli nadirzenith2equirectangular
24036#@cli : Transform selected nadir/zenith rectilinear projections to equirectangular images.
24037nadirzenith2equirectangular :
24038  e[^-1] "Transform nadir/zenith rectilinear projection$? to equirectangular images."
24039  repeat $! l[$>]
24040    100%,100%,1,2
24041    sh. 100%
24042    f. "
24043      X = 2*x/(w-1) - 1;
24044      Y = y/(h-1) - 0.5;
24045      output = 1;
24046      if (Y>0.125,
24047        sinphi1 = 1; xc = -0.5,
24048      if (Y<-0.125,
24049        sinphi1 = -1; xc = 0.5,
24050        output = 0
24051      ));
24052      cosc = sinphi1*sin(Y*pi);
24053      xx = cos(Y*pi)*sin(X*pi)/cosc;
24054      yy = -sinphi1*cos(Y*pi)*cos(X*pi)/cosc;
24055      if (abs(xx)>1, output=0);
24056      (xx*=0.5)+=xc;
24057      yy*=0.5;
24058      if (!output, xx = yy = -1);
24059      (++xx)*=0.5*w;
24060      (yy+=0.5)*=h;
24061      i(#-2) = xx; yy;"
24062    to_a[0] warp[0] [1],0,0,0 k...
24063  endl done
24064
24065#@cli polar2euclidean : _center_x[%],_center_y[%],_stretch_factor>0,\
24066# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
24067#@cli : Apply euclidean to polar transform on selected images.
24068#@cli : Default values: 'center_x=center_y=50%', 'stretch_factor=1' and 'boundary_conditions=1'.
24069#@cli : $ image.jpg +euclidean2polar ,
24070polar2euclidean : skip ${1=50%},${2=50%} check "${3=1}>0 && isint(${4=1}) && $4>=0 && $4<=3"
24071  e[^-1] "Apply polar to euclidean transform on image$?, with center point ($1,$2), stretch factor $3 and "\
24072         ${"arg 1+$4,dirichlet,neumann,periodic,mirror"}" boundary conditions."
24073  repeat $! l[$>]
24074    cx={if(${is_percent\ $1},$1*(w-1),$1)}
24075    cy={if(${is_percent\ $2},$2*(h-1),$2)}
24076    R={sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2))}
24077    f "X = sqrt((x-"$cx")^2+(y-"$cy")^2);
24078       tmp = atan2((y-"$cy"),(x-"$cx"));
24079       Y = if(tmp<0,tmp+2*pi,tmp);
24080       i((X/"$R")^(1/$3)*(w-1),Y*(h-1)/(2*pi),z,c,1,$4)"
24081  endl done
24082
24083#@cli raindrops : _amplitude,_density>=0,_wavelength>=0,_merging_steps>=0
24084#@cli : Apply raindrops deformation on selected images.
24085#@cli : Default values: 'amplitude=80','density=0.1', 'wavelength=1' and 'merging_steps=0'.
24086#@cli : $ image.jpg +raindrops ,
24087raindrops : check "${2=0.1}>=0 && ${3=1}>=0 && isint(${4=0}) && $4>=0" skip ${1=80}
24088  e[^-1] "Apply raindrops deformation on image$?, with amplitude $1, density $2, wavelength $3 and $4 merging steps."
24089  repeat $! l[$>]
24090    100%,100% noise. $2,2 ==. 1 distance. 1 f. 'cos(i)/(1+i/(1e-8+$3))'
24091    if $4
24092      i.. (0,1,0;1,0,1;0,1,0) /.. 2 .
24093      repeat $4 +convolve. ...,1 -. ... rm... done rm[-3,-2]
24094    fi
24095    g. a[-2,-1] c *. {$1/(1e-5+max(abs(im),abs(iM)))}
24096    warp.. .,1 rm.
24097  endl done
24098
24099#@cli ripple : _amplitude,_bandwidth,_shape={ 0=bloc | 1=triangle | 2=sine | 3=sine+ | 4=random },_angle,_offset
24100#@cli : Apply ripple deformation on selected images.
24101#@cli : Default values: 'amplitude=10', 'bandwidth=10', 'shape=2', 'angle=0' and 'offset=0'.
24102#@cli : $ image.jpg +ripple ,
24103ripple : skip ${1=10},${2=20},${3=2},${4=0},${5=0}
24104  e[^-1] "Apply ripple deformation on image$?, with amplitude $1, bandwidth $2, shape $3, angle $4 deg. and offset $5."
24105  theta={$4*pi/180} C={cos($theta)} S={-sin($theta)}
24106  repeat $! l[$>]
24107    100%,100%,1,1,"x" -. {w/2} 100%,100%,1,1,'y'
24108    -. {h/2-$5} *.. $S *. $C +[-2,-1]      # Generate rotated Y.
24109    _ripple$3. $1,$2                       # Generate warp field.
24110    +*. {-$S} *.. $C a[-2,-1] c            # Rotate warp field.
24111    warp.. .,1 rm.
24112  endl done
24113
24114_ripple0 : f {$1/2}*"(1-2*(i%"{2*$2}"<$2))"
24115_ripple1 : f "I=(i%$2)/$2;$1*(2*if(I<0.5,I,1-I)-0.5)"
24116_ripple2 : f {-$1/2}*"cos(i*"{2*pi/$2}")"
24117_ripple3 : f {-$1/2}*"abs(cos(i*"{2*pi/$2}"))"
24118_ripple4 : skip $* n 0,{h-1} 1,{h} rand. {-$1/2},{$1/2} m={im} M={iM} b. {$2/10} n. $m,$M map.. . rm.
24119
24120#@cli rotoidoscope : _center_x[%],_center_y[%],_tiles>0,_smoothness[%]>=0,\
24121# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
24122#@cli : Create rotational kaleidoscope effect from selected images.
24123#@cli : Default values: 'center_x=center_y=50%', 'tiles=10', 'smoothness=1' and 'boundary_conditions=3'.
24124#@cli : $ image.jpg +rotoidoscope ,
24125rotoidoscope : skip ${1=50%},${2=50%},${5=1} check "${3=10}>0 && ${4=3}>=0"
24126  e[^-1] "Create rotational kaleidoscope effect from image$?, with center point ($1,$2), $3 tiles and smoothness $4."
24127  repeat $! l[$>]
24128    repeat $3 +rotate[0] {360/$3},1,$5,$1,$2 blend_edges $4 done
24129  endl done
24130
24131#@cli spherize : _radius[%]>=0,_strength,_smoothness[%]>=0,_center_x[%],_center_y[%],_ratio_x/y>0,_angle,\
24132# _interpolation
24133#@cli : Apply spherize effect on selected images.
24134#@cli : Default values: 'radius=50%', 'strength=1', 'smoothness=0', 'center_x=center_y=50%', 'ratio_x/y=1', \
24135# 'angle=0' and 'interpolation=1'.
24136#@cli : $ image.jpg grid 5%,5%,0,0,0.6,255 spherize ,
24137spherize : check "${1=50%}>=0 && ${3=0}>=0 && ${6=1}>0 && isint(${8=1}) && $8>=0 && $8<=2"
24138           skip "${2=1},${4=50%},${5=50%},${7=0}"
24139  e[^-1] "Apply spherize effect on image$?, with radius $1, strength $2, smoothness $3, center ($4,$5),
24140          x/y-ratio $6, angle $7 and "${"arg 1+$8,nearest-neighbor,linear,cubic"}" interpolation."
24141  if !$1||!$2 return fi
24142  repeat $! l[$>]
24143    rmax={${"is_percent $1"}?0.5*sqrt((w-1)^2+(h-1)^2)*$1:$1}
24144    centerx={${"is_percent $4"}?(w-1)*$4:$4}
24145    centery={${"is_percent $5"}?(h-1)*$5:$5}
24146    strength={$2>0?$2:1-exp($2/5)}
24147    100%,100%,1,2,"
24148      begin(
24149        center = [ "$centerx","$centery" ];
24150        wh1 = [ w,h ] - 1;
24151        m2wh1 = 0.5*max(wh1);
24152        rmax = "$rmax"/m2wh1;
24153        const f = 1/"$strength";
24154        const ratio = $6;
24155        rotf = rot($7°);
24156        rotb = rot(-$7°);
24157      );
24158      xy = ([x,y] - center)/m2wh1;
24159      xy = rotf*xy;
24160      ratio>=1?(xy[1]*=ratio):(xy[0]/=ratio);
24161      r = norm2(xy);
24162      z = r<rmax?sign($2)*sqrt(rmax^2 - r^2):0;
24163      ratio>=1?(xy[1]/=ratio):(xy[0]*=ratio);
24164      xy = rotb*xy;
24165      xy = center + f*xy/(f + z)*m2wh1"
24166    b. $3
24167    warp.. .,0,$8,1 rm.
24168  endl done
24169
24170#@cli symmetrize : _x[%],_y[%],_angle,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror },\
24171# _is_antisymmetry={ 0 | 1 },_swap_sides={ 0 | 1 }
24172#@cli : Symmetrize selected images regarding specified axis.
24173#@cli : Default values: 'x=y=50%', 'angle=90', 'boundary_conditions=3', 'is_antisymmetry=0' and 'swap_sides=0'.
24174#@cli : $ image.jpg +symmetrize 50%,50%,45 +symmetrize[-1] 50%,50%,-45
24175symmetrize : skip ${1=50%},${2=50%},${3=90},${4=3},${5=0},${6=0}
24176  e[^-1] "Symmetrize image$?, regarding axis ($1,$2,$3 deg.)."
24177  theta={$3*pi/180} u={cos($theta)} v={sin($theta)}
24178  if $6 symmetry_cond=A<0 else symmetry_cond=A>0 fi
24179  repeat $! l[$>]
24180    x0={if(${is_percent\ $1},w*$1,$1)}
24181    y0={if(${is_percent\ $2},h*$2,$2)}
24182    if $5 f 'A=($y0-y)*$u-($x0-x)*$v;X=x+2*($x0-x);Y=y+2*($y0-y);if($symmetry_cond,i(X,Y,z,c,1,$4),i)'
24183    else f 'A=($y0-y)*$u-($x0-x)*$v;X=x-2*$v*A;Y=y+2*$u*A;if($symmetry_cond,i(X,Y,z,c,1,$4),i)'
24184    fi
24185  endl done
24186
24187#@cli transform_polar : "expr_radius",_"expr_angle",_center_x[%],_center_y[%],\
24188# _boundary_conditions={ 0=dirichlet | 1=neumann }
24189#@cli : Apply user-defined transform on polar representation of selected images.
24190#@cli : Default values: 'expr_radius=R-r', 'expr_rangle=a', 'center_x=center_y=50%' and 'boundary_conditions=1'.
24191#@cli : $ image.jpg +transform_polar[0] R*(r/R)^2,a +transform_polar[0] r,2*a
24192transform_polar : skip "${1=R-r}","${2=a}",${3=50%},${4=50%},${5=1}
24193  e[^-1] "Apply custom polar transform with 'new_r = $1', 'new_a = $2', center point ($3%,$4%)."
24194  repeat $! l[$>]
24195    cx={if(${is_percent\ $3},$3*(w-1),$3)}
24196    cy={if(${is_percent\ $4},$4*(h-1),$4)}
24197    R={sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2))}
24198    f "R ="$R";
24199       r = sqrt((x-"$cx")^2 + (y-"$cy")^2);
24200       a = atan2(y-"$cy",x-"$cx");
24201       nr = ($1);
24202       na = ($2);
24203       i("$cx" + nr*cos(na), "$cy" + nr*sin(na), z, c,1,$5)"
24204  endl done
24205
24206#@cli twirl : _amplitude,_center_x[%],_center_y[%],\
24207# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
24208#@cli : Apply twirl deformation on selected images.
24209#@cli : Default values: 'amplitude=1', 'center_x=center_y=50%' and 'boundary_conditions=3'.
24210#@cli : $ image.jpg twirl 0.6
24211twirl : skip ${1=1},${2=50%},${3=50%},${4=3}
24212  e[^-1] "Apply twirl deformation on image$?, with amplitude $1 and center point at ($2%,$3%)."
24213  euclidean2polar $2,$3,1,$4 repeat $!
24214    [$>],[$>],1,1,$1*x channels. -1,0 warp[$>] .,1,1,2 rm.
24215  done polar2euclidean $2,$3,1,1
24216
24217#@cli warp_perspective : _x-angle,_y-angle,_zoom>0,_x-center,_y-center,\
24218# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
24219#@cli : Warp selected images with perspective deformation.
24220#@cli : Default values: 'x-angle=1.5', 'y-angle=0', 'zoom=1', 'x-center=y-center=50' and 'boundary_conditions=2'.
24221#@cli : $ image.jpg warp_perspective ,
24222warp_perspective : skip ${1=1.5},${2=0},${3=1},${4=50},${5=50},${6=2}
24223  e[^-1] "Apply perspective warp on image$?, with angles ($1 deg.,$2 deg.), zoom $3 and offsets ($4,$5)."
24224  repeat $! l[$>]
24225    (0,100) -. $4 /. 100 (0;100) -. $5 /. 100 r[-2,-1] ...,...,...,1,3
24226    +*.. $2 +*.. $1 +[-2,-1] +. $3 /... . /[-2,-1]
24227    *.. 100 +.. $4 /.. 100 *.. {-3,w}
24228    *. 100 +. $5 /. 100 *. {-3,h}
24229    a[-2,-1] c warp.. .,0,1,$6 rm.
24230  endl done
24231
24232#@cli water : _amplitude,_smoothness>=0,_angle
24233#@cli : Apply water deformation on selected images.
24234#@cli : Default values: 'amplitude=30', 'smoothness=1.5' and 'angle=45'.
24235#@cli : $ image.jpg water ,
24236water : check ${2=1.5}>=0 skip ${1=30},${3=1},${4=45}
24237  e[^-1] "Apply water deformation on image$?, with amplitude $1, smoothness $2 and angle $3."
24238  repeat $! l[$>]
24239    25%,25%,25%,1 noise. $1 g. xy *.. {-sin($3*pi/180)} *. {cos($3*pi/180)} +[-2,-1] b. $2 *. 2
24240    r. ..,..,1,2,3 warp.. .,1 rm.
24241  endl done
24242
24243#@cli wave : _amplitude>=0,_frequency>=0,_center_x,_center_y
24244#@cli : Apply wave deformation on selected images.
24245#@cli : Default values: 'amplitude=4', 'frequency=0.4' and 'center_x=center_y=50'.
24246#@cli : $ image.jpg wave ,
24247wave : skip ${1=4},${2=0.4},${3=50},${4=50}
24248  e[^-1] "Apply wave deformation on image$?, with amplitude $1, frequency $2 and center point at ($3%,$4%)."
24249  repeat $! l[$>]
24250    100%,100% =. 1,$3%,$4% distance. 1
24251    *. $2 +sin. cos.. a[-2,-1] c *. $1
24252    warp.. .,1 rm.
24253  endl done
24254
24255#@cli wind : _amplitude>=0,_angle,0<=_attenuation<=1,_threshold
24256#@cli : Apply wind effect on selected images.
24257#@cli : Default values: 'amplitude=20', 'angle=0', 'attenuation=0.7' and 'threshold=20'.
24258#@cli : $ image.jpg +wind ,
24259wind : check "isint(${1=20}) && $1>=0 && ${3=0.7}>=0 && $3<=1" skip "${2=0},${4=20}"
24260  e[^-1] "Apply wind effect on image$?, with amplitude $1, angle "{round($2/45)*45}" deg., attenuation $3
24261          and threshold $4."
24262  if !$1 return fi
24263  dxdy=${-_wind{round($2/45)%8}}
24264  fact={(1-$3)^(1/$1)}
24265  repeat $! l[$>]
24266    +gradient_norm >=. $4%
24267    r. 100%,100%,1,.. *. ..
24268    repeat $1
24269      shift. $dxdy,0,0,0 max.. . *. $fact
24270      remove_pixels. {100/$1}%
24271    done rm.
24272  endl done
24273
24274_wind0 : u 1,0
24275_wind1 : u 1,1
24276_wind2 : u 0,1
24277_wind3 : u -1,1
24278_wind4 : u -1,0
24279_wind5 : u -1,-1
24280_wind6 : u 0,-1
24281_wind7 : u 1,-1
24282
24283#@cli zoom : _factor,_cx,_cy,_cz,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
24284#@cli : Apply zoom factor to selected images.
24285#@cli : Default values: 'factor=1', 'cx=cy=cz=0.5' and 'boundary_conditions=0'.
24286#@cli : $ image.jpg +zoom[0] 0.6 +zoom[0] 1.5
24287zoom : skip ${1=2},${2=0.5},${3=0.5},${4=0.5},${5=0}
24288  e[^-1] "Apply zoom effect on image$?, with factor $1 and center ($2,$3)."
24289  repeat $! l[$>]
24290    if d==1 # 2D image.
24291       ({(w-1)*$2*(1-1/$1)},{(w-1)*($2+(1-$2)/$1)})
24292       ({({-2,h}-1)*$3*(1-1/$1)};{({-2,h}-1)*($3+(1-$3)/$1)})
24293       r[-2--1] ...,...,1,1,3 a[-2--1] c warp.. .,0,1,$5
24294    else # 3D image.
24295       ({(w-1)*$2*(1-1/$1)},{(w-1)*($2+(1-$2)/$1)})
24296       ({({-2,h}-1)*$3*(1-1/$1)};{({-2,h}-1)*($3+(1-$3)/$1)})
24297       ({({-3,d}-1)*$4*(1-1/$1)}/{({-3,d}-1)*($4+(1-$4)/$1)})
24298       r[-3--1] [-4],[-4],[-4],1,3 a[-3--1] c warp.. .,0,1,$5
24299    fi
24300    rm.
24301  endl done
24302
24303#-----------------------------
24304#
24305#@cli :: Degradations
24306#
24307#-----------------------------
24308
24309#@cli cracks : 0<=_density<=100,_is_relief={ 0 | 1 },_opacity,_color1,...
24310#@cli : Draw random cracks on selected images with specified color.
24311#@cli : Default values: 'density=25', 'is_relief=0', 'opacity=1' and 'color1=0'.
24312#@cli : $ image.jpg +cracks ,
24313cracks : check "${1=25}>=0" skip ${2=0},${3=1},${4=0}
24314  e[^-1] "Add random cracks to image$?, with density $1, opacity $3 and color (${4--1})."
24315  repeat $! l[$>] cut={[im,iM]}
24316    100%,100%,1,2,'u<0.25*($1%)^4?[u,1]:[0,0]'
24317    s. c distance. 1 *. -1 watershed.. . rm.
24318    +dilate. 3 -[-2,-1] !=. 0 # thinning. 1
24319    if $2
24320      f. "i?i:j(1)?2:j(-1)?0.5:i" n. 0,1
24321      +fc.. ${4--1} *. .. !=.. 0 j... .,0,0,0,0,$3,..
24322    else
24323      +fc.. ${4--1} j... .,0,0,0,0,$3,..
24324    fi
24325    k[0]
24326  endl done
24327
24328#@cli light_patch : _density>0,_darkness>=0,_lightness>=0
24329#@cli : Add light patches to selected images.
24330#@cli : Default values: 'density=10', 'darkness=0.9' and 'lightness=1.7'.
24331#@cli : $ image.jpg +light_patch 20,0.9,4
24332light_patch : skip ${1=10},${2=0.9},${3=1.7}
24333  e[^-1] "Apply light patches to image$?, with density $1, darkness $2 and lightness $3."
24334  repeat $! l[$>]
24335    n 0,255 $1,$1 noise. 40 ri. ..,5 c. 0,255
24336    n. $2,$3 * c 0,255
24337  endl done
24338
24339#@cli noise_hurl : _amplitude>=0
24340#@cli : Add hurl noise to selected images.
24341#@cli : Default value: 'amplitude=10'.
24342#@cli : $ image.jpg +noise_hurl ,
24343noise_hurl : skip ${1=10}
24344  e[^-1] "Add hurl noise to image$?, with amplitude $1%."
24345  repeat $! l[$>]
24346    +f 0 noise. 10 n. {-2,[im,iM]} 100%,100%
24347    noise. $1,2 >. 0 ri. ..
24348    *.. . *. -1 +. 1 *[-3,-1] +
24349  endl done
24350
24351#@cli pixelize : _scale_x>0,_scale_y>0,_scale_z>0
24352#@cli : Pixelize selected images with specified scales.
24353#@cli : Default values: 'scale_x=20' and 'scale_y=scale_z=scale_x'.
24354#@cli : $ image.jpg +pixelize ,
24355pixelize : skip ${1=20},${2=$1},${3=$1}
24356  e[^-1] "Pixelize image$? with scales ($1%,$2%,$3%)."
24357  repeat $! l[$>] whd={w},{h},{d} r $1%,$2%,$3%,100%,2 r $whd endl done
24358
24359#@cli scanlines : _amplitude,_bandwidth,_shape={ 0=bloc | 1=triangle | 2=sine | 3=sine+ | 4=random },_angle,_offset
24360#@cli : Apply ripple deformation on selected images.
24361#@cli : Default values: 'amplitude=60', 'bandwidth=2', 'shape=0', 'angle=0' and 'offset=0'.
24362#@cli : $ image.jpg +scanlines ,
24363scanlines : skip ${1=60},${2=2},${3=0},${4=0},${5=0}
24364  e[^-1] "Apply scanlines effect on image$?, with amplitude $1, bandwidth $2, shape $3, angle $4 deg. and offset $5."
24365  theta={$4*pi/180} C={cos($theta)} S={-sin($theta)}
24366  repeat $! l[$>]
24367    100%,100%,1,1,"x" -. {w/2} 100%,100%,1,1,'y'
24368    -. {h/2-$5} *.. $S *. $C +[-2,-1]      # Generate rotated Y.
24369    _ripple$3. $1,$2                            # Generate warp field.
24370    n. {-$1},$1
24371    + cut 0,255
24372  endl done
24373
24374#@cli shade_stripes : _frequency>=0,_direction={ 0=horizontal | 1=vertical },_darkness>=0,_lightness>=0
24375#@cli : Add shade stripes to selected images.
24376#@cli : Default values: 'frequency=5', 'direction=1', 'darkness=0.8' and 'lightness=2'.
24377#@cli : $ image.jpg +shade_stripes 30
24378shade_stripes : skip ${1=5},${2=1},${3=0.8},${4=2}
24379  e[^-1] "Add "${arg\ 1+!$2,vertical,horizontal}" shaded stripes to image$?, with frequency $1, darkness $3 and
24380          lightness $4."
24381  n 0,255 repeat $! l[$>]
24382    {max(1,w*($2!=0))},{max(1,h*($2==0))} noise. $1,2 ==. 1 distance. 1 ri. .. n. $3,$4 * c 0,255
24383  endl done
24384
24385#@cli shadow_patch : _opacity>=0
24386#@cli : Add shadow patches to selected images.
24387#@cli : Default value: 'opacity=0.7'.
24388#@cli : $ image.jpg +shadow_patch 0.4
24389shadow_patch : skip ${1=0.7}
24390  e[^-1] "Apply shadow patches to image$?, with opacity $1."
24391  repeat $! l[$>]
24392    100%,100%,1,1 shift. -2,-2 shift. 1,1
24393    plasma. 3,0.3,8 abs. b. 1 c. 3%,15% ri. ..
24394    n. $1,1 *
24395  endl done
24396
24397#@cli spread : _dx>=0,_dy>=0,_dz>=0
24398#@cli : Spread pixel values of selected images randomly along x,y and z.
24399#@cli : Default values: 'dx=3', 'dy=dx' and 'dz=0'.
24400#@cli : $ image.jpg +spread 3
24401spread : skip ${1=3},${2=$1},${3=0}
24402  e[^-1] "Spread pixel of image$? randomly, with amplitudes ($1,$2,$3)."
24403  repeat $! l[$>]
24404    100%,100%,100%,3
24405    sh. 0 rand. {-$1},$1 rm.
24406    sh. 1 rand. {-$2},$2 rm.
24407    sh. 2 rand. {-$3},$3 rm.
24408    warp.. .,1 rm.
24409  endl done
24410
24411#@cli stripes_y : _frequency>=0
24412#@cli : Add vertical stripes to selected images.
24413#@cli : Default value: 'frequency=10'.
24414#@cli : $ image.jpg +stripes_y ,
24415stripes_y : skip ${1=10}
24416  e[^-1] "Add vertical stripes to image$?, with frequency $1."
24417  repeat $! l[$>]
24418    100% noise. $1,2 ==. 1 *. 255 ri. ..
24419    *. 0.15 + c 0,255
24420  endl done
24421
24422#@cli texturize_canvas : _amplitude>=0,_fibrousness>=0,_emboss_level>=0
24423#@cli : Add paint canvas texture to selected images.
24424#@cli : Default values: 'amplitude=20', 'fibrousness=3' and 'emboss_level=0.6'.
24425#@cli : $ image.jpg +texturize_canvas ,
24426texturize_canvas : check "${1=20}>=0 && ${2=3}>=0 && ${3=0.6}>=0 && ${4=80}"
24427  e[^-1] "Add canvas texture to image$?, with amplitude $1, fibrousness $2 and emboss level $3."
24428  repeat $! l[$>]
24429    {w},{h} rand. 0,255 +blur_x. $2 blur_y.. $2 +[-2,-1] g. a[-2,-1] c
24430    +compose_channels. + orientation.. compose_channels.. + n.. $3,1 n. 0,255
24431    sharpen. 80 *[-2,-1] n. -$1,$1 + c 0,255
24432  endl done
24433
24434#@cli texturize_paper
24435#@cli : Add paper texture to selected images.
24436#@cli : $ image.jpg +texturize_paper
24437texturize_paper :
24438  e[^-1] "Add paper texture to image$?."
24439  repeat $! l[$>]
24440    . 30%,30% noise. 1,2 ==. 1 r. ..,..,..,1,0 ifft.
24441    rm. shift. {round(w/2)},{round(h/2)},{round(d/2)},0,2 sharpen. 1 n. 1,1.2 ri. ..
24442    *[-2,-1] c. ..,.. rm..
24443  endl done
24444
24445#@cli vignette : _strength>=0,0<=_radius_min<=100,0<=_radius_max<=100
24446#@cli : Add vignette effect to selected images.
24447#@cli : Default values: 'strength=100', 'radius_min=70' and 'radius_max=90'.
24448#@cli : $ image.jpg vignette ,
24449vignette : check "${1=100}>=0 && ${2=70}>=0 && $2<=100 && ${3=90}>=0 && $3<=100"
24450  e[^-1] "Add vignette effect to image$?, with strength $1 and size $2."
24451  repeat $! l[$>]
24452    mM={[im,iM]} d={max(w,h)}
24453    $d,$d =. 1,50%,50% distance. 1 ri. ..,2
24454    c. $2%,$3% n. 0,$1 - c $mM
24455  endl done
24456
24457#@cli watermark_visible : _text,0<_opacity<1,_size>0,_angle,_mode={ 0=remove | 1=add },_smoothness>=0
24458#@cli : Add or remove a visible watermark on selected images (value range must be [0,255]).
24459#@cli : Default values: 'text=(c) G'MIC', 'opacity=0.3', 'size=53', 'angle=25', 'mode=1' and 'smoothness=0'.
24460#@cli : $ image.jpg watermark_visible ,0.7
24461watermark_visible : check "${2=0.3}>0 && $2<1 && ${3=53}>0 && ${6=0.5}>=0"
24462                    skip "${1=\251\ G\47MIC}",${4=25},${5=1}
24463  e[^-1] ${arg\ 1+!$5,Add,Remove}" visible watermark '$1' on image$?, with opacity $2, size $3, angle $4 deg."
24464  repeat $! l[$>]
24465    0 t. "$1",0,0,$3,1,255 rotate. $4,0,0 b. $6 n. 0,255
24466    ri. ..,0,2 +. .. c. 0,255   # Generate opaque watermark image
24467    if $5 *. $2 *.. {1-$2} +  # Add watermark
24468    else *. $2 - / {1-$2}     # Remove watermark
24469    fi
24470    c 0,255
24471  endl done
24472
24473#--------------------------------------
24474#
24475#@cli :: Blending and Fading
24476#
24477#--------------------------------------
24478
24479#@cli blend : [layer],blending_mode,_opacity[%],_selection_is={ 0=base-layers | 1=top-layers } : \
24480# blending_mode,_opacity[%]
24481#@cli : Blend selected G,GA,RGB or RGBA images by specified layer or blend all selected images together,
24482#@cli : using specified blending mode.
24483#@cli : 'blending_mode' can be { add | alpha | and | average | blue | burn | darken | difference |
24484#@cli : divide | dodge | edges | exclusion | freeze | grainextract | grainmerge | green | hardlight |
24485#@cli : hardmix | hue | interpolation | lchlightness | lighten | lightness | linearburn | linearlight | luminance |
24486#@cli : multiply | negation | or | overlay | pinlight | red | reflect | saturation | seamless | seamless_mixed |
24487#@cli : screen | shapeareamax | shapeareamax0 | shapeareamin | shapeareamin0 | shapeaverage | shapeaverage0 |
24488#@cli : shapemedian | shapemedian0 | shapemin | shapemin0 | shapemax | shapemax0 | softburn | softdodge |
24489#@cli : softlight | stamp | subtract | value | vividlight | xor }.
24490#@cli : 'opacity' should be in '[0,1]', or '[0,100]' if expressed with a '%'.
24491#@cli : Default values: 'blending_mode=alpha', 'opacity=1' and 'selection_is=0'.
24492#@cli : $ image.jpg +drop_shadow , resize2dy[-1] 200 rotate[-1] 20 +blend alpha display_rgba[-2]
24493#@cli : $ image.jpg testimage2d {w},{h} blend overlay
24494#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
24495# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
24496# ex add,alpha,and,average,blue,burn,darken
24497#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
24498# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
24499# ex difference,divide,dodge,exclusion,freeze,grainextract,grainmerge
24500#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
24501# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
24502# ex green,hardlight,hardmix,hue,interpolation,lighten,lightness
24503#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
24504# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
24505# ex linearburn,linearlight,luminance,multiply,negation,or,overlay
24506#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
24507# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
24508# ex pinlight,red,reflect,saturation,screen,shapeaverage,softburn
24509#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
24510# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
24511# ex softdodge,softlight,stamp,subtract,value,vividlight,xor
24512blend : skip ${1=alpha},${2=1},${3=1},${4=0}
24513  if ${"is_image_arg $1"}
24514    n={narg($*)} mode=${arg\ 1+($n>=2),alpha,$2}
24515    e[^-1] "Blend image$? with "${arg\ 1+$4,base,top}" layer $1, using '"$mode"' mode and opacity $3."
24516    repeat $! pass$1 l[$>,-1] if $4 rv fi blend $mode,$3 endl done return
24517  fi
24518  e[^-1] "Blend all image$? together, using '$1' mode and opacity $2."
24519  repeat $!-1 l[0,1]
24520    r[1] [0],[0],[0],100%,0,0,0.5,0.5
24521    s={"s0 = s#0<3?1:3; s1 = s<3?1:3; max(s0,s1)"} # Target color format (G or RGB).
24522    to_colormode[0] {$s+1-(s#0%2)}                 # Target format (G,GA,RGB or RGBA).
24523    to_colormode[1] {$s+1-(s%2)}                   # Mask format (G,GA,RGB or RGBA).
24524    if {0,"s==2 || s==4"} # Target has alpha.
24525      if "s==2 || s==4" # Mask has alpha.
24526        sh[0,1] 0,{s-2} _blend_$1[2,3] rm[2,3]
24527        if ['"$1"']=='alpha' # Special blending code for alpha-mode.
24528          sh[0,1] 0,{{0,s}-2} sh[0,1] 100% *[2,4] *[3,4] rm[2,3]
24529          +channels[1] 100% sh[1] 100% f[3] 255 rm[3]
24530          j[0] [1],0,0,0,0,{max(0,min(1,$2))},[2],255 rm[1,2]
24531          sh[0] 0,{s-2} sh[0] 100% +[2] 1e-10 /[1,2] rm[1] c 0,255
24532        else
24533          sh[1] 0,{s-2} sh[1] 100% j[0] [2],0,0,0,0,{max(0,min(1,$2))},[3],255 rm[^0]
24534        fi
24535      else # Mask has no alpha.
24536        sh[0] 0,{{0,s}-2} rv[1,2] _blend_$1[1,2] j[1] [2],0,0,0,0,{max(0,min(1,$2))} rm[^0]
24537      fi
24538    else # Target has no alpha.
24539      if "s==2 || s==4" # Mask has alpha.
24540        sh[1] 0,{s-2} _blend_$1[0,2] rm[2]
24541        sh[1] 100% j[0] [1],0,0,0,0,{max(0,min(1,$2))},[2],255 rm[^0]
24542      else # Mask has no alpha.
24543        _blend_$1 j[0] [1],0,0,0,0,{max(0,min(1,$2))} rm[1]
24544      fi
24545    fi
24546  endl done
24547
24548_blend_alpha :
24549_blend_normal :
24550_blend_and :
24551  &[1] [0]
24552_blend_add :
24553  +[1] [0] c[1] 0,255
24554_blend_average :
24555  +[1] [0] /[1] 2
24556_blend_blue :
24557  sh[0] 0,1 j[1] [2] rm[2]
24558_blend_burn :
24559  +-[0] 255 +[1] 0.1 /[2] [1] rm[1] +[1] 1 *[1] 255 c[1] 0,255
24560_blend_darken :
24561  min[1] [0]
24562_blend_difference :
24563  -[1] [0] abs[1]
24564_blend_divide :
24565  +[1] 0.1 ^[1] -1 *[1] [0] *[1] 255 c[1] 0,255
24566_blend_dodge :
24567  -[1] 255.1 ^[1] -1 *[1] [0] *[1] -255 c[1] 0,255
24568_blend_edges :
24569  +blend_edges 0.5 rm[1]
24570_blend_exclusion :
24571  +*[0,1] /[2] -127.5 +[1,2] +[1] [0]
24572_blend_freeze :
24573  *[1] -255 -[1] 0.1 +-[0] 255 sqr[2] /[2] [1] rm[1] +[1] 1 *[1] 255 c[1] 0,255
24574_blend_grainextract :
24575  -[1] [0] *[1] -1 +[1] 128 c[1] 0,255
24576_blend_grainmerge :
24577  +[1] [0] -[1] 128 c[1] 0,255
24578_blend_green :
24579  sh[0] 0 sh[0] 2 j[1] [2] j[1] [3],0,0,0,2 rm[2,3]
24580_blend_hardlight :
24581  +*[0,1] /[2] 127.5 ++[0,1] *[3] 2 -[3] 255 -[3] [2] >[1] 128
24582  j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255
24583_blend_hardmix :
24584  +[1] [0] >=[1] 255 *[1] 255
24585_blend_hue :
24586  to_color sh 0,2 rgb2hsv[2,3] sh[2] 1,2 j[1] [4],0,0,0,1 rm[4] hsv2rgb[2,3] rm[2,3]
24587_blend_interpolation :
24588  +*[0] {pi/255} *[1] {pi/255} cos[1,2] +[1,2] -[1] 2 *[1] -63.75 c[1] 0,255
24589_blend_lighten :
24590  max[1] [0]
24591_blend_lightness :
24592  to_color sh 0,2 rgb2lab[2,3] sh[2] 1,2 j[1] [4],0,0,0,1 rm[4] lab2rgb[2,3] rm[2,3]
24593_blend_lchlightness :
24594  _blend_lightness
24595_blend_luminance :
24596  to_color sh 0,2 rgb2ycbcr[2,3] sh[2] 1,2 j[1] [4],0,0,0,1 rm[4] ycbcr2rgb[2,3] rm[2,3]
24597_blend_linearburn :
24598  +[1] [0] -[1] 255 c. 0,255
24599_blend_linearlight :
24600  *[1] 2 +[1] [0] -[1] 255 c[1] 0,255
24601_blend_multiply :
24602  *[1] [0] /[1] 255
24603_blend_negation :
24604  +[1] [0] -[1] 255 abs[1] *[1] -1 +[1] 255
24605_blend_or :
24606  -|[1] [0]
24607_blend_overlay :
24608  +*[0,1] /[2] 127.5 +[1] [0] *[1] 2 -[1] 255 -[1] [2] +<[0] 128 j[1] [2],0,0,0,0,1,[3] rm[2,3] c[1] 0,255
24609_blend_pinlight :
24610  *[1] 2 +blend darken -[1] 256 +blend[0,1] lighten >=[1] 0
24611  j[2] [3],0,0,0,0,1,[1] rm[1,3]
24612_blend_reflect :
24613  -[1] 255.1 *[1] -1 +sqr[0] /[2] [1] rm[1] c[1] 0,255
24614_blend_red :
24615  sh[0] 1,100% j[1] [2],0,0,0,1 rm[2]
24616_blend_seamless :
24617  +blend_seamless 0 rm[1]
24618_blend_seamless_mixed :
24619  +blend_seamless 1 rm[1]
24620_blend_saturation :
24621  to_color sh 0,2 rgb2hsv[2,3] shift[2,3] 0,0,0,-1,2 sh[2] 1,2 j[1] [4],0,0,0,1 rm[4] shift[2,3] 0,0,0,1,2
24622  hsv2rgb[2,3] rm[2,3]
24623_blend_screen :
24624  +-[0] 255 -[1] 255 *[1,2] /[1] 255 *[1] -1 +[1] 255
24625_blend_shapeareamax :
24626  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0
24627  +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0
24628  {1,iM+1},1,1,{0,s+1}
24629  f[1] ">area = i(#2); best = I[#3,i]; if (area>best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i"
24630  rm[2] channels[2] 0,{s-2} map[1] [2] rm[2]
24631_blend_shapeareamax0 :
24632  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0
24633  +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0
24634  {1,iM+1},1,1,{0,s+1}
24635  f[1] ">area = i(#2); best = I[#3,i]; if (area>best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i"
24636  rm[2] channels[2] 0,{s-2} point[2] 0,0,0,1,0 map[1] [2] rm[2]
24637_blend_shapeareamin :
24638  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0
24639  +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0
24640  {1,iM+1},1,1,{0,s+1},inf
24641  f[1] ">area = i(#2); best = I[#3,i]; if (area<best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i"
24642  rm[2] channels[2] 0,{s-2} map[1] [2] rm[2]
24643_blend_shapeareamin0 :
24644  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0
24645  +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0
24646  {1,iM+1},1,1,{0,s+1},inf
24647  f[1] ">area = i(#2); best = I[#3,i]; if (area<best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i"
24648  rm[2] channels[2] 0,{s-2} point[2] 0,0,0,1,0 map[1] [2] rm[2]
24649_blend_shapeaverage :
24650  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0 {iM+1},1,1,{0,s}
24651  f[0] ">i(#2,i(#1,x,y,z,0),0,0,c)+=i;i"
24652  +histogram[1] {w},0,{w-1} /[-2,-1] map[1] . rm.
24653_blend_shapeaverage0 :
24654  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0 {iM+1},1,1,{0,s}
24655  f[0] ">i(#2,i(#1,x,y,z,0),0,0,c)+=i;i"
24656  +histogram[1] {w},0,{w-1} /[-2,-1] point. 0,0,0,1,0 map[1] . rm.
24657_blend_shapemedian :
24658  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0
24659  N={iM+1} $N,1,1,{s#0} $N,8,1,{s#0} s. x
24660  f[1] ">
24661    begin(siz = vector"$N"());
24662    k = i;
24663    k3 = k + 3;
24664    hk3 = h(#k3);
24665    copy(i[#k3,siz[k]++],I(#0),s#0,hk3,whd#0);
24666    if (siz[k]>=hk3,resize(#k3,1,round(1.5*hk3+1),1,s#0,0,0));
24667    end(repeat (size(siz),k, resize(#k+3,1,siz[k],1,s#0,0,0)));
24668    i"
24669  repeat s#0 sh[3--1] $> $N,1,1,1,"ic(#"$N"+3+x)" j[2] .,0,0,0,$> rm[-{$N+1}--1] done
24670  map[1] [2] k[0,1]
24671_blend_shapemedian0 :
24672  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0
24673  N={iM} {$N+1},1,1,{s#0} $N,8,1,{s#0} s. x
24674  f[1] ">
24675    begin(siz = vector"$N"());
24676    k = i;
24677    if (k,
24678      k1 = k - 1;
24679      k2 = k + 2;
24680      hk2 = h(#k2);
24681      copy(i[#k2,siz[k1]++],I(#0),s#0,hk2,whd#0);
24682      if (siz[k1]>=hk2,resize(#k2,1,round(1.5*hk2+1),1,s#0,0,0));
24683    );
24684    end(repeat (size(siz),k, resize(#k+3,1,siz[k],1,s#0,0,0)));
24685    i"
24686  repeat s#0 sh[3--1] $> $N,1,1,1,"ic(#"$N"+3+x)" j[2] .,1,0,0,$> rm[-{$N+1}--1] done
24687  map[1] [2] k[0,1]
24688_blend_shapemin :
24689  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0 {iM+1},1,1,{0,s},inf
24690  f[0] ">i(#2,i(#1,x,y,z,0),0,0,c) = min(i(#2,i(#1,x,y,z,0),0,0,c),i);i"
24691  map[1] [2] rm.
24692_blend_shapemin0 :
24693  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0 {iM+1},1,1,{0,s},inf
24694  f[0] ">i(#2,i(#1,x,y,z,0),0,0,c) = min(i(#2,i(#1,x,y,z,0),0,0,c),i);i"
24695  point. 0,0,0,1,0 map[1] [2] rm.
24696_blend_shapemax :
24697  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0 {iM+1},1,1,{0,s},-inf
24698  f[0] ">i(#2,i(#1,x,y,z,0),0,0,c) = max(i(#2,i(#1,x,y,z,0),0,0,c),i);i"
24699  map[1] [2] rm.
24700_blend_shapemax0 :
24701  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0 {iM+1},1,1,{0,s},-inf
24702  f[0] ">i(#2,i(#1,x,y,z,0),0,0,c) = max(i(#2,i(#1,x,y,z,0),0,0,c),i);i"
24703  point. 0,0,0,1,0 map[1] [2] rm.
24704_blend_softburn :
24705  +-[0] 255.1 ^[2] -1 *[2] [1] *[2] -127.5 +-[0] 255 ++[1] 0.1 /[3,4] *[3] 127.5 +[3] 255 +[1] [0] >[1] 255
24706  j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255
24707_blend_softdodge :
24708  +-[1] 255.1 ^[2] -1 *[2] [0] *[2] -127.5 +-[1] 255 ++[0] 0.1 /[3,4] *[3] 127.5 +[3] 255 +[1] [0] >[1] 255
24709  j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255
24710_blend_softlight :
24711  +/[0] 255 /[1] 255 +sqr. *[2] [1] *[1] [3] *[1] -2 *[2] 2 +[1-3] *[1] 255 c[1] 0,255
24712_blend_stamp :
24713  *[1] 2 +[1] [0] -[1] 255 c[1] 0,255
24714_blend_subtract :
24715  -[1] [0] *[1] -1 c[1] 0,255
24716_blend_value :
24717  to_color sh 0,2 rgb2hsv[2,3] sh[2] 0,1 j[1] [4] rm[4] hsv2rgb[2,3] rm[2,3]
24718_blend_vividlight :
24719  *[1] 2 +blend burn -[1] 256 +blend[0,1] dodge >=[1] 0
24720  j[2] [3],0,0,0,0,1,[1] rm[1,3]
24721_blend_xor :
24722  xor[1] [0]
24723
24724#@cli blend_edges : smoothness[%]>=0
24725#@cli : Blend selected images togethers using 'edges' mode.
24726#@cli : $ image.jpg testimage2d {w},{h} +blend_edges 0.8
24727blend_edges : check {$1>=0}
24728  e[^-1] "Blend image$? using 'edges' mode, with smoothness $1."
24729  if $!>1 to_rgb ri[^0] [0],0,0,0.5,0.5 repeat $! l[$>]
24730    +gradient_norm +. 1 b. $1 n. 1,10 sqr. s.. c *[-4--2] . a[-4--1] c
24731  endl done ri[^0] [0],0,0,0.5,0.5 + s. c /[-4--2] . rm. a[-3--1] c fi
24732
24733#@cli blend_fade : [fading_shape]
24734#@cli : Blend selected images together using specified fading shape.
24735#@cli : $ image.jpg testimage2d {w},{h} 100%,100%,1,1,'cos(y/10)' normalize[-1] 0,1 +blend_fade[0,1] [2]
24736blend_fade :
24737  e[^-1] "Blend image$? together using fading pattern $1."
24738  r ${-max_whds},0
24739  pass$1 0 r. [0],[0],[0],100%,1 max. 0 min. {$!-2}
24740  repeat $!-1 +-. $> abs. -. 1 *. -1 max. 0 *[$>,-1] done rm.
24741  +
24742
24743_fade :
24744  ri.. ...,5 ri. ..,3 c. $1%,$2% n. 0,1 j... ..,0,0,0,0,1,. rm[-2,-1]
24745
24746#@cli blend_median
24747#@cli : Blend selected images together using 'median' mode.
24748#@cli : $ image.jpg testimage2d {w},{h} +mirror[0] y +blend_median
24749blend_median :
24750  e[^-1] "Blend image$? using 'median' mode."
24751  if $!<2 return fi
24752  to_colormode 0 r ${-max_whd},100%,0,0,0.5,0.5,0.5
24753  if $!==2 + / 2
24754  else
24755    whds={w},{h},{d},{s} r 100%,100%,{d*s},1,-1 a c
24756    100%,100%,100%,1,"med(I(#0))" k. r $whds,-1
24757  fi
24758
24759#@cli blend_seamless : _is_mixed_mode={ 0 | 1 },_inner_fading[%]>=0,_outer_fading[%]>=0
24760#@cli : Blend selected images using a seamless blending mode (Poisson-based).
24761#@cli : Default values: 'is_mixed=0', 'inner_fading=0' and 'outer_fading=100%'.
24762blend_seamless : check "${2=0}>=0 && ${3=100%}>=0" skip ${1=0}
24763  s0="non-mixed" s1="mixed"
24764  e[^-1] "Blend image$? using seamless mode (Poisson-based), in "${s{$1!=0}}" mode with inner fading $2 and
24765              outer fading $3."
24766  to_a[^0] r {0,w+32},{0,h+32},1,100%,0,0,0.5,0.5 # Avoid periodic boundaries problems.
24767
24768  if ['$3']!='100%' # With outer fading.
24769    repeat $!-1 l[0,1]
24770      +blend_seamless $1,$2,100% channels.. 100% !=.. 0 distance.. 1
24771      iM={-2,iM} ic={if(${is_percent\ $3},2*$3*$iM,1+$3)}
24772      if $ic<=$iM c.. 0,{max(1,$ic)} n.. 0,1
24773      else n.. 0,{max(0,2-$ic/$iM)}
24774      fi
24775      *.. -1 +.. 1
24776      j[0] [2],0,0,0,0,1,[1] rm[1,2]
24777    endl done
24778  else # Without outer fading.
24779
24780    repeat $!-1 l[0,1]
24781
24782      # Get background average color.
24783      +r[0] 1,1,1,100%,2 avg={^} rm.
24784
24785      # Compute mixed gradients of background and top layer.
24786      split_opacity. !=. 0 *.. . erode. 3
24787      g[0,1] xy,1
24788      *[-3,-2] .
24789
24790      # Modify mask if 'mixed' mode selected.
24791      if $1
24792        +a[0,1] c +a[2,3] c norm[-2,-1]
24793        <[-2,-1] *[-2,-1]
24794      fi
24795
24796      # Compute the desired gradient map.
24797      if $2
24798        distance. 0
24799        iM={iM} ic={if(${is_percent\ $2},2*$2*$iM,1+$2)}
24800        if $ic<=$iM c. 0,{max(1,$ic)} n. 0,1
24801        else n. 0,{max(0,2-$ic/$iM)}
24802        fi
24803      fi
24804
24805      j[-5] ...,0,0,0,0,1,.
24806      j[-4] ..,0,0,0,0,1,.
24807      rm[-3--1]
24808
24809      # Compute divergence (right-hand term of Poisson eq.)
24810      g[0] x,-1 g[1] y,-1 +
24811
24812      # Inverse Laplacian and renormalize
24813      ilaplacian 0
24814      +fc. $avg
24815      +[-2,-1]
24816      c 0,255
24817
24818    endl done
24819  fi
24820  z 16,16,{w-17},{h-17}
24821
24822#@cli fade_diamond : 0<=_start<=100,0<=_end<=100
24823#@cli : Create diamond fading from selected images.
24824#@cli : Default values: 'start=80' and 'end=90'.
24825#@cli : $ image.jpg testimage2d {w},{h} +fade_diamond 80,85
24826fade_diamond : skip ${1=70},${2=90}
24827  e[^-1] "Create ($1%,$2%) diamond-shaped fading from image$?."
24828  repeat int($!/2) l[$>,{$>+1}]
24829    (0,1,0;1,1,1;0,1,0) _fade $1,$2
24830  endl done
24831
24832#@cli fade_linear : _angle,0<=_start<=100,0<=_end<=100
24833#@cli : Create linear fading from selected images.
24834#@cli : Default values: 'angle=45', 'start=30' and 'end=70'.
24835#@cli : $ image.jpg testimage2d {w},{h} +fade_linear 45,48,52
24836fade_linear : skip ${1=45},${2=30},${3=70}
24837  e[^-1] "Create ($2%,$3%) linear fading from image$?, with angle $1 deg."
24838  repeat int($!/2) l[$>,{$>+1}]
24839     64,64,1,1,"x*cos($1*pi/180) + y*sin($1*pi/180)" _fade $2,$3
24840  endl done
24841
24842#@cli fade_radial : 0<=_start<=100,0<=_end<=100
24843#@cli : Create radial fading from selected images.
24844#@cli : Default values: 'start=30' and 'end=70'.
24845#@cli : $ image.jpg testimage2d {w},{h} +fade_radial 30,70
24846fade_radial : skip ${1=30},${2=70}
24847  e[^-1] "Create ($1%,$2%) radial fading from image$?."
24848  repeat int($!/2) l[$>,{$>+1}]
24849    100%,100% =. 1,50%,50% distance. 1 _fade $1,$2
24850  endl done
24851
24852#@cli fade_x : 0<=_start<=100,0<=_end<=100
24853#@cli : Create horizontal fading from selected images.
24854#@cli : Default values: 'start=30' and 'end=70'.
24855#@cli : $ image.jpg testimage2d {w},{h} +fade_x 30,70
24856fade_x : skip ${1=30},${2=70}
24857  e[^-1] "Create ($1%,$2%) horizontal fading from image$?."
24858  repeat int($!/2) l[$>,{$>+1}] (0,1) _fade $1,$2 endl done
24859
24860#@cli fade_y : 0<=_start<=100,0<=_end<=100
24861#@cli : Create vertical fading from selected images.
24862#@cli : Default values: 'start=30' and 'end=70'.
24863#@cli : $ image.jpg testimage2d {w},{h} +fade_y 30,70
24864fade_y : skip ${1=30},${2=70}
24865  e[^-1] "Create ($1%,$2%) vertical fading from image$?."
24866  repeat int($!/2) l[$>,{$>+1}] (0;1) _fade $1,$2 endl done
24867
24868#@cli fade_z : 0<=_start<=100,0<=_end<=100
24869#@cli : Create transversal fading from selected images.
24870#@cli : Default values: 'start=30' and 'end=70'.
24871fade_z : skip ${1=30},${2=70}
24872  e[^-1] "Create ($1%,$2%) transversal fading from image$?."
24873  repeat int($!/2) l[$>,{$>+1}] (0/1) _fade $1,$2 endl done
24874
24875#@cli sub_alpha : [base_image],_opacity_gain>=1
24876#@cli : Compute the minimal alpha-channel difference (opposite of alpha blending) between the selected images
24877#@cli : and the specified base image.
24878#@cli : The alpha difference A-B is defined as the image having minimal opacity, such that alpha_blend(B,A-B) = A.
24879#@cli : Default value: 'opacity_gain=1'.
24880#@cli : $ image.jpg testimage2d {w},{h} +sub_alpha[0] [1] display_rgba
24881sub_alpha : check "${2=1}>=1 && "${"is_image_arg $1"}
24882  e[^-1] "Compute minimal alpha-channel difference between image$? and base image $1, with opacity gain $2."
24883  remove_opacity repeat $! pass$1 0 l[$>,-1]
24884    to_colormode 0 r ${-max_whd},100%,0,0,0.5,0.5  # Normalize image dimensions.
24885
24886    # Estimate minimal alpha-channel.
24887    +>[0,1] *[2] 255
24888    -[0,2] [1] replace[2] 0,1
24889    i[2] [0] /[2,3]
24890    compose_channels[2] max
24891    *[2] $2 c[2] 0,1
24892
24893    # Synthetize alpha-difference image.
24894    +replace[2] 0,1 /[0,3] +[0,1] *[1] 255 a c
24895
24896  endl done
24897
24898#---------------------------------------------
24899#
24900#@cli :: Image Sequences and Videos
24901#
24902#---------------------------------------------
24903
24904#@cli animate : filter_name,"param1_start,...,paramN_start","param1_end,...,paramN_end",nb_frames>=0,\
24905# _output_frames={ 0 | 1 },_output_filename : delay>0,_back and forth={ 0 | 1 }
24906#@cli : Animate filter from starting parameters to ending parameters or animate selected images
24907#@cli : in a display window.
24908#@cli : Default value: 'delay=30'.
24909#@cli : $ image.jpg animate flower,"0,3","20,8",9
24910animate : skip ${1=30},${2=0},${3=""},${4=10},${5=1},"${6=}"
24911  if "isnum($1)"
24912    e[0--3] "Animate image$?, with a delay of $1 ms"${"if $2 u \", in back-and-forth mode\" else u \"\" fi"}.
24913    if !$! return fi
24914    speed,pause,direction,scale,frame=$1,-1,1,1,0
24915    is_same_size={"res = 1; s = [ w#0,h#0 ]; for (k = 1, k<l && res, ++k, res = (s==[ w#k,h#k ]))"}
24916
24917    w[0] ${fitscreen[]\ {0,[w,h]}},1,0,{0,b}.{0,x}
24918    do
24919      title={$frame,b}{`narg({$frame,x})?_'.':0`}{$frame,x}
24920      if $is_same_size
24921        w[$frame] -1,-1,1,0,$title
24922      else
24923        w[$frame] {$scale*[${fitscreen[]\ {$frame,[w,h]}}]},1,0,$title
24924      fi
24925      frame+=$direction
24926      if $2
24927        if $frame==-1 frame=0 direction=1
24928        elif $frame==$! frame={$!-1} direction=-1
24929        fi
24930      else frame%=$!
24931      fi
24932      wait $speed
24933
24934      # Increase window size.
24935      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} scale*=1.5 wait -1 fi
24936
24937      # Decrease window size.
24938      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} scale/=1.5 wait -1 fi
24939
24940      # Reset window size.
24941      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} scale=1 wait -1 fi
24942      if {*,o} speed={min(500,max(10,$speed-10*{*,o}))} wait -1 fi
24943      if {*,SPACE}
24944        if $pause>=0 direction=$pause pause=-1
24945        else pause=$direction direction=0 fi
24946        wait -1
24947      fi
24948
24949    while {*}" && "!{*,Q}" && "!{*,ESC} w 0
24950  else
24951    e[0--3] "Compute animated version of filter '$1', from parameters $2 to $3 with $4 frames."
24952    if !($5||narg("$6")) return fi
24953    ($2) ($3) y[-2,-1] x a[-2,-1] y r. 100%,$4,1,1,3 mv. 0 rprogress 0
24954    repeat $!-1,u
24955      e[] " > Animate image ["$>"]"
24956      repeat $4 +l[0,1]
24957        -$1. {0,@{$>*{0,w}}-{($>+1)*{0,w}-1}} rm[0]
24958        if narg("$6") o ${filename\ "$6",$u,$>} fi
24959        if !$5 rm fi
24960        rprogress {100*($>+1)/$4}
24961        e[] "\r > Animate image ["$u"] : Frame "{$>+1}"/$4    "
24962      endl done
24963    rm[1] done rm[0]
24964  fi
24965
24966#@cli apply_camera : _"command",_camera_index>=0,_skip_frames>=0,_output_filename
24967#@cli : Apply specified command on live camera stream, and display it on display window [0].
24968#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
24969#@cli : Default values: 'command=""', 'camera_index=0' (default camera), 'skip_frames=0' and 'output_filename=""'.
24970apply_camera : check_opencv $0 skip "${1=},${4=}" check "${2=0}>=0 && ${3=0}>=0"
24971  e[^-1] "Apply command '$1' on camera stream ""#$2, with $3 frames skip and output filename '$4'."
24972  is_ext "$4",avi is_outavi=${}
24973  is_ext "$4",mp4 is_outmp4=${}
24974  l[] i=0 do
24975    camera $2,1,$3 $1 w. -1,-1,"[G'MIC] Camera ""#$2 ("{w}x{h}")"
24976    if narg("$4") if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$4",25,mp4v,1 else o. ${filename\ "$4",$i} i+=1 fi fi
24977    if {*,S} o. gmic_camera.png fi
24978    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
24979    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
24980    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
24981    rm.
24982  while {*}" && "!{*,ESC}" && "!{*,Q} camera $2,0 endl
24983
24984#@cli apply_files : "filename_pattern",_"command",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\
24985# _output_filename
24986#@cli : Apply a G'MIC command on specified input image files, in a streamed way.
24987#@cli : If a display window is opened, rendered frames are displayed in it during processing.
24988#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image file
24989#@cli : extension (saved as a sequence of images).
24990#@cli : Default values: 'command=(undefined)', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \
24991# and 'output_filename=(undefined)'.
24992apply_files : check "isint(${3=0}) && $3>=0 && isint(${4=-1}) && ($4>=0 || $4==-1) && ${5=1}>=1" skip "${2=},${6=}"
24993  e[^-1] "Apply command '$2' on input image files '$1', with first frame $3, last frame $4, frame step $5 and
24994          output filename '$6'.\n"
24995  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _apply_stream[] "${_file{$frame+1}}","$2",${3-5},"$6"
24996
24997#@cli apply_video : video_filename,_"command",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\
24998# _output_filename
24999#@cli : Apply a G'MIC command on all frames of the specified input video file, in a streamed way.
25000#@cli : If a display window is opened, rendered frames are displayed in it during processing.
25001#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
25002#@cli : file extension (saved as a sequence of images).
25003#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
25004#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'.
25005apply_video : check_opencv $0 check "isint(${3=0}) && $3>=0 && isint(${4=-1}) && ($4>=0 || $4==-1) && ${5=1}>=1"
25006              skip "${2=},${6=}"
25007  e[^-1] "Apply command '$2' on input video file '$1', with first frame $3, last frame $4, frame step $5 and
25008          output filename '$6'.\n"
25009  _N= v + _apply_stream[] "\"$1\",$frame","$2",${3-5},"$6"
25010
25011_apply_stream : skip "${2=},${6=}"
25012  is_ext "$6",avi is_outavi=${}
25013  is_ext "$6",mp4 is_outmp4=${}
25014  frame=$3 i=0 go_on=1
25015  do
25016    l[] $1 onfail go_on=0 endl
25017    if $go_on
25018      e[] "\r  > Frame ""#"$frame$_N"        "
25019      frame+=$5
25020      l $2 onfail error[0--5] "Command 'apply_stream': Specified command errored: "${} endl
25021      if !$! continue fi
25022      if narg("$6")
25023        if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$6",25,mp4v,1 else o. ${filename\ "$6",$i} i+=1 fi
25024      fi
25025      if {*}
25026        title="[G'MIC] Frame ""#"$frame
25027        if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,0,$title
25028        else w. -1,-1,0,$title
25029        fi
25030        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
25031        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
25032        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
25033      fi
25034      rm.
25035    fi
25036  while $go_on" && "($4==-1" || "$frame<=$4)
25037  if $is_outavi||$is_outmp4 o[] "$6",25,mp4v,0 fi
25038
25039#@cli average_files : "filename_pattern",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename
25040#@cli : Average specified input image files, in a streamed way.
25041#@cli : If a display window is opened, rendered frames are displayed in it during processing.
25042#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
25043#@cli : file extension (saved as a sequence of images).
25044#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'.
25045average_files : check "isint(${2=0}) && $2>=0 && isint(${3=-1}) && ($3>=0 || $3==-1) && ${4=1}>=1" skip "${5=}"
25046  e[^-1] "Average input image files '$1', with first frame $2, last frame $3, frame step $4 and
25047          output filename '$5'.\n"
25048  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _average_stream[] "${_file{$frame+1}}",${2-4},"$5"
25049
25050#@cli average_video : video_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename
25051#@cli : Average frames of specified input video file, in a streamed way.
25052#@cli : If a display window is opened, rendered frames are displayed in it during processing.
25053#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
25054#@cli : file extension (saved as a sequence of images).
25055#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
25056#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'.
25057average_video : check_opencv $0 check "isint(${2=0}) && $2>=0 && isint(${3=-1}) && ($3>=0 || $3==-1) && ${4=1}>=1"
25058                skip "${5=}"
25059  e[^-1] "Average frames of input video file '$1', with first frame $2, last frame $3, frame step $4 and
25060          output filename '$5'.\n"
25061  _N= v + _average_stream[] "\"$1\",$frame",${2-4},"$5"
25062
25063_average_stream : skip "${5=}"
25064  is_ext "$5",avi is_outavi=${}
25065  is_ext "$5",mp4 is_outmp4=${}
25066  frame=$2 i=0 go_on=1 N=0
25067  imM=inf,-inf
25068  do
25069    l[] $1 onfail go_on=0 endl
25070    if $go_on
25071      e[] "\r  > Frame ""#"$frame$_N"        "
25072      imM={v=[$imM];[min(im,v[0]),max(iM,v[1])]}
25073      N+=1
25074      if $!>1 r ${-max_whds} + fi
25075      if narg("$5")
25076        +/. $N c. $imM
25077        if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$5",25,mp4v,1 else o. ${filename\ "$5",$i} i+=1 fi
25078        rm.
25079      fi
25080      if {*}
25081        title="[G'MIC] Frame ""#"$frame
25082        +n 0,255
25083        if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,0,$title
25084        else w. -1,-1,0,$title
25085        fi
25086        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
25087        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
25088        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
25089        rm.
25090      fi
25091      frame+=$4
25092    fi
25093  while $go_on" && "($3==-1" || "$frame<=$3)
25094  / $N c $imM
25095  if $is_outavi||$is_outmp4 o[] "$5",25,mp4v,0 fi
25096
25097#@cli fade_files : "filename_pattern",_nb_inner_frames>0,_first_frame>=0,_last_frame={ >=0 | -1=last },\
25098# _frame_step>=1,_output_filename
25099#@cli : Generate a temporal fading from specified input image files, in a streamed way.
25100#@cli : If a display window is opened, rendered frames are displayed in it during processing.
25101#@cli : The output filename may have extension 'avi' or 'mp4' (saved as a video), or any other usual image
25102#@cli : file extension (saved as a sequence of images).
25103#@cli : Default values: 'nb_inner_frames=10', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \
25104# and 'output_filename=(undefined)'.
25105fade_files : check "isint(${2=10}) && $2>0 && isint(${3=0}) && $3>=0 &&
25106                    isint(${4=-1}) && ($4>=0 || $4==-1) && ${5=1}>=1" skip "${6=}"
25107  e[^-1] "Fade input image files '$1', with $2 inner frames, first frame $3, last frame $4, frame step $5 and
25108          output filename '$6'.\n"
25109  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _fade_stream[] "${_file{$frame+1}}",${2-5},"$6"
25110
25111#@cli fade_video : video_filename,_nb_inner_frames>0,_first_frame>=0,_last_frame={ >=0 | -1=last },\
25112# _frame_step>=1,_output_filename
25113#@cli : Create a temporal fading sequence from specified input video file, in a streamed way.
25114#@cli : If a display window is opened, rendered frames are displayed in it during processing.
25115#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
25116#@cli : Default values: 'nb_inner_frames=10', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \
25117# and 'output_filename=(undefined)'.
25118fade_video : check_opencv $0 check "isint(${2=10}) && $2>0 && isint(${3=0}) && $3>=0 &&
25119             isint(${4=-1}) && ($4>=0 || $4==-1) && ${5=1}>=1" skip "${6=}"
25120  e[^-1] "Fade frames of input video file '$1', with $2 inner frames, first frame $3, last frame $4, frame step $5 and
25121          output filename '$6'.\n"
25122  _N= v + _fade_stream[] "\"$1\",$frame",${2-5},"$6"
25123
25124_fade_stream : skip "${6=}"
25125  is_ext "$6",avi is_outavi=${}
25126  is_ext "$6",mp4 is_outmp4=${}
25127  frame=$3 i=0 go_on=1
25128
25129  l $1 onfail go_on=0 endl # Load first image.
25130  if !$go_on return fi
25131  w={w} h={h} s={s}
25132  if {*} w. ${fitscreen\ $w,$h},0,"[G'MIC]" fi
25133  pframe=$frame frame+=$5
25134  do
25135    l[] $1 onfail go_on=0 endl
25136    if !$go_on break fi
25137    to_colormode. $s r. $w,$h
25138    repeat $2+2 if $<
25139      title="[G'MIC] Frame ""#"$pframe" -> ""#"$frame$_N" ("{1+$>}/$2")"
25140      e[] "\r  - "$title
25141      +j[0] [1],0,0,0,0,{$>/($2+1)}
25142      if narg("$6")
25143        if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$6",25,mp4v,1 else filename "$6",$i i+=1 o. ${} fi
25144      fi
25145      if {*}
25146        w. -1,-1,0,$title
25147        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
25148        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
25149        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
25150      fi
25151      rm.
25152    fi done
25153    rm[0] pframe=$frame frame+=$5
25154  while $go_on" && "($4==-1" || "$frame<=$4)
25155
25156  # Output last frame.
25157  if narg("$6")
25158    if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o[] "$6",25,mp4v,0 else filename "$6",$i o. ${} fi
25159  fi
25160  rm
25161
25162#@cli files2video : "filename_pattern",_output_filename,_fps>0,_codec
25163#@cli : Convert several files into a single video file.
25164#@cli : Default values: 'output_filename=output.mp4', 'fps=25' and 'codec=mp4v'.
25165files2video : check "isint(${3=25}) && $3>0" skip "${2=output.mp4}",${4=mp4v}
25166  files=${"files \"$1\""} arg2var _file,$files nb_files=${}
25167  ('$files') if w>128 z. 0,127 s_files={t}... else s_files=$files fi rm.
25168  e[^-1] "Convert image files '"$s_files"' into frames of output video '$2', with $3 fps and $4 codec.\n"
25169  repeat $nb_files l[]
25170    file=${_file{$>+1}}
25171    _file=${basename\ $file}
25172    e[] "\r - Image "{1+$>}/$nb_files" ["$_file"] -> [$2]                    "
25173    i $file o "$2",$3,$4,1
25174    rm
25175  onfail e[] "\n - Error occurred on input file '"$file"'.\n"
25176  endl done
25177  o $"$2",0,0,0
25178
25179#@cli median_files : "filename_pattern",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\
25180# _frame_rows[%]>=1,_is_fast_approximation={ 0 | 1 }
25181#@cli : Compute the median frame of specified input image files, in a streamed way.
25182#@cli : If a display window is opened, rendered frame is displayed in it during processing.
25183#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1', 'frame_rows=20%' \
25184# and 'is_fast_approximation=0'.
25185median_files : check "isint(${2=0}) && $2>=0 && isint(${3=-1}) && ($3>=0 || $3==-1) &&
25186                     ${4=1}>=1 && ${5=20%}>0 && isnum(${6=0})"
25187  s0="fast" s1="precise"
25188  e[^-1] "Compute median of input image files '$1', with first frame $2, last frame $3, frame step $4,
25189              frame rows $5, using "${s{!$6}}" algorithm."
25190  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${}
25191  l[]
25192    ${_file{$frame+1}} nm. res f. 0
25193    v + _median_stream "${_file{$frame+1}}",${2-6} v -
25194  endl
25195
25196#@cli median_video : video_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\
25197# _frame_rows[%]>=1,_is_fast_approximation={ 0 | 1 }
25198#@cli : Compute the median of all frames of an input video file, in a streamed way.
25199#@cli : If a display window is opened, rendered frame is displayed in it during processing.
25200#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
25201#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1', 'frame_rows=100%' \
25202# and 'is_fast_approximation=1'.
25203median_video : check_opencv $0 check "isint(${2=0}) && $2>=0 && isint(${3=-1}) && ($3>=0 || $3==-1) &&
25204               ${4=1}>=1 && ${5=100%}>0 && isnum(${6=1})"
25205  s0="fast" s1="precise"
25206  e[^-1] "Compute median frame of input video file '$1', with first frame $2, last frame $3, frame step $4,
25207              frame rows $5, using "${s{!$6}}" algorithm."
25208  _N=
25209  l[]
25210    "$1",0 nm. res f. 0
25211    v + _median_stream "\"$1\",$frame",${2-6} v -
25212  endl
25213
25214_median_stream :
25215
25216  # Retrieve min/max values of all frames when fast method is used.
25217  if $6
25218    e[] "- Retrieve min/max values of all frames.\n"
25219    frame=$2 go_on=1
25220    imM=inf,-inf
25221    do
25222      l[] $1 onfail go_on=0 endl
25223      if $go_on
25224        e[] "\r    > Frame ""#"$frame$_N"        "
25225        imM={v=[$imM];[min(im,v[0]),max(iM,v[1])]}
25226        if {*}
25227          title="[G'MIC] Frame ""#"$frame
25228          if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,1,$title
25229          else w. -1,-1,1,$title
25230          fi
25231        fi
25232        rm.
25233        frame+=$4
25234      fi
25235    while $go_on" && "($3==-1" || "$frame<=$3)
25236    _N=/{$frame-$4}
25237    fact={v=[$imM];dv=v[1]-v[0];dv<=0?0:255/dv}
25238  fi
25239
25240  # Start median computation.
25241  h1={h-1} drows={round(${is_percent\ $5}?$5*h:$5)}
25242  nb_iter={round(h/$drows,1,1)}
25243  repeat $nb_iter
25244    row0={$drows*$>} row1={0,min(h,$row0+$drows-1)}
25245    e[] "- Iteration \#"{$>+1}/$nb_iter": Load rows "$row0-$row1/$h1".\n"
25246    frame=$2 go_on=1
25247
25248    if $6
25249      # Fast method : compute median frame using streamed histogram computation.
25250      N=0
25251      {w},$drows,256,{s} nm. hist
25252      do
25253        l[] $1 nm. img onfail go_on=0 endl
25254        if $img
25255          e[] "\r    > Frame ""#"$frame$_N"        "
25256          if {*}" && "!$>
25257            title="[G'MIC] Frame ""#"$frame
25258            if !narg($wh) wh=${fitscreen[]\ {img,[w,h]}} w[img] $wh,1,$title
25259            else w[img] -1,-1,1,$title
25260            fi
25261          fi
25262          rows[img] $row0,$row1 f[img] ":++i(#-2,x,y,round(i*"$fact"),c)"
25263          rm[img]
25264          frame+=$4 N+=1
25265        fi
25266      while $go_on" && "($3==-1" || "$frame<=$3)
25267      cumulate[hist] z
25268      N2={int($N/2)}
25269      [hist],[hist],1,[hist]
25270      if $N%2 # Odd number of frames.
25271        f. ":go_on = 1; for (z = 0, i(#"$hist",x,y,z,c)<"$N2" && z<256, ++z); z"
25272      else # Even number of frames.
25273        f. ":begin(N2p = "$N2"; N2n = N2p + 1);
25274             go_on = 1;
25275             for (zp = 0, i(#"$hist",x,y,zp,c)<N2p && zp<256, ++zp);
25276             for (zn = zp, i(#"$hist",x,y,zn,c)<N2n && zn<256, ++zn);
25277             0.5*(zn + zp);"
25278      fi
25279      rm..
25280      /. $fact c. $imM
25281
25282    else
25283      # Slow method : compute median frame using temporal quicksort.
25284      l[]
25285        do
25286          l[] $1 nm. img onfail go_on=0 endl
25287          if $go_on
25288            e[] "\r    > Frame ""#"$frame$_N"        "
25289            if {*}" && "!$>
25290              title="[G'MIC] Frame ""#"$frame
25291              if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,1,$title
25292              else w. -1,-1,1,$title
25293              fi
25294            fi
25295            rows. $row0,$row1
25296            frame+=$4
25297          fi
25298        while $go_on" && "($3==-1" || "$frame<=$3)
25299        e[] "\r    > Compute median blending of "$!" frames."
25300        __median_stream
25301      endl
25302    fi
25303
25304    _N=/{$frame-$4}
25305    j[res] .,0,$row0
25306    if {*} w[res] -1,-1,1,"[G'MIC] Iteration ""#"$> fi
25307    rm.
25308
25309  done
25310  e[] "- Done!"
25311
25312# Median blending optimized to deal with a lot of input frames.
25313__median_stream :
25314  if $!<2 return
25315  elif $!==2 + / 2
25316  else
25317    f. ":
25318      stack = vector"{0,2*$!}"();
25319      stacksize = 0;
25320      push(elt0,elt1) = (stack[stacksize++] = elt0; stack[stacksize++] = elt1);
25321      pop() = (_s1 = stack[--stacksize]; _s0 = stack[--stacksize]; [_s0,_s1]);
25322      push(0,"$!" - 1);
25323      while (stacksize>0,
25324        range = pop();
25325        lo = range[0];
25326        hi = range[1];
25327        pivot = i(#int((lo + hi)/2));
25328        while (lo<=hi,
25329           while (i(#lo)<pivot, ++lo);
25330           while (pivot<i(#hi), --hi);
25331           if (lo<=hi, _tmp = i(#lo); i(#lo++) = i(#hi); i(#hi--) = _tmp);
25332        );
25333        if (range[0]<hi,push(range[0],hi));
25334        if (lo<range[1],push(lo,range[1]));
25335      )"
25336    if $!%2 k[{int($!/2)}]
25337    else k[{int($!/2-1)},{$!/2}] + / 2
25338    fi
25339  fi
25340
25341#@cli morph : nb_inner_frames>=1,_smoothness>=0,_precision>=0
25342#@cli : Create morphing sequence between selected images.
25343#@cli : Default values: 'smoothness=0.1' and 'precision=4'.
25344#@cli : $ image.jpg +rotate 20,1,1,50%,50% morph 9
25345morph : check "$1>=0.5 && ${2=0.1}>=0 && ${3=4}>=0"
25346  nbf={round($1)}
25347  e[^-1] "Create morphing sequence between image$?, with "$nbf" inner frames, smoothness $2 and precision $3.\n"
25348  nchan=${-max_s} if $nchan<=4 to_colormode $nchan else channels 0,{$nchan-1} fi
25349  ri[^0] [0],3
25350  repeat $!-1 nm={$>,n} l[$<,{$<+1}]
25351    e[] "\r > Morph image "$>" to image "{$>+1}".    "
25352    +equalize[0,1] n[-2,-1] 0,255
25353    +displacement[3] [2],$2,$3 +displacement[2] [3],$2,$3 rm[-4,-3]
25354    repeat $nbf+2 if $>&&$<
25355      t={$>/($nbf+1)} omt={1-$t}
25356      +*[2] $t +warp[0] .,1,1,1 rm.. *. $omt
25357      +*[3] {1-$t} +warp[1] .,1,1,1 rm.. *. $t
25358      +[-2,-1]
25359    fi done
25360    rm[2,3] mv[2--1] 1 nm $nm
25361  endl done
25362
25363#@cli morph_files : "filename_pattern",_nb_inner_frames>0,_smoothness>=0,_precision>=0,\
25364# _first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename
25365#@cli : Generate a temporal morphing from specified input image files, in a streamed way.
25366#@cli : If a display window is opened, rendered frames are displayed in it during processing.
25367#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
25368#@cli : file extension (saved as a sequence of images).
25369#@cli : Default values: 'nb_inner_frames=10', 'smoothness=0.1', 'precision=4', 'first_frame=0', 'last_frame=-1', \
25370# 'frame_step=1' and 'output_filename=(undefined)'.
25371morph_files : check "isint(${2=10}) && $2>0 && ${3=0.1}>=0 && ${4=4}>=0 &&
25372                     isint(${5=0}) && $5>=0 && isint(${6=-1}) && ($6>=0 || $6==-1) && ${7=1}>=1" skip "${8=}"
25373  e[^-1] "Morph input image files '$1', with $2 inner frames, smoothness $3, precision $4, first frame $5,
25374          last frame $6, frame step $7 and output filename '$8'.\n"
25375  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} _morph_stream[] "${_file{$frame+1}}",${2-7},"$8"
25376
25377#@cli morph_rbf : nb_inner_frames>=1,xs0[%],ys0[%],xt0[%],yt0[%],...,xsN[%],ysN[%],xtN[%],ytN[%]
25378#@cli : Create morphing sequence between selected images, using RBF-based interpolation.
25379#@cli : Each argument (xsk,ysk)-(xtk,ytk) corresponds to the coordinates of a keypoint
25380#@cli : respectively on the source and target images. The set of all keypoints define the overall image deformation.
25381morph_rbf : check "$1>=0.5"
25382  nbf={round($1)}
25383  e[^-1] "Create morphing sequence between image$? using RBF interpolation, "\
25384    "with "$nbf" inner frames and keypoints ($*)."
25385  $=arg N={($#-1)/4}
25386  if int($N)!=$N error[0--2] "Command 'morph_rbf': Wrong number of arguments ($#)." fi
25387  ri[^0] [0],3
25388  repeat $!-1 nm={$>,n} l[$<,{$<+1}]
25389    e[] "\r > Morph image "$>" to image "{$>+1}".    "
25390
25391    # Retrieve absolute keypoints coordinates.
25392    4,$N
25393    repeat wh a=${arg{2+$>}} isp=${"is_percent "$a} eval i[$>]=$isp?($>%2?w#0:h#0)*$a:$a done
25394    permute. yzcx
25395
25396    # Generate forward and backward warping fields.
25397    +f. "[i0,i1,i2-i0,i3-i1]"
25398    f.. "[i2,i3,i0-i2,i1-i3]"
25399    rbf[-2,-1] {0,[w,h]}
25400
25401    # Compute morphing sequence.
25402    repeat $nbf+2 if $>&&$<
25403      [0],[0],1,[0],"
25404        const interpolation = 1;
25405        const boundary = 3;
25406        const t = "$>"/("$nbf"+1);
25407        const omt = 1 - t;
25408        begin(print([t,omt]));
25409        ub = i(#2,x,y,0,0);
25410        vb = i(#2,x,y,0,1);
25411        uf = i(#3,x,y,0,0);
25412        vf = i(#3,x,y,0,1);
25413        omt*I(#0,x - t*uf,y - t*vf) + t*I(#1,x - omt*ub,y - omt*vb)"
25414    fi done
25415    rm[2,3] mv[2--1] 1 nm $nm
25416  endl done
25417
25418#@cli morph_video : video_filename,_nb_inner_frames>0,_smoothness>=0,_precision>=0,\
25419# _first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename
25420#@cli : Generate a temporal morphing from specified input video file, in a streamed way.
25421#@cli : If a display window is opened, rendered frames are displayed in it during processing.
25422#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
25423#@cli : file extension (saved as a sequence of images).
25424#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
25425#@cli : Default values: 'nb_inner_frames=10', 'smoothness=0.1', 'precision=4', 'first_frame=0', 'last_frame=-1', \
25426# 'frame_step=1' and 'output_filename=(undefined)'.
25427morph_video : check_opencv $0 check "isint(${2=10}) && $2>0 && ${3=0.1}>=0 && ${4=4}>=0 &&
25428              isint(${5=0}) && $5>=0 && isint(${6=-1}) && ($6>=0 || $6==-1) && ${7=1}>=1" skip "${8=}"
25429  e[^-1] "Morph frames of input video file '$1', with $2 fading frames, smoothness $3, precision $4, first frame $5,
25430          last frame $6, frame step $7 and output filename '$8'.\n"
25431  _N= v + _morph_stream[] "\"$1\",$frame",${2-7},"$8"
25432
25433_morph_stream : skip "${8=}"
25434  is_ext "$8",avi is_outavi=${}
25435  is_ext "$8",mp4 is_outmp4=${}
25436  frame=$5 i=0 go_on=1
25437
25438  l $1 onfail go_on=0 endl # Load first image.
25439  if !$go_on return fi
25440  w={w} h={h} s={s}
25441  if {*} w. ${fitscreen\ $w,$h},0,"[G'MIC]" fi
25442  pframe=$frame frame+=$7
25443  do
25444    l[] $1 onfail go_on=0 endl
25445    if !$go_on break fi
25446    to_colormode. $s r. $w,$h
25447    cutvals={[min(im#0,im#1),max(iM#0,iM#1)]}
25448    e[] "\r  - Frame ""#"$pframe" -> ""#"$frame"            "
25449    +equalize[0,1] n[-2,-1] 0,255
25450    +displacement[3] [2],$3,$4 +displacement[2] [3],$3,$4 rm[-4,-3]
25451    repeat $2+2 if $<
25452      title="Frame ""#"$pframe" -> ""#"$frame" ("$>/$2")        "
25453      e[] "\r  - "$title
25454      t={$>/($2+1)} omt={1-$t}
25455      +*[2] $t +warp[0] .,1,1,1 rm.. *. $omt
25456      +*[3] {1-$t} +warp[1] .,1,1,1 rm.. *. $t
25457      +[-2,-1] c. $cutvals
25458      if narg("$8")
25459        if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$8",25,mp4v,1 else filename "$8",$i i+=1 o. ${} fi
25460      fi
25461      if {*}
25462        w. -1,-1,0,$title
25463        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
25464        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
25465        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
25466      fi
25467      rm.
25468    fi done
25469    rm[0,-2,-1] pframe=$frame frame+=$7
25470  while $go_on" && "($6==-1" || "$frame<=$6)
25471
25472  # Output last frame.
25473  if narg("$8")
25474    if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o[] "$8",25,mp4v,0 else filename "$8",$i o. ${} fi
25475  fi
25476  rm
25477
25478#@cli register_nonrigid : [destination],_smoothness>=0,_precision>0,_nb_scale>=0
25479#@cli : Register selected source images with specified destination image, using non-rigid warp.
25480#@cli : Default values: 'smoothness=0.2', 'precision=6' and 'nb_scale=0(auto)'.
25481#@cli : $ image.jpg +rotate 20,1,1,50%,50% +register_nonrigid[0] [1]
25482register_nonrigid : check ${is_image_arg\ $1}" && ${2=0.2}>=0 && ${3=5}>0 && ${4=0}>=0"
25483  e[^-1] "Register source image$? with destination image $1, using non-rigid warp with smoothness $2,
25484          precision $3 and $4 scale(s)."
25485  pass$1 0 equalize. n. 0,255
25486  repeat $!-1
25487    +equalize[$>] n. 0,255 +displacement.. .,$2,$3,$4 rm..
25488    warp[$>] .,1,1,1 rm.
25489  done rm.
25490
25491#@cli register_rigid : [destination],_smoothness>=0,\
25492# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25493#@cli : Register selected source images with specified destination image, using rigid warp (shift).
25494#@cli : Default values: 'smoothness=0.1%' and 'boundary_conditions=0'.
25495#@cli : $ image.jpg +shift 30,20 +register_rigid[0] [1]
25496register_rigid : check ${is_image_arg\ $1}" && ${2=0.1%}>=0 && isint(${3=0}) && $3>=0 && $3<=3"
25497  e[^-1] "Register source image$? with destination image $1, using rigid warp with smoothness $2."
25498  m "_register_rigid : b $2 equalize 256 n 0,1"
25499  pass$1 0 W,H,D,S={[w,h,d,s]}
25500  f={max(w,h)/1024}
25501  if $f<=1 # Small image: one-step registration
25502    _register_rigid.
25503    repeat $!-1
25504      if {$>,w!=$W||h!=$H||d!=$D}
25505        error[0--4] "Images have incompatible sizes ("{$>,[w,h,d,s]}") and ("{[$W,$H,$D,$S]}")."
25506      fi
25507      +_register_rigid[$>] phase_correlation. .. shift[$>] {^},0,$3 rm.
25508    done
25509    rm.
25510  else # Large image: two-steps registration
25511    +r. {[min(w,1024),min(h,1024),min(d,1024)]},100%,0,0,0.5,0.5
25512    rr2d.. 1024,1024,0,2
25513    _register_rigid[-2,-1]
25514    repeat $!-2
25515      if {$>,w!=$W||h!=$H||d!=$D}
25516        error[0--4] "Images have incompatible sizes ("{$>,[w,h,d,s]}") and ("{[$W,$H,$D,$S]}")."
25517      fi
25518
25519      # Low scale.
25520      +rr2d[$>] 1024,1024,0,2 _register_rigid.
25521      phase_correlation. ...
25522      s={$f*crop()} rm.
25523
25524      # High scale.
25525      +shift[$>] $s,0,$3
25526      r. {[min(w,1024),min(h,1024),min(d,1024)]},100%,0,0,0.5,0.5 _register_rigid.
25527      phase_correlation. ..
25528      s={[$s]+crop()} rm.
25529
25530      shift[$>] $s,0,$3
25531    done
25532    rm[-2,-1]
25533  fi
25534  um _register_rigid
25535
25536#@cli transition : [transition_shape],nb_added_frames>=0,100>=shading>=0,_single_frame_only={ -1=disabled | >=0 }
25537#@cli : Generate a transition sequence between selected images.
25538#@cli : Default values: 'shading=0' and 'single_frame_only=-1'.
25539#@cli : $ image.jpg +mirror c 100%,100% plasma[-1] 1,1,6 transition[0,1] [2],5
25540transition : check ${is_image_arg\ $1}" && $2>=0 && ${3=0}>=0 && $3<=100" skip ${4=-1}
25541  frame={round($4)} s0=" and shading $3" s1=", shading $3 and single-frame-only "$frame
25542  e[^-1] "Create transition sequence between image$? with $2 added frames, transition shape $1"${s{$4>0}}"."
25543  if $!<2" || "!$2 return fi
25544  to_colormode 0 r ${-max_whd},100%,0,0,0.5,0.5
25545  pass$1 0 norm. r. [0],[0],[0],1,3 n. 0,1 mv. 0
25546  repeat $!-2 l[0,{$<+1},{$<+2}]
25547    nm0={1,n}
25548    if $3 repeat $2 if $4<0" || "$>==$frame
25549      val0={($>+0.5)/$2-$3%}
25550      val1={($>+0.5)/$2+$3%}
25551      +f[0] '(i-$val0)/($val1-$val0)' c. 0,1
25552      +j[2] [1],0,0,0,0,1,. rm..
25553      nm. $nm0\ ""#{1+$>}
25554    fi done else repeat $2 if $4<0" || "$>==$frame
25555      +>=[0] {($>+0.5)/$2}
25556      +j[2] [1],0,0,0,0,1,. rm..
25557      nm. $nm0\ ""#{1+$>}
25558    fi done fi
25559    mv[2] $!
25560  endl done rm[0]
25561
25562#@cli transition3d : _nb_frames>=2,_nb_xtiles>0,_nb_ytiles>0,_axis_x,_axis_y,_axis_z,_is_antialias={ 0 | 1 }
25563#@cli : Create 3D transition sequence between selected consecutive images.
25564#@cli : 'axis_x', 'axis_y' and 'axis_z' can be set as mathematical expressions, depending on 'x' and 'y'.
25565#@cli : Default values: 'nb_frames=10', 'nb_xtiles=nb_ytiles=3', 'axis_x=1', 'axis_y=1', 'axis_z=0' \
25566# and 'is_antialias=1'.
25567#@cli : $ image.jpg +blur 5 transition3d 9 display_rgba
25568transition3d : check "isint(${1=10}) && $1>=2 && isint(${2=3}) && $2>0 && isint(${3=$2}) && $3>0"
25569               skip ${4=1},${5=1},${6=0},${7=1}
25570  e[^-1] "Create 3D transition sequence between image$?, with $1 frames, $2x$3 tiles and rotation axis ($4,$5,$6).\n"
25571  if $!<2 return fi
25572  slices 0 to_rgb r ${-max_whds},3
25573  off=0 repeat $!-1 l[{$>+$off},{$>+$off+1}]
25574    e[] "\r > Generate transition from image "$>" to image "{$>+1}".    "
25575
25576    # Create 3D rotation vectors.
25577    $2,$3,1,1,'$4'
25578    $2,$3,1,1,'$5'
25579    $2,$3,1,1,'$6'
25580    a[-3--1] z
25581    permute. zxyc r. 3,{$2*$3},1,1,-1
25582    repeat h rot$>={@0-2} shift. 0,-1,0,0 done
25583    rm.
25584
25585    # Create 3D tiles.
25586    +split_tiles[-2,-1] $2,$3 mv[0,1] $!
25587    N={$2*$3} i=0 y=0
25588    repeat $3
25589      x=0
25590      repeat $2
25591        lw={$i,w} lh={$i,h}
25592        imageplane3d[$i] imageplane3d[$N] r3d[$N] ${rot$i},180 c3d[$i,$N] +3d[$i,$N]
25593        x$i=$x y$i=$y x+=$lw i+=1
25594      done
25595      y+=$lh
25596    done
25597
25598    # Generate intermediate animation frames.
25599    repeat $1-2
25600      repeat $N r3d[$>] ${rot$>},{180/(1-$1)} ++3d[$>] ${x$>},${y$>},0 done
25601      +3d[-$N--1] c3d.
25602
25603      if $7 # Antialiased rendering.
25604        i... {-2,2*[w,h]},1,3,-1
25605        *3d. 2 j3d... .,50%,50%,0,1,2,0,0 rm.
25606        to_rgba.. replace_color.. 0,0,-1,-1,-1,255,0,0,0,0
25607        downsize_aliased.. 50
25608      else # Standard rendering.
25609        i... {-2,w},{-2,h},1,3,-1 j3d... .,50%,50%,0,1,2,0,0 rm.
25610        to_rgba.. replace_color.. 0,0,-1,-1,-1,255,0,0,0,0
25611      fi
25612
25613    done
25614    rm[0-{$N-1}]
25615    nm[1--2] {0,n}
25616    off+={$1-2}
25617  endl done
25618
25619#@cli video2files : input_filename,_output_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1
25620#@cli : Split specified input video file into image files, one for each frame.
25621#@cli : First and last frames as well as step between frames can be specified.
25622#@cli : Default values: 'output_filename=frame.png', 'first_frame=0', 'last_frame=-1' and 'frame_step=1'.
25623video2files : check "isint(${3=0}) && $3>=0 && isint(${4=-1}) && ($4>=0 || $4==-1) && isint(${5=1}) && $5>=1"
25624              skip ${2="frame.png"}
25625  e[^-1] "Split input video file '$1' into image frames '$2', with first frame $3, last frame $4, and frame step $5.\n"
25626  frame=$3 stopflag=0
25627  do l[]
25628    i "$1",$frame
25629    if $!
25630      o ${"filename \"$2\","$frame} rm
25631      e[] "\r  > Frame ""#"$frame
25632      frame+=$5
25633    else stopflag=1
25634    fi
25635  onfail stopflag=1
25636  endl while !$stopflag" && "($frame<=$4" || "$4==-1)
25637
25638#------------------------------
25639#
25640#@cli :: Convenience Functions
25641#
25642#------------------------------
25643
25644#@cli alert : _title,_message,_label_button1,_label_button2,...
25645#@cli : Display an alert box and wait for user's choice.
25646#@cli : If a single image is in the selection, it is used as an icon for the alert box.
25647#@cli : Default values: 'title=[G'MIC Alert]' and 'message=This is an alert box.'.
25648alert : skip "${1=[G"{`39`}"MIC Alert]},${2=This is an alert box.},${3=OK}"
25649  if $!==1
25650    e[0--3] "Display alert box, with image$?, title '$1', message '$2' and buttons '${3--1}'."
25651  else
25652    e[0--3] "Display alert box, with title '$1', message '$2' and buttons '${3--1}'."
25653  fi
25654
25655  if $!==1 logo= else logo=[] fi
25656  +l$logo
25657
25658    # Manage alert icon.
25659    if $!==1 to_rgb
25660    else # No logo provided, generate default logo (alert).
25661      64,64 polygon 3,50%,10%,10%,90%,90%,90%,1,1 b 3 >= 50%
25662      +erode. 5 -. .. ==. 0
25663      polygon. 4,47%,43%,53%,43%,53%,66%,47%,66%,1,0 circle. 50%,76%,2,1,0
25664      +*[0] 255 . 100%,100% a[-3--1] c -. '3*(y-h/2)'
25665      *. .. rm.. *[0] 255 rv a c
25666      drop_shadow 3,3,1 i[0] 100%,100%,1,3,200 blend alpha
25667    fi
25668    channels -1,2
25669
25670    # Create buttons graphics.
25671    $=arg
25672    repeat $#-2 label=${arg{$>+3}} 0 t. $label,0,0,16,1,-200 done
25673    r[^0] {min(128,max(64,${max_w[^0]}+12))},{min(48,max(24,${max_h[^0]}+12))},1,1,0,0,0.5,0.5
25674    +[^0] 200 to_rgb[^0]
25675    mv[0] $!
25676    [0],[0],1,1,'(y-h/2)' *. -2 c. -30,30 +[0--3] . rm. c[^-1] 0,255  # Add shading to buttons.
25677
25678    repeat $!-1 l[$<]
25679
25680      # Create selected buttons.
25681      +rectangle 0,0,100%,100%,1,0xFFFFFFFF,0
25682      rectangle. 1,1,{w-2},{h-2},1,0xFFFFFFFF,0
25683      line. 2,{h-3},{w-3},{h-3},1,150 line. {w-3},{h-3},{w-3},2,1,150
25684      line. 1,1,{w-3},1,1,255 line. 1,1,1,{h-3},1,255
25685      rectangle. 4,4,{w-5},{h-5},1,0xAAAAAAAA,0
25686
25687      # Create clicked buttons.
25688      +shift.. 1,1,0,0,2
25689      rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0
25690      rectangle. 1,1,{w-2},{h-2},1,0xFFFFFFFF,150
25691      rectangle. 4,4,{w-5},{h-5},1,0xAAAAAAAA,0
25692
25693      # Create default aspect.
25694      rectangle... 0,0,100%,100%,1,0xFFFFFFFF,0
25695      line... 1,{h-2},{w-2},{h-2},1,150 line... {w-2},{h-2},{w-2},1,1,150
25696      line... 0,0,{w-2},0,1,255 line... 0,0,0,{h-2},1,255
25697
25698      # Create coordinates image.
25699      i[0] 100%,100% =[0] 1,0,0
25700
25701    a c endl done
25702
25703
25704    # Render alert box graphics.
25705    +l
25706      channels 0,3 sh 1,100% -[50%--1] 200 rm[50%--1] frame 8,8,0
25707      if $!<6 a[^-1] x else append_tiles[^-1] , fi
25708      0 t. "$2",0,0,16,1,0,-200,-200,-200 r. {w+16},{h+8},1,4,0
25709      a[-2,-1] x,0.5 rv a y,0.5
25710      sh 1,100% +. 200 rm.
25711      rectangle 0,0,100%,100%,1,0xFFFFFFFF,0
25712      line 0,0,{w-2},0,1,0,255,255,255 line 0,0,0,{h-2},1,0,255,255,255
25713    endl
25714    rm..
25715    +channels. 0
25716
25717    # Retrieve (x,y) coordinates of the buttons and fill active area.
25718    (0,{w-1}) (0;{-2,h-1}) ri[-2,-1] ...,3 a[-2,-1] c round. rv[-2,-1] *[-2,-1]
25719    discard. 0 y. r. {h/2},2,1,1,-1
25720    channels.. 1,3 rv[-2,-1]
25721    100%,100% repeat w#-3 x0={-3,i($>,0)} y0={-3,i($>,1)} rectangle. $x0,$y0,{$x0+{0,w}-1},{$y0+{0,h}-1},1,{1+$>} done
25722    a[-2,-1] c
25723
25724    # Enter event loop.
25725    repeat 9 if !{*$>} disp=$> break fi done   # Find available display window.
25726    if !narg($disp) error[0--4] "Command '$0': Cannot open display window for alert box." fi
25727
25728    selected={if($#==3,0,-1)} clicked=-1
25729    do
25730
25731      # Render current view.
25732      +channels. 0,2
25733      if $clicked>=0
25734        x0={-3,i($clicked,0)} y0={-3,i($clicked,1)}
25735        sh[$clicked] 7,9
25736        j.. .,$x0,$y0 rm.
25737      elif $selected>=0
25738        x0={-3,i($selected,0)} y0={-3,i($selected,1)}
25739        sh[$selected] 4,6
25740        j.. .,$x0,$y0 rm.
25741      fi
25742      w$disp. 100%,100%,0,"$1" rm. wait
25743
25744      # Handle user interactions.
25745      xm={*$disp,x} ym={*$disp,y} bm={{*$disp,b}&1} val={i($xm,$ym,0,3)}
25746      if $bm" && "$val clicked={$val-1}
25747      elif $bm" && "!$val" && "$clicked>=0 selected=$clicked clicked=-1
25748      elif !$bm" && "$clicked>=0" && "$clicked==$val-1 break
25749      fi
25750      if {*$disp,ARROWRIGHT} selected={($selected+1)%{-2,w}} wait -1
25751      elif {*$disp,ARROWLEFT} selected={($selected-1)%{-2,w}+($selected==-1)} wait -1
25752      elif $selected>=0" && "{*$disp,ENTER} clicked=$selected break
25753      fi
25754
25755    while {*$disp}" && "!{*$disp,ESC}
25756
25757    # Return result (index of clicked button or '-1').
25758    w$disp 0 rm u $clicked
25759  endl
25760
25761#@cli arg : n>=1,_arg1,...,_argN
25762#@cli : Return the n-th argument of the specified argument list.
25763arg : check "isint($1) && ($1)>0"
25764  $=arg u ${arg{1+($1)}}
25765
25766#@cli arg0 : n>=0,_arg0,...,_argN
25767#@cli : Return the n-th argument of the specified argument list (where 'n' starts from '0').
25768arg0 : check "isint($1) && ($1)>=0"
25769  $=arg u ${arg{2+($1)}}
25770
25771#@cli arg2img : argument_1,...,argument_N
25772#@cli : Split specified list of arguments and return each as a new image (as a null-terminated string).
25773arg2img :
25774  $=arg repeat $# ({'${arg{1+$>}}'},0) done
25775
25776#@cli arg2var : variable_name,argument_1,...,argument_N
25777#@cli : For each i in [1...N], set 'variable_name$i=argument_i'.
25778#@cli : The variable name should be global to make this command useful (i.e. starts by an underscore).
25779arg2var :
25780  $=arg u {$#-1} repeat ${} $1{1+$>}=${arg{2+$>}} done
25781
25782#@cli autocrop_coords : value1,value2,... | auto
25783#@cli : Return coordinates (x0,y0,z0,x1,y1,z1) of the autocrop that could be performed on the latest
25784#@cli : of the selected images.
25785#@cli : Default value: 'auto'
25786autocrop_coords : skip ${1=auto}
25787  is_auto={['"$1"']=='auto'}
25788  w={w} h={h} d={d}
25789  value={i(w-1,h-1,d-1)} +=. {1+$value},100%,100%,100% _autocrop$is_auto. ${1--1} =. $value,100%,100%,100%
25790  x0={$w-w} y0={$h-h} z0={$d-d} rm.
25791  +_autocrop$is_auto. ${1--1}
25792  x1={$x0+w-1} y1={$y0+h-1} z1={$z0+d-1} rm.
25793  u $x0,$y0,$z0,$x1,$y1,$z1
25794
25795_autocrop0 : autocrop $*
25796_autocrop1 : skip $* autocrop
25797
25798#@cli average_colors
25799#@cli : Return the average vector-value of the latest of the selected images.
25800average_colors :
25801  res=""
25802  repeat s-1 sh. {1+$>} res=$res,{ia} rm. done
25803  sh. 0 u {ia}$res rm.
25804
25805#@cli base642img : "base64_string"
25806#@cli : Decode given base64-encoded string as a newly inserted image at the end of the list.
25807#@cli : The argument string must have been generated using command 'img2base64'.
25808base642img :
25809  base642uchar "$1" unserialize.
25810
25811#@cli base642uchar : "base64_string"
25812#@cli : Decode given base64-encoded string as a newly inserted 1-column image at the end of the list.
25813#@cli : The argument string must have been generated using command 'uchar2base64'.
25814base642uchar :
25815  0
25816  eval "
25817    ref(vector256(),hash);
25818    for (k = _'A', k<=_'Z', ++k, hash[k] = k - _'A');
25819    for (k = _'a', k<=_'z', ++k, hash[k] = k - _'a' + 26);
25820    for (k = _'0', k<=_'9', ++k, hash[k] = k - _'0' + 52);
25821    hash[_'+'] = hash[_'-'] = 62;
25822    hash[_'/'] = hash[_'_'] = 63;
25823    s = ['$1'];
25824    const ss = size(s);
25825    ss>=2?(
25826      resize(#-1,1,ss*3/4 - (s[ss-1]==_'=') - (s[ss-2]==_'='),1,1);
25827      od = 0;
25828      for (os = 0, os<size(s),
25829        c1 = hash[s[os++]];
25830        c2 = hash[s[os++]];
25831        c3 = hash[s[os++]];
25832        c4 = hash[s[os++]];
25833        i[#-1,od++] = (c1<<2) | (c2>>4);
25834        i[#-1,od++] = ((c2&15)<<4) | (c3>>2);
25835        i[#-1,od++] = ((c3&3)<<6) | c4;
25836      )
25837    )"
25838  nm. "[unnamed]"
25839
25840#@cli basename : file_path,_variable_name_for_folder
25841#@cli : Return the basename of a file path, and opt. its folder location.
25842#@cli : When specified 'variable_name_for_folder' must starts by an underscore
25843#@cli : (global variable accessible from calling function).
25844basename : skip ${2=unused}
25845  l[] ({"'$1'"}) replace 92,47 s +,47
25846  if i==47 a y $2={t} u ""
25847  elif $!==1 u {t} $2=""
25848  else a[^-1] y u {t} $2={-2,t}
25849  fi
25850  rm endl
25851
25852#@cli bin : binary_int1,...
25853#@cli : Print specified binary integers into their octal, decimal, hexadecimal and string representations.
25854bin :
25855  dec=${bin2dec\ ${^0}}
25856  e[^-1] "Convert binary integer"${arg\ 1+($#>1),"",s}" '${^0}' to octal '"${dec2oct\ $dec}"',
25857          decimal '"$dec"', hexadecimal '"${dec2hex\ $dec}"' and string '"${dec2str\ $dec}"'."
25858
25859#@cli bin2dec : binary_int1,...
25860#@cli : Convert specified binary integers into their decimal representations.
25861bin2dec :
25862  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
25863
25864_bin2dec :
25865  u {"str = vtos(abs($1));
25866      for (k = val = 0, str[k], ++k,
25867        c = str[k];
25868        (val<<=1)+=(c==_'0'?0:c==_'1'?1:nan);
25869        isnan(val)?break()
25870      ); sign($1)*val"}
25871
25872# Command to check what lines of G'MIC sources are larger than 120 columns.
25873_check120 :
25874  use_vt100
25875  it[] "$1" s +,10
25876  1,$!,1,2,">begin(line = 1); is_lines=i[#y,0]==10; line+=is_lines?h#y:0; [is_lines || h#y<=120?-1:y,line]"
25877  lines={{@-1}-1} f. "I = I; if(I[0]<0,I[1]=-1); I"
25878  discard. -1
25879  if w
25880    r. 1,{h/2},1,2,-1
25881    repeat h
25882      l,L={I[$>]}
25883      e[] $_vt100_c"  - [Line "$_vt100_b$L$_vt100_n$_vt100_c", "\
25884              $_vt100_b{$l,h}$_vt100_n$_vt100_c" chars]: "$_vt100_n{$l,t}
25885    done
25886  fi
25887  rm
25888  _total_lines+=$lines
25889  e[] "  - Scanned : "${lines}" lines"
25890
25891check120 :
25892  _total_lines=0
25893  files 0,*.h c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi
25894  files 0,*.cpp c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi
25895  files 0,*.c c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi
25896  files 0,*.gmic c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi
25897  repeat narg($files)
25898    arg 1+$>,$files file=${}
25899    e[] " * File '"$file"'."
25900    v + _check120 $file v -
25901  done
25902  e[] " - Total scanned : "${_total_lines}" lines"
25903
25904#@cli covariance_colors : _avg_outvarname
25905#@cli : Return the covariance matrix of the vector-valued colors in the latest of the selected images
25906#@cli : (for arbitrary number of channels).
25907#@cli : Parameter 'avg_outvarname' is used as a variable name that takes the value of the average vector-value.
25908covariance_colors : skip "${1=avg}"
25909  $1=${-average_colors}
25910  f ">begin( avg = [ "$""$1" ]; const S2 = s^2; C = vectorS2(0); );
25911      mI = I - avg; C+=mul(mI,mI,s);
25912      end( C/=whd - 1; run('u ',vtos(C)) );
25913      I"
25914
25915#@cli dec : decimal_int1,...
25916#@cli : Print specified decimal integers into their binary, octal, hexadecimal and string representations.
25917dec :
25918  e[^-1] "Convert decimal integer"${arg\ 1+($#>1),"",s}" '${^0}' to binary '"${dec2bin\ ${^0}}"',"\
25919          " octal '"${dec2oct\ ${^0}}"', hexadecimal '"${dec2hex\ ${^0}}"' and string '"${dec2str\ ${^0}}"'."
25920
25921#@cli dec2str : decimal_int1,...
25922#@cli : Convert specifial decimal integers into its string representation.
25923dec2str :
25924  u {`[${^0}]`}
25925
25926#@cli dec2bin : decimal_int1,...
25927#@cli : Convert specified decimal integers into their binary representations.
25928dec2bin :
25929  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
25930
25931_dec2bin :
25932  u {`"const sgn = sign($1);
25933       const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1))))) + (sgn>=0?0:1);
25934       res = vectorN();
25935       sgn>=0?0:(res[0] = _'-');
25936       for (val = abs($1); k = size(res) - 1, k>=(sgn<0?1:0), --k, res[k] = _'0' + (val&1); val>>=1); res"`}
25937
25938#@cli dec2hex : decimal_int1,...
25939#@cli : Convert specified decimal integers into their hexadecimal representations.
25940dec2hex :
25941  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
25942
25943_dec2hex :
25944  u {`"begin(tab = [ _'0',_'1',_'2',_'3',_'4',_'5',_'6',_'7',_'8',_'9',_'a',_'b',_'c',_'d',_'e',_'f' ]);
25945       const sgn = sign($1);
25946       const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1)))/4)) + (sgn>=0?0:1);
25947       res = vectorN();
25948       sgn>=0?0:(res[0] = _'-');
25949       for (val = abs($1); k = size(res) - 1, k>=(sgn<0?1:0), --k, res[k] = tab[val&15]; val>>=4); res"`}
25950
25951#@cli dec2oct : decimal_int1,...
25952#@cli : Convert specified decimal integers into their octal representations.
25953dec2oct :
25954  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
25955
25956_dec2oct :
25957  u {`"const sgn = sign($1);
25958       const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1)))/3)) + (sgn>=0?0:1);
25959       res = vectorN();
25960       sgn>=0?0:(res[0] = _'-');
25961       for (val = abs($1); k = size(res) - 1, k>=(sgn<0?1:0), --k, res[k] = _'0' + (val&7); val>>=3); res"`}
25962
25963#@cli fact : value
25964#@cli : Return the factorial of the specified value.
25965fact : check isint($1)
25966  res=1 repeat $1 res*={($>+1)} done u $res
25967
25968#@cli fibonacci : N>=0
25969#@cli : Return the Nth number of the Fibonacci sequence.
25970#@cli : $ echo ${"fibonacci 10"}
25971#@cli : \n~~~\n[gmic]-0./ Start G'MIC interpreter.\n[gmic]-0./ 55\n[gmic]-0./ End G'MIC interpreter.\n~~~\n
25972fibonacci : check "$1>=0"
25973  u {N=$1;if(N<2,N,for(n=N;F0=0;F1=1,n=n-1,F2=F0+F1;F0=F1;F1=F2))}
25974
25975#@cli file_mv : filename_src,filename_dest
25976#@cli : Rename or move a file from a location $1 to another location $2.
25977file_mv :
25978  e[^-1] "Move file '$1' to location '$2'."
25979  if ${-is_windows} x "move "$1" "$2 else x "mv "$1" "$2 fi
25980
25981#@cli file_rand
25982#@cli : Return a random filename for storing temporary data.
25983file_rand :
25984  do filename=${-path_tmp}gmic$_pid{`round(u(vector6(_'0'),vector6(_'9')))`}
25985  while isfile(['{/$filename}'])
25986  u $filename
25987
25988#@cli filename : filename,_number1,_number2,...,_numberN
25989#@cli : Return a filename numbered with specified indices.
25990filename : skip "${1=default}"
25991  if $#==1 u "$1"
25992  else
25993  (${2--1}) nm. "$1" u {f}{b}
25994  repeat w
25995    u ${}_{int(i/100000)%10}{int(i/10000)%10}{int(i/1000)%10}{int(i/100)%10}{int(i/10)%10}{i%10}
25996    shift. -1
25997  done
25998  if narg({'{x}'}) u ${}.{x} fi
25999  rm.
26000  fi
26001
26002#@cli files : _mode,path : (+)
26003#@cli : Return the list of files and/or subfolders from specified path.
26004#@cli : 'path' can be eventually a matching pattern.
26005#@cli : 'mode' can be { 0=files only | 1=folders only | 2=files + folders }.
26006#@cli : Add '3' to 'mode' to return full paths instead of filenames only.
26007#@cli : Default value: 'mode=5'.
26008
26009#@cli fitratio_wh : min_width,min_height,ratio_wh
26010#@cli : Return a 2D size 'width,height' which is bigger than 'min_width,min_height' and has the specified w/h ratio.
26011fitratio_wh :
26012  if $3*$2>$1 u {int($3*$2)},$2 else u $1,{int($1/$3)} fi
26013
26014#@cli fitscreen : width,height,_depth,_minimal_size[%],_maximal_size[%] : [image],_minimal_size[%],_maximal_size[%]
26015#@cli : Return the 'ideal' size WxH for a window intended to display an image of specified size on screen.
26016#@cli : Default values: 'depth=1', 'minimal_size=128' and 'maximal_size=85%'.
26017fitscreen : skip "${2=},${3=},${4=},${5=}"
26018  if ${"is_image_arg $1"}
26019    l$1 W,H,D={[w,h,d]} endl if narg($2) m=$2 else m=25% fi if narg($3) M=$3 else M=85% fi
26020  else
26021    W,H=${1-2} if narg($3) D=$3 else D=1 fi if narg($4) m=$4 else m=25% fi if narg($5) M=$5 else M=85% fi
26022  fi
26023  eval "
26024    is_percent(str) = (unref(_is_pct); _is_pct=['#str']; _is_pct[size(_is_pct) - 1]==_'%');
26025    const u = "{*,u}";
26026    const v = "{*,v}";
26027    ms = round(is_percent("$m")?[ u,v ]*"$m":[ "$m,$m" ]);
26028    Ms = round(is_percent("$M")?[ u,v ]*"$M":[ "$M,$M" ]);
26029    s = [ "$W,$H" ];
26030    "$D">1?(s+="$D");
26031    s[0]<ms[0]?(s = [ ms[0],s[1]*ms[0]/s[0] ]);
26032    s[1]<ms[1]?(s = [ s[0]*ms[1]/s[1],ms[1] ]);
26033    s[0]>Ms[0]?(s = [ Ms[0],s[1]*Ms[0]/s[0] ]);
26034    s[1]>Ms[1]?(s = [ s[0]*Ms[1]/s[1],Ms[1] ]);
26035    s[0] = max(1,s[0],ms[0]);
26036    s[1] = max(1,s[1],ms[1]);
26037    round(s)"
26038
26039#@cli fontchart
26040#@cli : Insert G'MIC font chart at the end of the image list.
26041#@cli : $ fontchart
26042fontchart :
26043  e[^-1] "Generate G'MIC font chart."
26044  l[]
26045    repeat 256
26046      if $>==92 char=\\ else char={`max(1,(c=$>;c>=23&&c<=28?32:c))`} fi
26047      0 t. {``$char},0,0,50,1,255
26048    done
26049    a z,0.5
26050    s z
26051    repeat $!
26052      t[$>] $>,1,-1,13,1,200
26053      0 t. \\${dec2oct\ $>},1,-1,13,1,1
26054      100%,100%,1,1,200 j[$>] .,{$>,[w,h]-[w#-1,h#-1]},0,0,1,.. rm[-2,-1]
26055    done
26056    frame 1,1,128 append_tiles ,
26057  endl
26058
26059# font2cimgh
26060# Encode a font image as a C-style string for CImg.h.
26061font2cimgh :
26062  e[^-1] "Encode font image$? as a C-style string for CImg.h."
26063  repeat $! l[$>] bnm={0,b}
26064    e[] "  > Encode font '"$bnm"'."
26065    W,H={[w/256,h]}
26066    if !isint($W) error[0--4] "Font image '"$bnm"' has wrong dimensions ("{[w,h,d,s]}")." fi
26067    +f "i==im || i==iM" is_binary={im==1} rm.
26068
26069    # Find best parameters for RLE compression.
26070    Mm=0 MM=100
26071    do
26072      M={floor(($Mm+$MM)/2)}
26073      +compress_rle $is_binary,$M rows. 6,100% +. {32-im} iM={iM}
26074      if iM<126 Mm=$M rm. elif iM>126 MM=$M rm. fi
26075    while $iM!=126
26076    k. nb_chunks={1+int(h/65536)}
26077
26078    e[] "\r  > Encode font '"$bnm"' -> W = "$W", H = "$H", M = "$M", is_binary = "$is_binary",
26079             nb_chunks = "$nb_chunks"."
26080
26081    # Generate C-style string for storing font data.
26082    replace_str "\\","\\\\"
26083    replace_str "\"","\\\""
26084    s y,-111
26085    repeat $!
26086      if {$>,i[h-1]==_'\\'&&i[h-2]!=_'\\'} rows[$>] 0,{$>,h-2} rows[{$>+1}] -1,100% =[{$>+1}] {'\\'} fi
26087      l[$>]
26088        i[0] ('"      \""') ('\"')
26089        if !$< ('" };"') fi
26090        ('\n')
26091        y a y
26092      endl
26093    done
26094    repeat $nb_chunks-1 ind={int($!*($>+1)/$nb_chunks)} l[$ind] = {','},0,100% ('\n') y a y endl done
26095    i[0] ('"    static const char *const data_font"${W}x${H}"[] = {"\n')
26096    y a y ot $bnm.h
26097
26098  endl done
26099
26100#@cli fps
26101#@cli : Return the number of time this function is called per second, or -1 if this info is not yet available.
26102#@cli : Useful to display the framerate when displaying animations.
26103fps :
26104  if narg($_fps_fps)
26105    dt={$|-$_fps_time}
26106    if $dt>1 _fps_fps={round($_fps_nbframes/$dt)} _fps_time=$| _fps_nbframes=0 fi
26107    u $_fps_fps
26108    _fps_nbframes+=1
26109  else _fps_nbframes=0 _fps_time=$| _fps_fps=-1 u -1
26110  fi
26111
26112#@cli gcd : a,b
26113#@cli : Return the GCD (greatest common divisor) between a and b.
26114gcd : check "isint($1) && isint($2) && $1*$2!=0"
26115  _gcd {max(abs($1),abs($2))},{min(abs($1),abs($2))}
26116
26117_gcd :
26118  r={$1%$2} if $r u ${_gcd\ $2,$r} else u $2 fi
26119
26120#@cli hex : hexadecimal_int1,...
26121#@cli : Print specified hexadecimal integers into their binary, octal, decimal and string representations.
26122hex :
26123  dec=${hex2dec\ ${^0}}
26124  e[^-1] "Convert hexadecimal integer"${arg\ 1+($#>1),s,""}" '${^0}' to binary '"${dec2bin\ $dec}"',
26125           octal '"${dec2oct\ $dec}"', decimal '"$dec"' and string '"${dec2str\ $dec}"'."
26126
26127#@cli hex2dec : hexadecimal_int1,...
26128#@cli : Convert specified hexadecimal integers into their decimal representations.
26129hex2dec :
26130  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
26131
26132_hex2dec :
26133  u {"str = ['$1']; str[0]==_'-'?-stov([['0x'],str[1,max(1,size(str)-1)]]):stov([['0x'],str])"}
26134
26135#@cli hex2img : "hexadecimal_string"
26136#@cli : Insert new image 1xN at the end of the list with values specified by the given hexadecimal-encoded string.
26137hex2img :
26138  ('"$1"') 1,{w/2}
26139  f. "*
26140    from_char(x) = x>=48 && x<=57?x - 48:x-87;
26141    off = 2*y;
26142    from_char(i[#-2,off])*16 + from_char(i[#-2,off + 1])"
26143  rm..
26144
26145#@cli hex2str : hexadecimal_string
26146#@cli : Convert specified hexadecimal string into a string.
26147#@cli : See also: ''str2hex''.
26148hex2str : skip ${1=""}
26149  if !narg("$1") u "" return fi
26150  ('$*') if w<2 rm. u "" return fi
26151  f. 'v=i-if(i>=97,87,48);if(x%2,v,v*16)' r. 2,{int(w/2)},1,1,-1 cumulate. x z. 1,1
26152  u {t} rm.
26153
26154#@cli img2base64 : _encoding={ 0=base64 | 1=base64url },_store_names={ 0 | 1 }
26155#@cli : Encode selected images as a base64-encoded string.
26156#@cli : The images can be then decoded using command 'base642img'.
26157#@cli : Default values: 'encoding=0'.
26158img2base64 : check "isbool(${1=0}) && isbool(${2=1})"
26159  if isnum("$1") encoding=$1 else encoding=0 noarg fi
26160  +serialize auto,1,$2 u ${uchar2base64\ $encoding} rm.
26161
26162#@cli img2hex
26163#@cli : Return representation of last image as an hexadecimal-encoded string.
26164#@cli : Input image must have values that are integers in [0,255].
26165img2hex :
26166  whds={w},{h},{d},{s} y. 2,{h}
26167  f.. "*
26168    to_char(x) = x>=0 && x<=9?48 + x:87 + x;
26169    i(#-1,0,y) = to_char(int(i/16));
26170    i(#-1,1,y) = to_char(i%16);
26171    i"
26172  u {t} rm. r. $whds,-1
26173
26174#@cli img2str
26175#@cli : Return the content of the latest of the selected images as a special G'MIC input string.
26176img2str :
26177  i[-2] 256
26178  eval. ">begin(off = 0);
26179    sep = x==w - 1?(y==h - 1?(z==d - 1?(c==s - 1?0:_'^'):_'/'):_';'):_',';
26180    it = vtos(i);
26181    off + size(it) + 1>=w(#-2)?resize(#-2,round(1.5*w(#-2)),1,1,1,0);
26182    for (k = 0, k<size(it) && it[k], ++k, i[#-2,off++] = it[k]);
26183    i[#-2,off++] = sep"
26184  u {-2,t} rm..
26185
26186#@cli img2text : _line_separator
26187#@cli : Return text contained in a multi-line image.
26188#@cli : Default value: 'line_separator= '.
26189img2text : skip "${1= }"
26190  +l s y s -,0 y x if $!>1 i[1--2] ('"$1"') fi a x u {0,t} rm endl
26191
26192#@cli img82hex
26193#@cli : Convert selected 8bits-valued vectors into their hexadecimal representations (ascii-encoded).
26194img82hex :
26195  e[^-1] "Convert 8bits-valued vector$? into hexadecimal representations (ascii-encoded)."
26196  % 256 y
26197  repeat $!
26198    +f[$>] 'v=int(i)&15;v+if(v<10,48,87)' # Lower digit
26199    f[$>] 'v=int(i)>>4;v+if(v<10,48,87)'  # Higher digit
26200    a[$>,-1] x
26201  done
26202
26203#@cli hex2img8
26204#@cli : Convert selected hexadecimal representations (ascii-encoded) into 8bits-valued vectors.
26205hex2img8 :
26206  e[^-1] "Convert hexadecimal representation$? (ascii-encoded) into 8bits-valued vectors."
26207  repeat $!
26208    s. x,2 f[-2,-1] 'if(i>=97,i-87,i-48)' *.. 16 +[-2,-1]
26209  mv. 0 done
26210
26211#@cli is_3d
26212#@cli : Return 1 if all of the selected images are 3D objects, 0 otherwise.
26213is_3d :
26214  u 1 l check3d 1 onfail u 0 endl
26215
26216# Faster version.
26217_is_3d :
26218  u {"h>6 && int(i[0])==67 && int(i[1])==73 && int(i[2])==109 && int(i[3])==103 && int(i[4])==51 && int(i[5])==100"}
26219
26220#@cli is_change : _value={ 0=false | 1=true }
26221#@cli : Set or unset the 'is_change' flag associated to the image list.
26222#@cli : This flag tells the interpreter whether or not the image list should be displayed when the pipeline ends.
26223#@cli : Default value: 'value=1'.
26224is_change :
26225  l[] check "isbool(${1=1})" arg=$1 onfail noarg arg=1 endl
26226  if $arg 0 rm. else w9[] 0 fi
26227
26228#@cli is_half
26229#@cli : Return 1 if the type of image pixels is limited to half-float.
26230is_half :
26231  (2049) u {i==2048} rm.
26232
26233#@cli is_ext : filename,_extension
26234#@cli : Return 1 if specified filename has a given extensioin.
26235is_ext : skip "${1=}"
26236  0 nm. "_$1" u {"lowercase(['"{x}"'])==lowercase(['$2'])"} rm.
26237
26238#@cli is_image_arg : string
26239#@cli : Return 1 if specified string looks like '[ind]'.
26240is_image_arg : skip "${1=;}"
26241  u {"str = ['$1'];
26242      s1 = size(str) - 1;
26243      (str[0]==_'[' && str[s1]==_']') || (str[0]=='.' && str[s1]=='.') && min(str)>=45 && max(str)<=122"}
26244
26245#@cli is_pattern : string
26246#@cli : Return 1 if specified string looks like a drawing pattern '0x......'.
26247is_pattern : skip "${1=;}"
26248  u {"str = ['$1']; size(str)>2 && same(str,'0x',2)"}
26249
26250#@cli is_percent : string
26251#@cli : Return 1 if specified string ends with a '%', 0 otherwise.
26252is_percent :
26253  u {"s=['$1'];s[size(s)-1]==_'%'"}
26254
26255#@cli is_variable_name : "str"
26256#@cli : Returns 1 if specified argument can be considered as a variable name, 0 otherwise.
26257is_variable_name :
26258  l[]
26259    u {"str = ['$1'];
26260        res = 1;
26261        for (k = 0, res && k<size(str), ++k,
26262          c = str[k];
26263          res&= (c>=_'a' && c<=_'z') || (c>=_'A' && c<=_'Z') || (c>=_'0' && c<=_'9') || c=='_';
26264        );
26265        c = str[0];
26266        res && (c<_'0' || c>_'9')"}
26267  onfail u 0 endl
26268
26269#@cli is_videofilename
26270#@cli : Return 1 if extension of specified filename is typical from video files.
26271is_videofilename : skip "${1=}"
26272  0 nm. "_$1" u {"
26273    ext = lowercase(['"{x}"']);
26274    ext=='avi' || ext=='mov' || ext=='asf' || ext=='divx' || ext=='flv' || ext=='mpg' ||
26275    ext=='m1v' || ext=='m2v' || ext=='m4v' || ext=='mjp' || ext=='mp4' || ext=='mkv' ||
26276    ext=='mpe' || ext=='movie' || ext=='ogm' || ext=='ogg' || ext=='ogv' || ext=='qt' || ext=='rm' ||
26277    ext=='vob' || ext=='wmv' || ext=='xvid' || ext=='mpeg'"}
26278  rm.
26279
26280#@cli is_macos
26281#@cli : Return 1 if current computer OS is Darwin (MacOS), 0 otherwise.
26282is_macos :
26283  if !narg($_is_macos) l[]
26284    if ${-is_windows} _is_macos=0
26285    else
26286      file=${-path_tmp}gmic_uname
26287      x "uname >"{/$file}
26288      it[] $file
26289      _is_macos={same(crop(),'Darwin',6,0)}
26290      rm
26291    fi
26292  onfail _is_macos=0 rm
26293  endl fi
26294  u $_is_macos
26295
26296#@cli is_windows
26297#@cli : Return 1 if current computer OS is Windows, 0 otherwise.
26298is_windows :
26299  if !narg($_is_windows) _is_windows={['$OS']!=0" && "['$WINDIR']!=0} fi
26300  u $_is_windows
26301
26302#@cli math_lib
26303#@cli : Return string that defines a set of several useful macros for the embedded math evaluator.
26304math_lib :
26305  u "
26306
26307  # Return number of elements in min-heap #ind.
26308  heap_size(ind) = i[#ind,whd(#ind)-1];
26309
26310  # Insert new element 'elt' into min-heap #ind.
26311  heap_insert(ind,elt) = (
26312    _heap_siz = heap_size(#ind);
26313    _heap_siz>=h(#ind) - 1?resize(#ind,1,_heap_siz*2 + 2,1,s#ind,0);
26314    hind = h(#ind);
26315    unref(_heap_elt);
26316    _heap_elt = [ elt ];
26317    _heap_eltsiz = max(1,size(_heap_elt));
26318
26319    copy(i[#ind,_heap_siz],_heap_elt[0],_heap_eltsiz,hind,1);
26320    for (_heap_pos = _heap_siz,
26321         _heap_pos && (_heap_par = int((_heap_pos + 1)/2) - 1; _heap_elt[0]<i[#ind,_heap_par]),
26322         _heap_pos = _heap_par,
26323      copy(i[#ind,_heap_pos],i[#ind,_heap_par],_heap_eltsiz,hind,hind);
26324      copy(i[#ind,_heap_par],_heap_elt,_heap_eltsiz,hind,1);
26325    );
26326    (heap_size(#ind)) = ++_heap_siz;
26327  );
26328
26329  # Remove root element from min-heap #ind.
26330  heap_remove(ind) = (
26331    _heap_siz = heap_size(#ind) - 1;
26332    hind = h(#ind);
26333    _heap_eltsiz = max(1,s#ind);
26334
26335    copy(i[#ind,0],i[#ind,_heap_siz],_heap_eltsiz,hind,hind);
26336    _heap_pos = 0;
26337    do (
26338      _heap_left = 2*_heap_pos + 1;
26339      _heap_right = _heap_left + 1;
26340      value = i[#ind,_heap_pos];
26341      _heap_right<_heap_siz && value>i[#ind,_heap_right]?(
26342        _heap_swap = i[#ind,_heap_left]<i[#ind,_heap_right]?_heap_left:_heap_right;
26343      ):_heap_left<_heap_siz && value>i[#ind,_heap_left]?(
26344        _heap_swap = _heap_left;
26345      ):break();
26346      copy(i[#ind,_heap_siz],i[#ind,_heap_swap],_heap_eltsiz,hind,hind); # Use previous last elt as temporary for swap.
26347      copy(i[#ind,_heap_swap],i[#ind,_heap_pos],_heap_eltsiz,hind,hind);
26348      copy(i[#ind,_heap_pos],i[#ind,_heap_siz],_heap_eltsiz,hind,hind);
26349      _heap_pos = _heap_swap;
26350    );
26351    (heap_size(#ind)) = _heap_siz;
26352  );
26353
26354  # Return number of elements in dynamic array #ind.
26355  dar_size(ind) = i[#ind,whd(#ind)-1];
26356
26357  # Return last element of a dynamic array
26358  dar_Back(ind) = I[#ind,dar_size(#ind)-1];
26359  dar_back(ind) = i[#ind,dar_size(#ind)-1];
26360
26361  # Inserts new element 'elt' into dynamic array #ind, at index [pos] ('pos' must be in [0,dar_size(#ind)]).
26362  dar_insert(ind,elt,pos) = (
26363    _dar_pos = pos;
26364    _dar_siz = dar_size(#ind);
26365    if (_dar_pos<=_dar_siz,
26366      _dar_siz>=h(#ind) - 1?resize(#ind,1,_dar_siz*2 + 2,1,s(#ind),0);
26367      for (_dar_c = 0, _dar_c<s(#ind), ++_dar_c,
26368        copy(i(#ind,_dar_pos + 1,0,0,_dar_c),i(#ind,_dar_pos,0,0,_dar_c),_dar_siz - _dar_pos)
26369      );
26370      unref(_dar_elt); _dar_elt = elt;
26371      copy(i[#ind,_dar_pos],_dar_elt,max(1,size(_dar_elt)),h(#ind),1);
26372      (dar_size(ind#)) = ++_dar_siz;
26373    );
26374  );
26375
26376  # Inserts new element 'elt' at the end of dynamic array #ind.
26377  dar_insert(ind,elt) = (
26378    _dar_siz = dar_size(#ind);
26379    _dar_siz>=h(#ind) - 1?resize(#ind,1,_dar_siz*2 + 2,1,s(#ind),0);
26380    unref(_dar_elt); _dar_elt = elt;
26381    copy(i[#ind,_dar_siz],_dar_elt,max(1,size(_dar_elt)),h(#ind),1);
26382    (dar_size(ind#)) = ++_dar_siz;
26383  );
26384
26385  # Remove element from dynamic array #ind, at index [pos] ('pos' must be in [0,dar_size(#ind) - 1]).
26386  dar_remove(ind,pos) = (
26387    _dar_pos = pos;
26388    _dar_siz = dar_size(#ind);
26389    if (_dar_pos<_dar_siz,
26390      _dar_siz = --dar_size(#ind);
26391      for (_dar_c = 0, _dar_c<s(#ind), ++_dar_c,
26392        copy(i(#ind,_dar_pos,0,0,_dar_c),i(#ind,_dar_pos + 1,0,0,_dar_c),_dar_siz - _dar_pos)
26393      );
26394    )
26395  );
26396
26397  # Remove last element from dynamic array #ind.
26398  dar_remove(ind) = (
26399    _dar_siz = dar_size(#ind);
26400    _dar_siz>0?--dar_size(#ind);
26401  );
26402
26403  # Distance from point A to point B
26404  dist(A,B) = (
26405    norm(B - A);
26406  );
26407
26408  # Distance from point X to segment [A,B]
26409  dist(X,A,B) = (
26410    AB = B - A;
26411    P = A + dot(X - A,B - A)/max(1e-8,dot(AB,AB))*AB;
26412    dot(P - A,P - B)<=0?norm(P - X):min(norm(A - X),norm(B - X));
26413  );
26414
26415  # Distance from segment [A,B] to segment [C,D]
26416  dist(A,B,C,D) = (
26417    min(dist(A,C,D),dist(B,C,D),dist(C,A,B),dist(D,A,B));
26418  );
26419
26420  # Return the 'm' largest eigenvalues of a (large) square matrix A,
26421  # using the Arnoldi iteration method (https://en.wikipedia.org/wiki/Arnoldi_iteration).
26422  # A larger 'm' goes with better numerical precision.
26423  meig(A,m,n) = (
26424    unref(meig_m,meig_n);
26425    const meig_n = n;
26426    const meig_m = min(m,meig_n);
26427
26428    # Initialize data.
26429    ref(vector(#meig_m*meig_n),meig_V);
26430    ref(vector(#meig_m^2),meig_H);
26431    ref(expr('u(-1,1)',1,meig_n),meig_vj); meig_vj/=norm(meig_vj);
26432
26433    # Iteration loop.
26434    repeat (meig_m,meig_j,
26435      copy(meig_V[meig_j*meig_n],meig_vj,meig_n);
26436      meig_vj = A*meig_vj;
26437      for (meig_i = 0, meig_i<=meig_j, ++meig_i,
26438        ref(meig_V[meig_i*meig_n,meig_n],meig_vi);
26439        meig_d = dot(meig_vj,meig_vi);
26440        meig_H[meig_j*meig_m + meig_i] = meig_d;
26441        meig_vj-=meig_d*meig_vi;
26442      );
26443      meig_nvj = norm(meig_vj);
26444      meig_H[meig_j*meig_m + meig_j + 1] = meig_nvj;
26445      meig_nvj<1e-12?break();
26446      meig_vj/=meig_nvj;
26447    );
26448    eig(meig_H)[0,m];
26449  );
26450
26451  # Dichotomic search : Find x in [xmin,xmax] s.a f(x) = target, with a epsilon-precision
26452  search_dichotomic(fn_x,target,epsilon,xmin,xmax) = (
26453    # fn_x must be a strictly monotonic function !
26454    # if 'xmin = xmax = nan', range of 'x' is auto-detected.
26455    _dicho_fn(x) = _dicho_sgn*(fn_x);
26456    _dicho_epsilon = epsilon;
26457    _dicho_m = xmin;
26458    _dicho_M = xmax;
26459    _dicho_sgn = 1;
26460    _dicho_autom = isnan(_dicho_m);
26461    _dicho_autoM = isnan(_dicho_M);
26462    if (_dicho_autom, _dicho_m = -1);
26463    if (_dicho_autoM, _dicho_M = 1);
26464    _dicho_sgn = _dicho_fn(_dicho_m)>_dicho_fn(_dicho_M)?-1:1;
26465    _dicho_res = nan;
26466    _dicho_target = _dicho_sgn*target;
26467    _dicho_nb_attempts = 30;
26468    _dicho_autom?do (
26469      _dicho_fm = _dicho_fn(_dicho_m);
26470      _dicho_fm<_dicho_target?break();
26471      _dicho_m*=2;
26472    _(while), --_dicho_nb_attempts);
26473    _dicho_nb_attempts?(
26474      _dicho_autoM?do (
26475        _dicho_fM = _dicho_fn(_dicho_M);
26476        _dicho_fM>_dicho_target?break();
26477        _dicho_M*=2;
26478      _(while), --_dicho_nb_attempts);
26479      _dicho_nb_attempts?(
26480        _dicho_nb_attempts = 100;
26481        do (
26482          _dicho_c = (_dicho_m + _dicho_M)/2;
26483          _dicho_fc = _dicho_fn(_dicho_c);
26484          abs(_dicho_fc - _dicho_target)<_dicho_epsilon?(_dicho_res = _dicho_c; break()):
26485          _dicho_fc<_dicho_target?(_dicho_m = _dicho_c):
26486          (_dicho_M = _dicho_c);
26487          _(while), --_dicho_nb_attempts
26488        );
26489      );
26490    );
26491    _dicho_res;
26492  );
26493  search_dichotomic(fn_x,target,epsilon) = search_dichotomic(fn_x,target,1e-3,nan,nan);
26494  search_dichotomic(fn_x,target) = search_dichotomic(fn_x,target,1e-3);
26495  search_dichotomic(fn_x) = search_dichotomic(fn_x,0);
26496
26497  arrow(ind,P0,P1,angle,length,opacity,color) = ( # Draw an arrow from P0 to P1 on image #ind
26498    unref(_da_color);
26499    _opacity = opacity;
26500    _da_color = color;
26501    _P0 = P0;
26502    _P1 = P1;
26503    _P0P1 = _P1;
26504    _P0P1-=_P0;
26505    if (length<0, _P0P1*=-length/100, _P0P1*=length/norm2(_P0P1));
26506    coords = [ _P0,_P1,_P1,_P1 - rot(angle°)*_P0P1,_P1,_P1 - rot(-angle°)*_P0P1 ];
26507    repeat (3,_k, polygon(#ind,2,coords[4*_k,2],coords[4*_k+2,2],_opacity,_da_color));
26508  );
26509
26510  spline(ind,P0,T0,P1,T1,opacity,color) = ( # Draw spline P0-P1 with tangents T0,T1 on image #ind
26511    unref(_ds_color);
26512    _P0 = P0;
26513    _P1 = P1;
26514    _opacity = opacity;
26515    _ds_color = resize(color,s#ind)*=abs(_opacity);
26516    _omopacity = 1 - max(_opacity,0);
26517    _C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ _P0,P1,T0,T1 ],2);
26518    _dt = _dtmin = 1/max(abs(_P1 - _P0));
26519    _P0 = inf;
26520    for (_t = 0, _t<=1, _t+=_dt,
26521      _P = round(mul([_t^3,_t^2,_t,1],_C,2));
26522      _dP = abs(mul([3*_t^2,2*_t,1,0],_C,2));
26523      _dt = min(_dtmin,0.75/max(_dP));
26524      if (_P0!=_P, I(#ind,_P) = _ds_color + _omopacity*I(#ind,_P));
26525      _P0 = _P;
26526    );
26527    nan;
26528  );
26529
26530  triangle(ind,P0,P1,P2,opacity,color0,color1,color2) = ( # Draw a shaded triangle P0-P1-P2 on image #ind
26531    unref(_dt_color);
26532    unref(_dt_color0);
26533    unref(_dt_color1);
26534    unref(_dt_color2);
26535    _opacity = opacity;
26536    _dt_color0 = resize(color0,s#ind);
26537    _dt_color1 = resize(color1,s#ind);
26538    _dt_color2 = resize(color2,s#ind);
26539    _A = round(P0);
26540    _B = round(P1);
26541    _C = round(P2);
26542    _xmin = max(0,min(_A[0],_B[0],_C[0]));
26543    _xmax = min(w#ind-1,max(_A[0],_B[0],_C[0]));
26544    _ymin = max(0,min(_A[1],_B[1],_C[1]));
26545    _ymax = min(h#ind-1,max(_A[1],_B[1],_C[1]));
26546    _M = transpose([_A,1,_B,1,_C,1],3);
26547    for (_y = _ymin, _y<_ymax, ++_y,
26548      for (_x = _xmin, _x<_xmax, ++_x,
26549        _L = round(solve(_M,[_x,_y,1]),1e-5);
26550        if (min(_L)>=0,
26551          _dt_color = _L[0]*_dt_color0 + _L[1]*_dt_color1 + _L[2]*_dt_color2;
26552          copy(i(#ind,_x,_y,0,0),_dt_color,size(_dt_color),whd#ind,1,_opacity);
26553        );
26554      );
26555    );
26556    nan;
26557  );
26558
26559  hsv2rgb(I) = ( # Convert HSV vector to RGB
26560    _I = I;
26561    _I[0]%=360;
26562    _I[1] = cut(_I[1],0,1);
26563    _I[2] = cut(_I[2],0,1);
26564    _c = _I[2]*_I[1];
26565    _x = _c*(1-abs((_I[0]/60)%2-1));
26566    (arg(1 + int(_I[0]/60),[_c,_x,0],[_x,_c,0],[0,_c,_x],[0,_x,_c],[_x,0,_c],[_c,0,_x])+=_I[2] - _c)*=255;
26567  );
26568
26569  is_intriangle(P,A,B,C) = ( # Test if a point is inside a triangle
26570    _v0 = C - A;
26571    _v1 = B - A;
26572    _v2 = P - A;
26573    _dot00 = dot(_v0,_v0);
26574    _dot01 = dot(_v0,_v1);
26575    _dot02 = dot(_v0,_v2);
26576    _dot11 = dot(_v1,_v1);
26577    _dot12 = dot(_v1,_v2);
26578    _invDenom = 1/(_dot00*_dot11 - _dot01*_dot01);
26579    _u = (_dot11*_dot02 - _dot01*_dot12)*_invDenom;
26580    _v = (_dot00*_dot12 - _dot01*_dot02)*_invDenom;
26581    _u>=0 && _v>=0 && _u + _v<1);
26582
26583  is_inquadrilateral(P,A,B,C,D) = ( # Test if a point is inside a quadrilateral
26584    is_intriangle(P,A,B,D) || is_intriangle(P,B,C,D);
26585  );
26586
26587  is_percent(str) = (unref(_is_pct); _is_pct=['#str']; _is_pct[size(_is_pct) - 1]==_'%');
26588
26589  length_spline(P0,T0,P1,T1) = ( # Compute the length of a spline P0-P1 with tangents T0,T1
26590    _P0 = P0;
26591    _P1 = P1;
26592    _C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ _P0,_P1,T0,T1 ],2);
26593    _l = norm(_P1 - _P0);
26594    if (_l,
26595      _nl = _l + 1;
26596      _dt = 1/_l;
26597      while (_nl - _l>=0.01,
26598        _l = _nl;
26599        _nl = 0;
26600        __P0 = _P0;
26601        for (_t = 0, _t<=1, _t+=_dt,
26602          __P = mul([_t^3,_t^2,_t,1],_C,2);
26603          _nl+=norm(__P - __P0);
26604          __P0 = __P;
26605        );
26606        _dt = 1/max(1,_nl);
26607      );
26608    );
26609    _l
26610  );
26611
26612  pexp(x) = ( # Good polynomial approximation of 'exp(-x^2)' for |x|<2
26613    _pexp_x = abs(x);
26614    _pexp_x<2?-0.110353*_pexp_x^4 + 0.683221*_pexp_x^3 -1.17282*_pexp_x^2 + 1:0
26615  );
26616
26617  proj(X,A,B) = ( # Projection of point X onto line (A,B)
26618    _AB = B - A;
26619    P = A + dot(X - A,_AB)/max(1e-8,dot(_AB,_AB))*_AB;
26620  );
26621
26622  # CIE DeltaE_2000 color difference function.
26623  # (code adapted from https://github.com/gfiumara/CIEDE2000).
26624  deltaE00(lab1,lab2) = (
26625    _deltaE00_deg2rad(deg) = (deg*pi/180);
26626    unref(_deltaE00_lab1); _deltaE00_lab1 = lab1;
26627    unref(_deltaE00_lab2); _deltaE00_lab2 = lab2;
26628    unref(_deltaE00_k_L); const _deltaE00_k_L = 1;
26629    unref(_deltaE00_k_C); const _deltaE00_k_C = 1;
26630    unref(_deltaE00_k_H); const _deltaE00_k_H = 1;
26631    unref(_deltaE00_deg360inrad); const _deltaE00_deg360inrad = _deltaE00_deg2rad(360);
26632    unref(_deltaE00_deg180inrad); const _deltaE00_deg180inrad = _deltaE00_deg2rad(180);
26633    unref(_deltaE00_pow25to7); const _deltaE00_pow25to7 = 25^7;
26634
26635    # Step 1
26636    _deltaE00_C1 = norm(_deltaE00_lab1[1,2]);
26637    _deltaE00_C2 = norm(_deltaE00_lab2[1,2]);
26638    _deltaE00_barC = (_deltaE00_C1 + _deltaE00_C2)/2;
26639    _deltaE00_G = 0.5*(1 - sqrt(_deltaE00_barC^7/(_deltaE00_barC^7 + _deltaE00_pow25to7)));
26640    _deltaE00_a1prime = (1 + _deltaE00_G)*_deltaE00_lab1[1];
26641    _deltaE00_a2prime = (1 + _deltaE00_G)*_deltaE00_lab2[1];
26642    _deltaE00_Cprime1 = norm(_deltaE00_a1prime,_deltaE00_lab1[2]);
26643    _deltaE00_Cprime2 = norm(_deltaE00_a2prime,_deltaE00_lab2[2]);
26644
26645    _deltaE00_lab1[2]==0 && _deltaE00_a1prime==0?(
26646      _deltaE00_hprime1 = 0;
26647    ):(
26648      _deltaE00_hprime1 = atan2(_deltaE00_lab1[2],_deltaE00_a1prime);
26649      _deltaE00_hprime1<0?(_deltaE00_hprime1+=_deltaE00_deg360inrad);
26650      _deltaE00_lab2[2]==0 && _deltaE00_a2prime==0?(
26651        _deltaE00_hprime2 = 0;
26652      ):(
26653        _deltaE00_hprime2 = atan2(_deltaE00_lab2[2],_deltaE00_a2prime);
26654        _deltaE00_hprime2<0?(_deltaE00_hprime2+=_deltaE00_deg360inrad);
26655      );
26656    );
26657
26658    # Step 2
26659    _deltaE00_deltaLprime = _deltaE00_lab2[0] - _deltaE00_lab1[0];
26660    _deltaE00_deltaCprime = _deltaE00_Cprime2 - _deltaE00_Cprime1;
26661    _deltaE00_CprimeProduct = _deltaE00_Cprime1*_deltaE00_Cprime2;
26662    _deltaE00_CprimeProduct==0?(
26663      _deltaE00_deltahprime = 0;
26664    ):(
26665      _deltaE00_deltahprime = _deltaE00_hprime2 - _deltaE00_hprime1;
26666      _deltaE00_deltahprime<-_deltaE00_deg180inrad?(
26667        _deltaE00_deltahprime += _deltaE00_deg360inrad;
26668      ):_deltaE00_deltahprime>_deltaE00_deg180inrad?(
26669        _deltaE00_deltahprime -= _deltaE00_deg360inrad;
26670      );
26671    );
26672    _deltaE00_deltaHprime = 2*sqrt(_deltaE00_CprimeProduct)*sin(_deltaE00_deltahprime/2);
26673
26674    # Step 3
26675    _deltaE00_barLprime = (_deltaE00_lab1[0] + _deltaE00_lab2[0])/2;
26676    _deltaE00_barCprime = (_deltaE00_Cprime1 + _deltaE00_Cprime2)/2;
26677    _deltaE00_hprimeSum = _deltaE00_hprime1 + _deltaE00_hprime2;
26678    _deltaE00_Cprime1*_deltaE00_Cprime2==0?(
26679      _deltaE00_barhprime = _deltaE00_hprimeSum;
26680    ):(
26681      abs(_deltaE00_hprime1 - _deltaE00_hprime2)<=_deltaE00_deg180inrad?(
26682        _deltaE00_barhprime = _deltaE00_hprimeSum/2;
26683      ):(
26684        _deltaE00_hprimeSum<_deltaE00_deg360inrad?(
26685          _deltaE00_barhprime = (_deltaE00_hprimeSum + _deltaE00_deg360inrad)/2;
26686        ):(
26687          _deltaE00_barhprime = (_deltaE00_hprimeSum - _deltaE00_deg360inrad)/2;
26688        );
26689      );
26690    );
26691
26692    _deltaE00_T = 1.0 - (0.17*cos(_deltaE00_barhprime - _deltaE00_deg2rad(30))) +
26693                         (0.24*cos(2*_deltaE00_barhprime)) +
26694                         (0.32*cos(3*_deltaE00_barhprime + _deltaE00_deg2rad(6))) -
26695                         (0.2*cos(4*_deltaE00_barhprime - _deltaE00_deg2rad(63)));
26696    _deltaE00_deltaTheta = _deltaE00_deg2rad(30)*
26697      exp(-((_deltaE00_barhprime - _deltaE00_deg2rad(275)) / _deltaE00_deg2rad(25))^2);
26698
26699    _deltaE00_R_C = 2*sqrt(_deltaE00_barCprime^7/(_deltaE00_barCprime^7 + _deltaE00_pow25to7));
26700    _deltaE00_S_L = 1 + 0.015*(_deltaE00_barLprime - 50)^2/sqrt(20 + (_deltaE00_barLprime - 50)^2);
26701    _deltaE00_S_C = 1 + 0.045*_deltaE00_barCprime;
26702    _deltaE00_S_H = 1 + 0.015*_deltaE00_barCprime*_deltaE00_T;
26703    _deltaE00_R_T = -sin(2*_deltaE00_deltaTheta)* _deltaE00_R_C;
26704
26705    sqrt((_deltaE00_deltaLprime/(_deltaE00_k_L*_deltaE00_S_L))^2 +
26706         (_deltaE00_deltaCprime/(_deltaE00_k_C*_deltaE00_S_C))^2 +
26707         (_deltaE00_deltaHprime/(_deltaE00_k_H*_deltaE00_S_H))^2 +
26708         _deltaE00_R_T*_deltaE00_deltaCprime/(_deltaE00_k_C*_deltaE00_S_C)*
26709         _deltaE00_deltaHprime/(_deltaE00_k_H*_deltaE00_S_H));
26710  );"
26711
26712#@cli mad
26713#@cli : Return the MAD (Maximum Absolute Deviation) of the last selected image.
26714#@cli : The MAD is defined as MAD = med_i|x_i-med_j(x_j)|
26715mad :
26716  if $! +-. {ic} abs. u {1.4826*ic} rm. else u 0 fi
26717
26718#@cli max_w
26719#@cli : Return the maximal width between selected images.
26720max_w :
26721  _minmax_whds max,0,1
26722
26723#@cli max_h
26724#@cli : Return the maximal height between selected images.
26725max_h :
26726  _minmax_whds max,1,1
26727
26728#@cli max_d
26729#@cli : Return the maximal depth between selected images.
26730max_d :
26731  _minmax_whds max,2,1
26732
26733#@cli max_s
26734#@cli : Return the maximal spectrum between selected images.
26735max_s :
26736  _minmax_whds max,3,1
26737
26738#@cli max_wh
26739#@cli : Return the maximal wxh size of selected images.
26740max_wh :
26741  _minmax_whds max,0,2
26742
26743#@cli max_whd
26744#@cli : Return the maximal wxhxd size of selected images.
26745max_whd :
26746  _minmax_whds max,0,3
26747
26748#@cli max_whds
26749#@cli : Return the maximal wxhxdxs size of selected images.
26750max_whds :
26751  _minmax_whds max,0,4
26752
26753#@cli median_color
26754#@cli : Return the median color value of the last selected image.
26755median_color :
26756  u {"expr('med(crop(#-1,0,0,0,y,w#-1,h#-1,d#-1,1))',1,3)"}
26757
26758#@cli min_w
26759#@cli : Return the minimal width between selected images.
26760min_w :
26761  _minmax_whds min,0,1
26762
26763#@cli min_h
26764#@cli : Return the minimal height between selected images.
26765min_h :
26766  _minmax_whds min,1,1
26767
26768#@cli min_d
26769#@cli : Return the minimal depth between selected images.
26770min_d :
26771  _minmax_whds min,2,1
26772
26773#@cli min_s
26774#@cli : Return the minimal s size of selected images.
26775min_s :
26776  _minmax_whds min,3,1
26777
26778#@cli min_wh
26779#@cli : Return the minimal wxh size of selected images.
26780min_wh :
26781  _minmax_whds min,0,2
26782
26783#@cli min_whd
26784#@cli : Return the minimal wxhxd size of selected images.
26785min_whd :
26786  _minmax_whds min,0,3
26787
26788#@cli min_whds
26789#@cli : Return the minimal wxhxdxs size of selected images.
26790min_whds :
26791  _minmax_whds min,0,4
26792
26793_minmax_whds :
26794  u {"
26795    mw = w; mh = h; md = d; ms = s;
26796    repeat (l,k,
26797      mw = $1(mw,w#k);
26798      mh = $1(mh,h#k);
26799      md = $1(md,d#k);
26800      ms = $1(ms,s#k);
26801    );
26802    ([mw,mh,md,ms])[$2,$3]"}
26803
26804
26805#@cli nmd : eq. to 'named' : (+)
26806
26807#@cli named : _mode,"name1","name2",... : (+)
26808#@cli : Return the set of indices corresponding to images of the selection with specified names.
26809#@cli : After this command returns, the status contains a list of indices (unsigned integers),
26810#@cli : separated by commas (or an empty string if no images with those names have been found).
26811#@cli : (eq. to 'nmd').
26812#@cli : 'mode' can be { 0=all indices (default) | 1=lowest index | 2=highest index | \
26813# 3 = all indices (case insensitive) | 4 = lowest index (case insensitive) | 5 = highest index (case insensitive)}
26814
26815#@cli normalize_filename : filename
26816#@cli : Return a "normalized" version of the specified filename, without spaces and capital letters.
26817normalize_filename :
26818  ('"$1"') f. 'if(i>=65&&i<=90,i+32,if(i==32,95,i))' u {t} rm.
26819
26820#@cli oct : octal_int1,...
26821#@cli : Print specified octal integers into their binary, decimal, hexadecimal and string representations.
26822oct :
26823  dec=${oct2dec\ ${^0}}
26824  e[^-1] "Convert octal integer"${arg\ 1+($#>1),"",s}" '${^0}' to binary '"${dec2bin\ $dec}"', decimal '"$dec"',
26825           hexadecimal '"${dec2hex\ $dec}"' and string '"${dec2str\ $dec}"'."
26826
26827#@cli oct2dec : octal_int1,...
26828#@cli : Convert specified octal integers into their decimal representations.
26829oct2dec :
26830  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
26831
26832_oct2dec :
26833  u {"str = vtos(abs($1));
26834      for (k = val = 0, str[k], ++k,
26835        c = str[k];
26836        (val<<=3)+=(c>=_'0' && c<=_'7'?c - _'0':nan);
26837        isnan(val)?break()
26838      ); sign($1)*val"}
26839
26840#gmic ovh2stats : logfile_globname
26841#gmic : Generate statistics on the usage of G'MIC-Qt from OVH server logfiles.
26842#gmic : logfiles must be located in current directory.
26843#gmic : Default value: 'logfile_globname=gmic.eu-*-*-*.log.gz"
26844ovh2stats : skip "${1=gmic.eu-*-*-*.log.gz}"
26845  e[^-1] "Generate statistics on the usage of G'MIC-Qt from OVH server logfiles '$1'."
26846  use_vt100
26847
26848  # Load log files.
26849  files "$1" files=${}
26850  e[] "* Load log files."
26851  if !narg(${}) error[0--2] "No OVH logfiles matching name '$1' have been found." fi
26852
26853  repeat narg(${})
26854    arg0 $>,$files file=${}
26855    day,month,year={"str = ['"$file"'];
26856      t1 = find(str,_'-') + 1;
26857      t2 = find(str,_'-',t1) + 1;
26858      t3 = find(str,_'-',t2) + 1;
26859      t1>0 && t2>t1 && t3>t2?(
26860        day = stov(str,t1);
26861        month = stov(str,t2);
26862        year = stov(str,t3);
26863        [ day,month,year ];
26864      ):[0,0,0]"}
26865    if !$day&&!$month&&!$year day,month,year=n.a fi
26866    e[] "  > "$year/$month/$day:" "$_vt100_g$file$_vt100_n
26867    it $file
26868    nm. ${year}_${month}_${day}
26869  done
26870
26871  # Keep only lines containing 'G'MIC-Qt'.
26872  repeat $! nm={$>,n} l[$>] s -,{'\n'}
26873    eval "
26874      str = ['G'MIC-Qt'];
26875      to_keep = vectorl();
26876      p = 0;
26877      repeat (l,k, find(#k,str)>=0?(to_keep[p++] = k));
26878      store(to_keep,'to_keep',p);
26879      run('$to_keep k[{^}]')"
26880    if $!>1 i[1--2] ('\n') fi
26881  a y nm $nm endl done
26882
26883  # Analyze log files.
26884  e[] "\n* Analyze log files (day by day)."
26885  +_ovh2stats    # Display stats, day by day.
26886
26887  e[] "\n* Analyze log files (all days)."
26888  a y _ovh2stats # Display stats, all days.
26889
26890
26891_ovh2stats :
26892  v + N=$! repeat $! l[$>] ('{n}') replace. {'_'},{'/'} date={t} rm.
26893    s -,{'\n'}
26894
26895    # Name each image by the IP address.
26896    eval "
26897      name = vector256();
26898      repeat (l,k,
26899        p = find(#k,_' ');
26900        copy(name,i[#k,0],p);
26901        name[p] = 0;
26902        run('name[',k,'] ',name);
26903      )"
26904
26905    # Discard IP duplicates.
26906    eval "
26907      const N = "$!";
26908      ref(vector(#2*N),tab);
26909      repeat (N,k,
26910        ref(name(#k,256),nam);
26911        for (i = j = 0, i<size(nam) && nam[i], ++i, nam[i]!=_'.'?(nam[j++] = nam[i])); nam[j] = 0;
26912        tab[2*k] = stov(nam);
26913        tab[2*k + 1] = k;
26914      );
26915      tab = sort(tab,1,N,2);
26916      for (i = 0, i<size(tab) - 1, ++i, tab[2*i]==tab[2*i + 2]?tab[2*i] = tab[2*i + 1] = -1);
26917      store(tab,'tab')"
26918    $tab discard. -1 r. 2,{h/2},1,1,-1 z. 1,1 k[{^}]
26919
26920    # Analyze G'MIC version, host software and OS.
26921    eval "
26922      gimp = krita = _8bf = paintdotnet = digikam = standalone = 0;
26923      linux = windows = macos = bsd = unknown = 0;
26924      ips = 0;
26925      repeat (l,k,
26926        ref(lowercase(crop(#k,0,0,0,0,1,256,1,1)),str);
26927        find(str,'gimp')>=0?++gimp:
26928        find(str,'krita')>=0?++krita:
26929        find(str,'8bf')>=0?++_8bf:
26930        find(str,'paint.net')>=0?++paintdotnet:
26931        find(str,'digikam')>=0?++digikam:
26932        ++standalone;
26933
26934        find(str,'windows')>=0?++windows:
26935        find(str,'linux')>=0?++linux:
26936        find(str,'mac os')>=0?++macos:
26937        find(str,'bsd')>=0?++bsd:
26938        ++unknown;
26939
26940        ++ips;
26941      );
26942      store([ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown],'features')"
26943    rm $features
26944    ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown={^}
26945
26946    if $N>1
26947      e[] "  > "$date": "\
26948          ${_vt100_b}"Unique IPs"${_vt100_n}" = "$ips" (100%), "\
26949          ${_vt100_m}${_vt100_b}"GIMP"${_vt100_n}" = "$gimp" ("{_round($gimp/$ips*100,0.1)}"%), "\
26950          ${_vt100_c}${_vt100_b}"Krita"${_vt100_n}" = "$krita" ("{_round($krita/$ips*100,0.1)}"%), "\
26951          ${_vt100_g}${_vt100_b}"8bf"${_vt100_n}" = "$_8bf" ("{_round($_8bf/$ips*100,0.1)}"%), "\
26952          ${_vt100_r}${_vt100_b}"Paint.NET"${_vt100_n}" = "$paintdotnet" ("{_round($paintdotnet/$ips*100,0.1)}"%), "\
26953          ${_vt100_m}${_vt100_b}"Digikam"${_vt100_n}" = "$digikam" ("{_round($digikam/$ips*100,0.1)}"%), "\
26954          ${_vt100_c}${_vt100_b}"Standalone"${_vt100_n}" = "$standalone" ("{_round($standalone/$ips*100,0.1)}"%)."
26955
26956      e[] "                "\
26957          ${_vt100_r}${_vt100_b}"Windows"${_vt100_n}" = "$windows" ("{_round($windows/$ips*100,0.1)}"%), "\
26958          ${_vt100_g}${_vt100_b}"Linux"${_vt100_n}" = "$linux" ("{_round($linux/$ips*100,0.1)}"%), "\
26959          ${_vt100_m}${_vt100_b}"Mac OS"${_vt100_n}" = "$macos" ("{_round($macos/$ips*100,0.1)}"%), "\
26960          ${_vt100_c}${_vt100_b}"BSD"${_vt100_n}" = "$bsd" ("{_round($bsd/$ips*100,0.1)}"%), "\
26961          ${_vt100_c}${_vt100_b}"Unknown"${_vt100_n}" = "$unknown" ("{_round($unknown/$ips*100,0.1)}"%).\n"
26962    fi
26963
26964  endl done
26965  + / $N round.
26966  ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown={^}
26967  summary0,summary1=Total,Average
26968  e[] "  > "${summary{$N>1}}" : "\
26969      ${_vt100_b}"Unique IPs"${_vt100_n}" = "$ips" (100%), "\
26970      ${_vt100_m}${_vt100_b}"GIMP"${_vt100_n}" = "$gimp" ("{_round($gimp/$ips*100,0.1)}"%), "\
26971      ${_vt100_c}${_vt100_b}"Krita"${_vt100_n}" = "$krita" ("{_round($krita/$ips*100,0.1)}"%), "\
26972      ${_vt100_g}${_vt100_b}"8bf"${_vt100_n}" = "$_8bf" ("{_round($_8bf/$ips*100,0.1)}"%), "\
26973      ${_vt100_r}${_vt100_b}"Paint.NET"${_vt100_n}" = "$paintdotnet" ("{_round($paintdotnet/$ips*100,0.1)}"%), "\
26974      ${_vt100_m}${_vt100_b}"Digikam"${_vt100_n}" = "$digikam" ("{_round($digikam/$ips*100,0.1)}"%), "\
26975      ${_vt100_c}${_vt100_b}"Standalone"${_vt100_n}" = "$standalone" ("{_round($standalone/$ips*100,0.1)}"%)."
26976
26977  e[] "                "\
26978      ${_vt100_r}${_vt100_b}"Windows"${_vt100_n}" = "$windows" ("{_round($windows/$ips*100,0.1)}"%), "\
26979      ${_vt100_g}${_vt100_b}"Linux"${_vt100_n}" = "$linux" ("{_round($linux/$ips*100,0.1)}"%), "\
26980      ${_vt100_m}${_vt100_b}"Mac OS"${_vt100_n}" = "$macos" ("{_round($macos/$ips*100,0.1)}"%), "\
26981      ${_vt100_c}${_vt100_b}"BSD"${_vt100_n}" = "$bsd" ("{_round($bsd/$ips*100,0.1)}"%), "\
26982      ${_vt100_c}${_vt100_b}"Unknown"${_vt100_n}" = "$unknown" ("{_round($unknown/$ips*100,0.1)}"%).\n"
26983  rm
26984
26985#@cli padint : number,_size>0
26986#@cli : Return a integer with 'size' digits (eventually left-padded with '0').
26987padint : check "isint($1)" skip ${2=4}
26988  u "" repeat $2 u ${}{int($1/10^$<)%10} done
26989
26990#@cli path_cache
26991#@cli : Return a path to store G'MIC data files for one user (whose value is OS-dependent).
26992path_cache :
26993  if !narg({'$_path_cache'})
26994    _path_cache=$_path_rc
26995    if ['$GMIC_CACHE_PATH']!=0 _patch_cache=$GMIC_CACHE_PATH
26996    elif !${-is_windows}
26997      if isdir(['{/$HOME/.cache}']) _path_cache=$HOME/.cache/gmic/
26998      elif ['$XDG_CACHE_HOME']!=0 _path_cache=$XDG_CACHE_HOME/gmic/
26999      fi
27000      if !isdir('{/$_path_cache}') x "mkdir -p "$_path_cache fi
27001    fi
27002  fi
27003  u $_path_cache
27004
27005#@cli path_current
27006#@cli : Return current folder from where G'MIC has been run.
27007path_current :
27008  if !${-is_windows}
27009    filename=${-path_tmp}gmic_pwd
27010    l[] x "pwd > "$filename it $filename autocrop {'\n'} autocrop {'" "'} u {t}/ rm onfail u "./" endl
27011  else u "./"
27012  fi
27013
27014#@cli path_gimp
27015#@cli : Return a path to store GIMP configuration files for one user (whose value is OS-dependent).
27016path_gimp :
27017  if !narg({'$_path_gimp'})
27018    if narg({'${GIMP2_DIRECTORY}'}) _path_gimp=${GIMP2_DIRECTORY}
27019    elif narg({'${USERPROFILE}'}) _path_gimp=${USERPROFILE}
27020    elif narg({'${HOME}'}) _path_gimp=${HOME}
27021    fi
27022    if ${-is_windows} sep={`92`} else sep=/ fi
27023    if isdir(['{/$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.10}'])
27024      _path_gimp=$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.10${sep}
27025    elif isdir(['{/$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.8}'])
27026      _path_gimp=$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.8${sep}
27027    elif isdir(['{/$_path_gimp${sep}.config${sep}GIMP${sep}2.10}'])
27028      _path_gimp=$_path_gimp${sep}.config${sep}GIMP${sep}2.10${sep}
27029    elif isdir(['{/$_path_gimp${sep}.gimp-2.8}'])
27030      _path_gimp=$_path_gimp${sep}.gimp-2.8${sep}
27031    elif isdir(['{/$_path_gimp${sep}.gimp-2.6}'])
27032      _path_gimp=$_path_gimp${sep}.gimp-2.6${sep}
27033    else
27034      _path_gimp=${-path_tmp}
27035    fi
27036  fi
27037  u $_path_gimp
27038
27039#@cli path_tmp
27040#@cli : Return a path to store temporary files (whose value is OS-dependent).
27041path_tmp :
27042  if !narg({'$_path_tmp'})
27043    if narg({'${TMP}'}) _path_tmp=${TMP}
27044    elif narg({'${TEMP}'}) _path_tmp=${TEMP}
27045    elif narg({'${TMPDIR}'}) _path_tmp=${TMPDIR}
27046    elif narg({'${HOME}'}) _path_tmp="/tmp"
27047    fi
27048    if ${-is_windows} _path_tmp=$_path_tmp{`92`} else _path_tmp=$_path_tmp/ fi
27049  fi
27050  u $_path_tmp
27051
27052#@cli remove_copymark : "image_name"
27053#@cli : Remove copy mark from names of selected images.
27054remove_copymark :
27055  u {`"name = ['$1'];
27056       i = find(name,'_c');
27057       i>=0 && i<size(name) - 2 && (c=name[i+2])>=_'1' && c<=_'9'?(
27058         ext = ['"{$>,x}"'];
27059         size(ext)?copy(name[i],[_'.',ext,0]):(name[i]=0);
27060       ); name"`}
27061
27062# render_donations: Parse the PayPal .CSV file and generate 'latest-months' donation gfx.
27063# $1 = filename.csv
27064# $2 = number of latest months.
27065render_donations : skip "${1="$HOME"/work/src/gmic_donations/donations.csv}",${2=0}
27066  use_vt100
27067  nb_months={$2?$2:4}
27068  l[]
27069    it[] "$1" s -,{'\n'}
27070    rm[0] # Discard labels
27071
27072    # Parse CSV data.
27073    eur2usd={1/0.7988} # Change rate from EUR to USD (Paypal conversion rate).
27074    nb_entries=$!
27075    repeat $!,e l[0]
27076
27077      # Split CSV items.
27078      repeat 10 l$>= done
27079      f ">begin(iss=0);i==_'\"'?(iss=!iss;i):i==_',' && !iss?-1:i" s -,-1 autocrop {'\"'}
27080      if $!>3 f[3] "lowercase(i)" fi # currency
27081      if $!>4 replace[4-{min($!,6)}] {','},{'.'} fi # amounts
27082      repeat $! l$>={$>,t} done
27083      rm
27084
27085      # Available values.
27086      date_$e=$l0
27087      name_$e=$l1
27088      type_$e=$l2
27089      currency_$e=$l3
27090      donation_$e={$l4}
27091      charge_paypal_$e={$l5}
27092      charge_lila_$e={$l6}
27093      mail_$e=$l7
27094      message_$e=$l8
27095
27096      # Deduced values.
27097      l[]
27098        ('${date_$e}') s -,{'/'} day_$e,month_$e,year_$e={0,t},{1,t},{2,t} rm
27099        edate_$e={${day_$e}+100*(${month_$e}+100*${year_$e})}
27100      endl
27101      donation_charged_$e={${donation_$e}+${charge_paypal_$e}+${charge_lila_$e}}
27102
27103      if '${currency_$e}'=='eur'
27104        donation_eur_$e=${donation_$e}
27105        donation_charged_eur_$e=${donation_charged_$e}
27106        donation_usd_$e={${donation_$e}*$eur2usd}
27107        donation_charged_usd_$e={${donation_charged_$e}*$eur2usd}
27108      else
27109        donation_usd_$e=${donation_$e}
27110        donation_charged_usd_$e=${donation_charged_$e}
27111        donation_eur_$e={${donation_$e}/$eur2usd}
27112        donation_charged_eur_$e={${donation_charged_$e}/$eur2usd}
27113      fi
27114
27115      # Print infos.
27116      if ${donation_$e}>=15 col=$_vt100_r else col= fi
27117      e[] "- ["$_vt100_c$_vt100_b"#"$e$_vt100_n" - "$_vt100_c${date_$e}$_vt100_n"] "\
27118          $col$_vt100_b{_${donation_$e}}" "${currency_$e}$_vt100_n\
27119          " (paypal: "{_${charge_paypal_$e}}" "${currency_$e},\
27120          " lila: "{_${charge_lila_$e}}" "${currency_$e}" ->"\
27121          " "{_${donation_charged_$e}}" "${currency_$e}" ="\
27122          " "{_${donation_charged_eur_$e}}" eur),"\
27123          " from "${name_$e}" ("${mail_$e}") : '"$_vt100_b${message_$e}$_vt100_n"'"
27124    endl done
27125
27126    # Compute image for donations of the last months.
27127    e[] ""
27128    all_eur,all_usd,all_charged_eur,all_charged_usd,asep_eur,asep_usd=
27129    repeat $nb_months
27130      edate={y=date(0);m=date(1)-1-$>;while(m<=0,--y;m+=12);100*y+m}
27131      s_month=${"arg "$edate%100",January,February,March,April,May,June,July,August,\
27132                                  September,October,November,December"}
27133      s_year={int($edate/100)}
27134
27135      month_eur,month_charged_eur,month_usd,month_charged_usd,msep_eur,msep_usd=
27136      nb_sponsors=0
27137      repeat $nb_entries,e if $edate==int(${edate_$e}/100)
27138        currency=${currency_$e}
27139        val_eur=${donation_eur_$e}
27140        val_charged_eur=${donation_charged_eur_$e}
27141        val_usd=${donation_usd_$e}
27142        val_charged_usd=${donation_charged_usd_$e}
27143        nb_sponsors+=1
27144
27145        if '${currency_$e}'=='eur'
27146          all_eur.=$asep_eur$val_eur
27147          all_charged_eur.=$asep_eur$val_charged_eur
27148          month_eur.=$msep_eur$val_eur
27149          month_charged_eur.=$msep_eur$val_charged_eur
27150          asep_eur=, msep_eur=,
27151        else
27152          all_usd.=$asep_usd$val_usd
27153          all_charged_usd.=$asep_usd$val_charged_usd
27154          month_usd.=$msep_usd$val_usd
27155          month_charged_usd.=$msep_usd$val_charged_usd
27156          asep_usd=, msep_usd=,
27157        fi
27158      fi done
27159
27160      month_sum_eur={sum(0$month_eur)+sum(0$month_usd)/$eur2usd}
27161      month_sum_charged_eur={sum(0$month_charged_eur)+sum(0$month_charged_usd)/$eur2usd}
27162
27163      e[] "* "$_vt100_c$_vt100_b$s_month" "$s_year": "$_vt100_n\
27164          $_vt100_b{_$month_sum_eur}" eur"$_vt100_n" (charged: "{_$month_sum_charged_eur}" eur)"\
27165          " = "{_sum(0$month_eur)}" eur"$_vt100_n" (charged: "{_sum(0$month_charged_eur)}" eur)"\
27166          " + "{_sum(0$month_usd)}" usd (charged: "{_sum(0$month_charged_usd)}" usd)"
27167
27168      600,30,1,3,'x<$month_sum_eur?[139,181,173]+(y>h/2?0:40):[220,230,240]' r. 500,30,1,3 c. 0,255
27169      shape_circle 24 s. x,2 s[-2,-1] y,2
27170      ri[-4] [-5],0,1,0,0 ri[-3] [-5],0,1,0,1 ri[-2] [-5],0,1,1,0 ri[-1] [-5],0,1,1,1 min[-4--1]
27171      channels. 0  *.. . *. 255 a[-2,-1] c
27172      r. {[w,h]+4},1,100%,0,0,0.5,0.5
27173      sh. 100% dilate_circ. 3 b. 1 rm.
27174
27175      t. $s_month" "$s_year,0.02~,0.5~,24,0.5,0,0,0,255
27176      t. {_round($month_sum_eur)}" \37 = "{_round($month_sum_eur*$eur2usd)}" $",0.5~,0.5~,24,1,0,0,0,255
27177      t. $nb_sponsors" sponsor"${"if "$nb_sponsors"!=1 u s else u \"\" fi"},0.97~,0.5~,24,0.5,0,0,0,255
27178    done
27179    rv
27180
27181    all_max={max($all_eur,[$all_usd]/$eur2usd)}
27182    all_min={min($all_eur,[$all_usd]/$eur2usd)}
27183    all_sum_eur={sum($all_eur)}
27184    all_sum_usd={sum($all_usd)}
27185    all_sum_charged_eur={sum($all_charged_eur)}
27186    all_sum_charged_usd={sum($all_charged_usd)}
27187    all_avg={avg($all_eur,[$all_usd]/$eur2usd)}
27188    all_med={med($all_eur,[$all_usd]/$eur2usd)}
27189
27190    0 t. "Avg: "{_round($all_avg,0.1)}"\37 / "\
27191         "Med: "{_round($all_med,0.1)}"\37 / "\
27192         "Min: "{_round($all_min,0.1)}"\37 / "\
27193         "Max: "{_round($all_max,0.1)}"\37",0,0,24,1,1
27194    *. 200 i[-2] 100%,100%,1,3 a[-2,-1] c
27195
27196    rows -5,100% a y,0.5 r2dx 480
27197    if $2==0 o $HOME/work/src/gmic/html/img/donations_latest_months.png fi
27198  endl
27199
27200  total_eur={$all_sum_eur+$all_sum_usd/$eur2usd}
27201  total_charged_eur={$all_sum_charged_eur+$all_sum_charged_usd/$eur2usd}
27202  e[] "\n==> "$_vt100_c${_vt100_b}"TOTAL : "$_vt100_n\
27203      $_vt100_b{_floor($total_eur)}" eur"$_vt100_n" (charged : "{_floor($total_charged_eur)}" eur)"\
27204      " = "{_floor($all_sum_eur)}" eur (charged: "{_floor($all_sum_charged_eur)}" eur)"\
27205      " + "{_floor($all_sum_usd)}" usd (charged: "{_floor($all_sum_charged_usd)}" usd)\n"
27206
27207  # Generate report image for Twitter.
27208  sp gmicky,{h}
27209  l[-2,-1]
27210    rgb={I(w-1)} to_rgba rv
27211    frame 10,10,0,0,0,0
27212    drgba $rgb a x
27213    shape_heart 82 +r. 100%,100%,1,3 hsv2rgb. rv[-2,-1] *. 255 a[-2,-1] c
27214    r. ..,..,1,100%,0,0,0.23,0.15 drop_shadow. 3,3,2,0,0
27215    blend alpha,0.7 r2dx 500
27216    if $2==0 o $HOME/Desktop/donations_latest_months.jpg,85 rm return fi
27217  endl
27218
27219  # Generate coin graphics.
27220  l[]
27221    $HOME/work/src/gmic/html/img/icon_coin.png
27222    N=0
27223    text$N,r$N,g$N,b$N,file$N="2 \37",32,48,32,"don_2eur.png" N+=1
27224    text$N,r$N,g$N,b$N,file$N="5 \37",200,200,200,"don_5eur.png" N+=1
27225    text$N,r$N,g$N,b$N,file$N="10 \37",190,100,100,"don_10eur.png" N+=1
27226    text$N,r$N,g$N,b$N,file$N="+ \37",255,128,0,"don_moreeur.png" N+=1
27227    text$N,r$N,g$N,b$N,file$N="2 $",32,48,32,"don_2usd.png" N+=1
27228    text$N,r$N,g$N,b$N,file$N="5 $",200,200,200,"don_5usd.png" N+=1
27229    text$N,r$N,g$N,b$N,file$N="10 $",190,100,100,"don_10usd.png" N+=1
27230    text$N,r$N,g$N,b$N,file$N="+ $",255,128,0,"don_moreusd.png" N+=1
27231    repeat $N +l[0]
27232      frame. 10,10,0,0,0,0
27233      sh. 0,2
27234      (${r$>}^${g$>}^${b$>}) rgb2hsl[-2,-1] H,S={[R,G]} rm.
27235      l. s c -[0] {ia#0-$H} %[0] 360 -[1] {ia#1-$S} +[2] 0.1 /[1] 2 c[1,2] 0,1 a c endl
27236      hsl2rgb. rm.
27237      sh. 100% dilate_circ. 10 rm.
27238
27239      100%,100% t. ${text$>},0.5~,0.5~,45%,1,255 dilate_circ. 10 +dilate_circ. 15 to_rgb.. a[-2,-1] c
27240      blend[-2,-1] alpha drop_shadow. 1,1 r2dx. 48 frame 10,5,0,0,0,0
27241      outfile=$HOME/work/src/gmic/html/img/${file$>}
27242      if !isfile('$outfile') o. $outfile fi
27243      rm.
27244    endl done
27245    rm[0]
27246  endl
27247
27248#@cli reset
27249#@cli : Reset global parameters of the interpreter environment.
27250reset :
27251  e[^-1] "Reset global parameters of the interpreter environment."
27252  db3d m3d md3d f3d l3d sl3d ss3d
27253
27254#@cli rgb
27255#@cli : Return a random int-valued RGB color.
27256rgb :
27257  u {round(u(255))},{round(u(255))},{round(u(255))}
27258
27259RGB : rgb
27260
27261#@cli rgba
27262#@cli : Return a random int-valued RGBA color.
27263rgba :
27264  u {round(u(255))},{round(u(255))},{round(u(255))},{round(u(255))}
27265
27266RGBA : rgba
27267
27268#@cli shell_cols
27269#@cli : Return the estimated number of columns of the current shell.
27270shell_cols :
27271  if ${-is_windows} u 80
27272  else
27273    file_rand filename=${}
27274    l[] cols=80 x "tput cols > "$filename it $filename if isint({t}) cols={{t}} fi rm onfail cols=80 rm endl
27275    delete $filename
27276    u $cols
27277  fi
27278
27279#@cli size_value
27280#@cli : Return the size (in bytes) of an image value.
27281size_value :
27282  u {str=['$_pixeltype'];echo(str);str=='float'?4:str=='double'?8:0}
27283
27284
27285#@cli std_noise
27286#@cli : Return the estimated noise standard deviation of the last selected image.
27287std_noise :
27288  if $! +laplacian. -. {ic} abs. u {1.4826*ic/sqrt(d==1?20:42)} rm. else u 0 fi
27289
27290#@cli str : string
27291#@cli : Print specified string into its binary, octal, decimal and hexadecimal representations.
27292str : skip $1
27293  dec={'$*'}
27294  e[^-1] "Convert string '$*' to binary '"${dec2bin\ $dec}"', octal '"${dec2oct\ $dec}"', decimal '"$dec"' and
27295           hexadecimal '"${dec2hex\ $dec}"'."
27296
27297#@cli str2hex : "string"
27298#@cli : Convert specified string argument into a sequence of hexadecimal values (returned as a string).
27299#@cli : See also: ''hex2str''.
27300#@cli : $ hex=${"str2hex \"Hello my friends\""} echo $hex
27301#@cli : \n~~~\n[gmic]-0./ Start G'MIC interpreter.\n[gmic]-0./ 48656c6c6f206d7920667269656e6473\n\
27302# [gmic]-0./ End G'MIC interpreter.\n~~~\n
27303str2hex :
27304  ('"$*"') y. r. 2,100% f. 'v=if(x,i%16,int(i/16));if(v<=9,48+v,87+v)' u {t} rm.
27305
27306#@cli strcapitalize : string
27307#@cli : Capitalize specified string.
27308strcapitalize :
27309  l[] ('{/"$1"}')
27310    f "(i<=_' ' || i==_'_') && i!=_'\n'?_' ':i" # Replace blank characters and underscores
27311    f ">
27312       t(s) = ( unref(ss); const ss = size(s); lowercase(crop(x,ss))!=s || j[ss]!=_' ');
27313       !x || (p=j[-1])==_'.' || p==_':' || p==_';' || p==_'\"' || (find(#0,_' ',x)<0 && p==_' ') ||
27314       (p==_' ' || p==_'-') &&
27315        t('a') && t('an') && t('and') && t('as') && t('at') && t('but') && t('by') && t('for') && t('from') &&
27316        t('in') && t('into') && t('like') && t('near') && t('nor') && t('of') && t('off') && t('on') &&
27317        t('onto') && t('or') && t('over') && t('past') && t('per') && t('than') && t('the') && t('to') &&
27318        t('up') && t('upon') && t('via') && t('with')?uppercase(i):i"
27319    u {t} rm endl
27320
27321#@cli strcontains : string1,string2
27322#@cli : Return 1 if the first string contains the second one.
27323strcontains :
27324  u {"find(['$1'],['$2'])>=0"}
27325
27326#@cli strlen : string1
27327#@cli : Return the length of specified string argument.
27328strlen : skip "${1=}"
27329  u {narg({'"$1"'})}
27330
27331#@cli strreplace : string,search,replace
27332#@cli : Search and replace substrings in an input string.
27333strreplace : skip "${3=}"
27334  if narg("$3")
27335    ls=${strlen\ "$2"}
27336    lr={${strlen\ "$3"}-1}
27337    l[] ('"$1"':y) s +,{'"$2"'} s y,-$ls
27338    repeat $! if [{$>,^}]==['"$2"'] rows[$>] 0,$lr f[$>] {'"$3"'} fi done
27339    a y u {t} rm endl
27340  else
27341    l[] ('"$1"') s -,{'"$2"'} a y u {t} rm endl
27342  fi
27343
27344#@cli strlowercase : string
27345#@cli : Return a lower-case version of the specified string.
27346strlowercase :
27347  ('"$*"') u {`lowercase([{^}])`} rm.
27348
27349#@cli struppercase : string
27350#@cli : Return an upper-case version of the specified string.
27351struppercase :
27352  ('"$*"') u {`uppercase([{^}])`} rm.
27353
27354#@cli strvar : "string"
27355#@cli : Return a simplified version of the specified string, that can be used as a variable name.
27356#@cli : (version that creates a lowercase result, no longer than 128 chars).
27357strvar :
27358  _strvar_fn="lowercase(c)" _strvar "$*"
27359
27360#@cli strcasevar : "string"
27361#@cli : Return a simplified version of the specified string, that can be used as a variable name.
27362#@cli : (version that keeps original case of specified string, no longer than 128 chars).
27363strcasevar :
27364  _strvar_fn="c" _strvar "$*"
27365
27366_strvar :
27367  l[]
27368    ('"$*"':y) f. "c = i; inrange(c,_'0',_'9') || inrange(c,_'a',_'z') ||
27369                          inrange(c,_'A',_'Z') || c==_'_'?"$_strvar_fn":_'_'"
27370    rows. 0,{min(h,128)-1} autocrop. {'_'}
27371    if inrange(i,_'0',_'9') i.. ('_') a[-2,-1] y fi
27372    do h={h} replace_str. "__","_" while h!=$h
27373    u {t} rm
27374  endl
27375
27376#@cli strver : _version
27377#@cli : Return the specified version number of the G'MIC interpreter, as a string.
27378#@cli : Default value: 'version=$_version'.
27379strver : skip "${1=}"
27380  if isnum("$1")" && $1>0" ver="$1" else ver=$_version noarg fi
27381  ('$ver') r. {2*w-1} f. 'if(x%2,_'.',i)' u {t} rm.
27382
27383#@cli tic
27384#@cli : Initialize tic-toc timer.
27385#@cli : Use it in conjunction with 'toc'.
27386tic :
27387  e[^-1] "Initialize timer."
27388  if !narg($_ticpos) _ticpos=0 fi _tic$_ticpos=$| _ticpos+=1
27389
27390#@cli toc
27391#@cli : Display elapsed time of the tic-toc timer since the last call to 'tic'.
27392#@cli : This command returns the elapsed time in the status value.
27393#@cli : Use it in conjunction with 'tic'.
27394toc :
27395  v=$^ _ticpos-=1 u {_$|-${_tic$_ticpos}}
27396  v 0 e[^-1] "Elapsed time: "${}" s". v $v
27397
27398#@cli to_clutname : "string"
27399#@cli : Return simplified name that can be used as a CLUT name, from specified input string.
27400to_clutname :
27401  0 nm. "$1" nm={b} rm.
27402  u {`"ss = lowercase([['"{/$nm}"'],0]);
27403       const N = 2*size(ss);
27404       sd = vectorN();
27405       for (ps = pd = 0, ss[ps], ++ps,
27406         ss[ps]<=_' '?(sd[pd++] = _'_'):
27407         (ss[ps]==_'(' || ss[ps]==_')' ||
27408          ss[ps]==_'{' || ss[ps]==_'}' ||
27409          ss[ps]==_'[' || ss[ps]==_']' ||
27410          ss[ps]==_'\'' || ss[ps]==_'\"')?0:
27411         (ps && ss[ps]>=_'0' && ss[ps]<=_'9' && ss[ps-1]>=_'a' && ss[ps-1]<=_'z')?(sd[pd++] = _'_'; sd[pd++] = ss[ps]):
27412         (sd[pd++] = ss[ps]);
27413       ); sd[pd] = 0; sd"`}
27414
27415#@cli uchar2base64 : _encoding={ 0=base64 | 1=base64url }
27416#@cli : Encode the values of the latest of the selected images as a base64-encoded string.
27417#@cli : The string can be decoded using command 'base642uchar'.
27418#@cli : Selected images must have values that are integers in [0,255].
27419#@cli : Default values: 'encoding=0'.
27420uchar2base64 : skip "${1=0}"
27421  if isnum("$1") encoding=$1 else encoding=0 noarg fi
27422  {ceil(whds*4/3)+([0,2,1])[whds%3]}
27423  eval "
27424    hash = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
27425    "$encoding"?(hash[62] = _'-'; hash[63] = _'_');
27426    od = ov = n = 0;
27427    for (os = 0, os<whds#-2,
27428      v = i[#-2,os++]&255;
27429      n==0?(i[#-1,od++] = hash[v>>2]; ov = v; n = 1):
27430      n==1?(i[#-1,od++] = hash[((ov&3)<<4) | (v>>4)]; ov = v; n = 2):
27431      (i[#-1,od++] = hash[((ov&15)<<2) | (v>>6)]; i[#-1,od++] = hash[v&63]; n = 0);
27432    );
27433    n==1?(i[#-1,od++] = hash[((ov&3)<<4)]; copy(i[#-1,od],_'=',2,1,0); od+=2):
27434    n==2?(i[#-1,od++] = hash[((ov&15)<<2)]; i[#-1,od++] = _'=');
27435  "
27436  u {t} rm.
27437
27438#-------------------------------------
27439#
27440#@cli :: Other Interactive Commands
27441#
27442#-------------------------------------
27443
27444#@cli demos : _run_in_parallel={ 0=no | 1=yes | 2=auto }
27445#@cli : Show a menu to select and view all G'MIC interactive demos.
27446demos : check "isint(${1=2}) && $1>=0 && $1<=2" check_display $0
27447  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
27448  strver=${-strver}
27449  if $_prerelease strver=${strver}_pre#$_prerelease fi
27450  e[] "\n
27451------ "${g}"G\47MIC demos"$n" ------------------\n
27452----\n
27453---- "${c}"Mouse button"$n" to select a demo.\n
27454---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
27455---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
27456---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
27457----\n
27458-------------------------------------"
27459  l[]
27460  entries="2048 game","Blobs Editor","Bouncing Balls","Connect Four","Fire Effect","Fireworks","Fish-Eye Effect",\
27461          "Fourier Filtering","Tower of Hano\357","Histogram Demo","Hough Transform","Jawbreaker","Virtual Landscape",\
27462          "The Game of Life","Light Effect","Mandelbrot Explorer","3D Metaballs","Minesweeper","Minimal Path",\
27463          "Pacman","Paint","Plasma Effect","RGB Quantization","3D Reflection","3D Rubber Object","Shade Bobs",\
27464          "Spline Editor","3D Starfield","Tetris","Tic-Tac-Toe","Image Waves","Fractal Whirls","Color Curves"
27465  commands=x_2048,x_blobs,x_bouncing,x_connect4,x_fire,x_fireworks,x_fisheye,\
27466           x_fourier,x_hanoi,x_histogram,x_hough,x_jawbreaker,x_landscape,x_life,\
27467           x_light,x_mandelbrot,x_metaballs3d,x_minesweeper,x_minimal_path,x_pacman,x_paint,\
27468           x_plasma,x_quantize_rgb,x_reflection3d,x_rubber3d,x_shadebobs,x_spline,x_starfield3d,\
27469           x_tetris,x_tictactoe,x_waves,x_whirl,_demo_color_curves
27470  nb_entries={narg($entries)}
27471  parallel_mode={1-if($1!=2,$1,$_cpus>=2)}
27472  strver=${-strver}
27473  if $_prerelease strver.=_pre#$_prerelease fi
27474
27475  # Generate menu graphics.
27476  l[]
27477    repeat $nb_entries
27478      arg 1+$>,$entries entry=${}
27479      0 t. $entry,0,0,24,1,1
27480    done
27481    r ${-max_wh},1,1,0,0,0.5,0.5
27482    frame 12,6,0 a z
27483    +n[0] 0,255
27484    +shift[0] 1,3,0,0 max[0,-1]
27485    +f[0] z+1
27486    1,1,100%,3,u(128,255) r. 1,[0],[0],3 *. 'y/(h-1)' r. [0],[0],[0],3,3
27487
27488    100%,100%,1,1,"x = min(x,w-1-x); y = min(y,h-1-y); (x*y/wh)^0.7>0.01"
27489    +dilate. 3 xor.. .
27490    +[0] ..
27491    *.. 100 +[1,-2]
27492    *[2,3] .
27493    rm.
27494    frame 4,4,0
27495    a c s z
27496    append_tiles 3 s c
27497    to_rgb[1] a[3-5] c
27498
27499    0 t. "G\'MIC demos",2,2,32,1,1,1,1
27500    if $_prerelease 0 t. "Version:\n"$strver,40,1,18,1,1,1,1 rows. 2,100%
27501    else 0 t. "Version: "$strver,40,1,18,1,1,1,1 rows. -2,100%
27502    fi
27503    a[-2,-1] x,0.5
27504    b. 0.5 n. 0,1
27505    (32;255^255;255^0;0) r. ..,..,1,3,3 *[-2,-1] round. 1,-1 +!=. 0 channels. 1
27506
27507    s=8
27508    r[^-2,-1] 100%,{h+h#0+2*$s+6},1,100%,0,0,0,1
27509    rectangle[0] 0,$s,100%,{$s+6+h-1},1,0.6
27510    rectangle[1] 0,$s,100%,{$s+6+h-1},1,120,120,32
27511    j[0] .,{(w#0-w)/2},{$s+3},0,0,-1
27512    j[1] ..,{(w#0-w)/2},{$s+3},0,0,1,. rm[-2,-1]
27513    c[0] 0,1
27514    nm menu_opac,menu_fgcol,menu_ind,menu_bgcol
27515  endl
27516
27517  # Generate 3D cursors.
27518  arrow3d 20,20,0,0,0,0,20%,40%,40% col3d. 255,128,0 nm. cursor3d
27519
27520  # Generate 3D background object.
27521  l[]
27522    n=16
27523    chromeball64x64[] 200,100,64 n. 0,230 s. c,-3
27524    rgb2hsv.. r.. 100%,100%,$n,3 f.. "[z*360/d,G,B]" hsv2rgb..
27525    N={6*$n} P={2*$N-1}
27526    i[0] ('CImg3d') i[1] ($N,$P) i[2] 3,$N # Header, points
27527    i[3] 2,$N,1,1,"x?y:1" i[4] 3,{$N-1},1,1,"x==0?2:x==1?y:y+1" y[3,4] a[3,4] y # Primitives
27528    l[4] # Colors
27529      s z i[0--2] (-128,{w},{h},3) 4,{$N-$n},1,1,'x==0?-128:x==1?y%$n:0' # Colors
27530      3,{$P-$N},1,1,200 y a y
27531    endl
27532    l[5] # Opacities
27533      n 0,0.5 i[0] (-128,{w},{h},1) 4,{$N-1},1,1,-128,0,0,0
27534      1,{$P-$N},1,1,0.25 y a y
27535    endl
27536    y a y
27537    nm background3d
27538  endl
27539
27540  # Generate background.
27541  {menu_fgcol,[w,h]},1,3 +plasma. 1,1,5 n. 0,230 water. 100
27542  (0.1;0.03^0;0.1^0.2;0.1) ri. ..,3 *[-2,-1]
27543  (0;1) r. ..,..,1,1,3 pow. 1.5 n. 0.2,1.15 *[-2,-1] n. 0,128
27544  nm. background
27545  w. -1,-1,0,"[G'MIC - "$strver"]" cursor 0 w[] -1,-1,0,0,{([{*,u,v}]-[{*,w,h}])/2}
27546
27547  # Enter event loop.
27548  omb,ind_clicked,cfx,cfy,cfz,alpha=0
27549  nfx,nfy,nfz={[g,g,g]} time0={$|-4}
27550  do
27551    mx,my,mb={menu_fgcol,x={*,x};y={*,y};[x<0?-1:x*(w-1)/({*,w}-1),y<0?-1:y*(h-1)/({*,h}-1),{*,b}]}
27552    ind={menu_ind,i($mx,$my)}
27553    if $mb" && "!$ind_clicked ind_clicked=$ind fi
27554
27555    # Render current view.
27556    [background]
27557
27558    3,$N,1,1,"const t = 0.8*"$|"; const a = "$alpha"; const oma = 1 - a;
27559              x==0?oma*cos("$cfx"*y + t) + a*cos("$nfx"*y + t):
27560              x==1?oma*sin("$cfy"*y + t) + a*sin("$nfy"*y + t):
27561                   oma*sin("$cfz"*y + t) + a*sin("$nfz"*y + t)"
27562    y.
27563    j[background3d] .,0,8 rm.
27564    +r3d[background3d] 1,2,3,{20*$|} *3d. {menu_fgcol,[w,h]/2-30},300 +3d. 0,0,300
27565    j3d.. .,50%,50%,0,1,1,0,0,200 rm.
27566    if $|-$time0>5 alpha+=0.02 fi
27567    if $alpha>1 alpha-=1 cfx,cfy,cfz=$nfx,$nfy,$nfz nfx,nfy,nfz={[g,g,g]} time0={$|-u*3} fi
27568
27569    if $ind>0 +==[menu_ind] # Draw selection background
27570      $ind j.. [menu_bgcol],0,0,0,0,{$mb" && "$ind_clicked==$ind?0.6:1},. rm.
27571    fi
27572    j. [menu_fgcol],0,0,0,0,1,[menu_opac]
27573
27574    if $mx>0
27575      +r3d[cursor3d] 1,1.3,0.6,{50*cos($|)}
27576      j3d.. .,$mx,$my,0,1,4,0,0,800,{-2,[w,h]/2},-1000,0.7 rm.
27577    fi
27578
27579    w. -1,-1,0
27580    if {*,CTRLLEFT}" && "{*,D} w. 150%,150% elif {*,CTRLLEFT}" && "{*,C} w. 100%,100% fi
27581    rm.
27582
27583    # Manage user selection.
27584    if !$mb" && "$omb" && "$ind_clicked" && "$ind_clicked==$ind
27585      m "com : v 1 "${arg\ $ind,$commands} parallel $parallel_mode,"l[] com rm endl" um com
27586    elif !$mb ind_clicked=0
27587    fi
27588    omb=$mb
27589
27590    wait 20
27591  while {*}" && "!{*,ESC}" && "!{*,Q}
27592  rm w 0 endl v 0 e[] ""
27593
27594_demo_color_curves :
27595  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
27596  if !narg($__demo_color_curve)
27597    e[] "\n
27598------ "${g}"Color curves"$n" ----------------------------------------------------------------------------\n
27599----\n
27600---- "${c}"Left mouse button"$n" on a curve creates a new control point (or moves an existing one).\n
27601---- "${c}"Right mouse button"$n" on a control point deletes it.\n
27602---- "${c}"Left mouse button"$n" on the main image window shows the initial image until button is released.\n
27603---- "${c}"Right mouse button"$n" on the main image window adds a keypoint to all curves from picked color.\n
27604---- Key '"${c}"R"$n"' on a curve resets it.\n
27605---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
27606---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
27607---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
27608---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n
27609----\n
27610------------------------------------------------------------------------------------------------"
27611    __demo_color_curve=1 l[] do rm sp ? while s!=3 endl x_color_curves rgb __demo_color_curve=
27612  else
27613    e[] "\n
27614------ "${g}"Color curves"$n" ----------------------------------------------------------------------------\n
27615----\n
27616---- Only "${c}"one session"$n" allowed at the same time !\n
27617----\n
27618------------------------------------------------------------------------------------------------"
27619  fi
27620
27621#@cli tixy : "expression"
27622#@cli : Animate specified mathematical expression with a 16x16 grid of circles, \
27623# using the rules described at <https://tixy.land>.
27624tixy : skip "${1=sin(t-sqrt((x-8)^2+(y-8)^2))}" check_display $0
27625  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r b=$_vt100_b
27626  if narg("$*") expr="$*" else expr="$1" fi
27627  l[] ('$expr')
27628    replace_str "%","%25"
27629    replace_str "&","%26"
27630    replace_str "(","%28"
27631    replace_str ")","%29"
27632    replace_str "+","%2B"
27633    replace_str ",","%2C"
27634    replace_str ";","%2C"
27635    replace_str "/","%2F"
27636    replace_str "[","%5B"
27637    replace_str "]","%5D"
27638    replace_str "|","%7C"
27639    replace_str "^","**"
27640    replace_str " ","+"
27641    url=https://tixy.land?code={t}
27642  rm endl
27643
27644  e[] "\n
27645------ "${g}"tixy"$n" ------------------------------------------------------------------------------------\n
27646----\n
27647---- tixy - creative code golfing: "${g}"https://tixy.land"$n"\n
27648----\n
27649---- "$r${b}"Expression:"$n" "$expr"\n
27650----\n
27651---- "$r${b}"Corresponding URL:"$n" "$url"\n
27652----\n
27653---- "$r${b}"Rules:"$n"\n
27654---- . Specified expression depends on 4 variables "${g}"(t,i,x,y)"$n" and is rendered as an animation.\n
27655---- . Variables "${g}"x"$n" and "${g}"y"$n", in range "${c}"[0,15]"$n", are the spatial position of each dot.\n
27656---- . Variable "${g}"i"$n", in range "${c}"[0,255]"$n", is the dot index, i.e. "${c}"i = x + 16*y"$n".\n
27657---- . Variable "${g}"t>=0"$n" is the (decimal) number of elapsed seconds.\n
27658---- . Specified expression "${g}"func(t,i,x,y)"$n" must return a complex value "${c}"c"$n".\n
27659---- . Variable "${g}"z"$n" can be used as a shortcut for "${g}"[ x,y ]"$n".\n
27660---- . Variable "${g}"j"$n" is the complex imaginary number "${g}"[ 0,1 ]"$n".\n
27661---- . "${c}"cabs(c)"$n" in "${g}"[0,1]"$n" determines the radius of each dot at "${c}"(x,y)"$n".\n
27662---- . "${c}"carg(c)"$n" in "${g}"[-pi,pi]"$n" determine the color of each dot at "${c}"(x,y)"$n".\n
27663----\n
27664---- Key '"${c}"SPACE"$n"' starts/stops recording animation as video file '"${c}"out.mp4"$n"'.\n
27665---- Key '"${c}"ENTER"$n"' takes a screenshot of current image.\n
27666---- Key '"${c}"A"$n"' enables/disables anti-aliasing.\n
27667---- Key '"${c}"F"$n"' displays formula on the image.\n
27668---- Key '"${c}"R"$n"' resets time.\n
27669---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
27670---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
27671---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
27672---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n
27673------------------------------------------------------------------------------------------------"
27674  w[] 400,400,0,"[G'MIC] tixy: "$expr
27675  t0=$| is_recording=0 is_antialias=1 is_screenshot=0 is_formula=0 nb_frames=0
27676  360,1,1,3,[x,abs(cos(x/w*pi))^0.5,1] hsv2rgb. shift. -180,0,0,0,2 nm. cmap
27677  strvar $expr basename=${}
27678
27679  do
27680
27681    # Manage user-events.
27682    is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
27683    if $is_ctrl" && "{*,-D} w[] {1.25*[{*,w,h}]} rmn formula,mformula
27684    elif $is_ctrl" && "{*,-C} w[] {0.8*[{*,w,h}]} rmn formula,mformula
27685    elif $is_ctrl" && "{*,R} w[] 400,400 rmn formula,mformula
27686    elif {*,-A} is_antialias={!$is_antialias}
27687    elif {*,-F} is_formula={!$is_formula}
27688    elif {*,R} t0=$|
27689    elif {*,-SPACE}
27690      if $is_recording o[] $basename.mp4,15,0,0 nb_frames=0
27691      else t0=$| fi
27692      is_recording={!$is_recording}
27693    elif {*,-ENTER} is_screenshot=1
27694    fi
27695
27696    # Render animation frame.
27697    m={min({*,w,h})} _is_antialias={$is_antialias" && "$m<512}
27698    {m=$m*($_is_antialias?2:1);[m,m]},1,3
27699
27700    16,16,1,1,">begin(
27701        const b = w#-1/16;
27702        const t = "$|-$t0";
27703        j = [ 0,1 ];
27704      );
27705      i = x + 16*y;
27706      ref([ x,y ],z);
27707      val = ("$expr");
27708      n = cabs(val);
27709      r = floor(0.48*b*cut(n,0,1));
27710      a = (360+carg(val)*180/pi)%360;
27711      c = I[#"$cmap",a];
27712      r>1?ellipse(#-1,b*(x + 0.5),b*(y + 0.5),r,r,0,1,c):
27713      r>0?ellipse(#-1,b*(x + 0.5),b*(y + 0.5),1,1,0,0.5,c)"
27714    rm.
27715
27716    if $_is_antialias r. 50%,50%,1,3,2 fi
27717    if {*,w}!={*,h} -. 128 r. {*,w,h},1,3,0,0,0.5,0.5 +. 128 fi
27718    if $is_formula
27719      if !narg($formula)
27720        0 t. {``$expr},0,0,48,1,255 frame. 5,5,0 +dilate. 11 max. 128 to_rgb..
27721        if w>0.75*{*,w} r2dx[-2,-1] {0.75*{*,w}} fi
27722        mv[-2,-1] 0 nm[0,1] formula,mformula
27723      fi
27724      j. [formula],0.5~,1~,0,0,1,[mformula],255
27725    fi
27726    if $is_screenshot o. $basename.png is_screenshot=0 fi
27727    if $is_recording o. $basename.mp4,15,0,1 nb_frames+=1 to. "Recording video\n[Frame \#"$nb_frames"]",0,0,5% fi
27728    w. rm. wait 40
27729  while {*}" && "!{*,ESC}" && "!{*,Q}
27730  rmn cmap,formula,mformula
27731  w[] 0
27732
27733#@cli x_2048
27734#@cli : Launch the 2048 game.
27735x_2048 : check_display $0
27736  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
27737  e[] "\n
27738------ "${g}"2048"$n" -----------------------------------------------\n
27739----\n
27740---- Join the numbers and get to the "${g}"2048"$n" tile!\n
27741----\n
27742---- Use your "${c}"arrow keys"$n" to move the tiles. When two tiles\n
27743---- with the same number touch, they merge into one!\n
27744---- This command is a port of the '"${c}"2048"$n"' game originally\n
27745---- designed by "${c}"Gabriele Cirulli"$n", and available at:\n
27746---- "${g}"http://gabrielecirulli.github.io/2048/"$n"\n
27747----\n
27748---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
27749----\n
27750--------------------------------------------------------------"
27751  l[]
27752  score=0 f3d 50 m3d 0
27753  m "_x_2048_setrandom : +==[0] 0 f. 'if(i,4*y+x,-1)' discard. -1
27754     off={i[round(u(h-1))]} rm. x={$off&3} y={$off>>2} n={if(u<0.75,1,2)}
27755     =[0] $n,$x,$y [{2+$n}] c3d.
27756     repeat 6 j3d[1] .,{78+$x*121},{190+$y*121},{10*$<},{(1+$>)/6} w[1] wait 20 done
27757     rm."
27758  m "_x_2048_object3d : +f[0] 'if(i,i*16+4*y+x,-1)' discard. -1
27759     N={h} repeat h v={-{1+$>},@$>} ++3d[{2+($v>>4)}] {$v&3},{($v>>2)&3} done
27760     +3d[-$N--1] rm.."
27761  i[0] 4,4
27762
27763  # Pre-render game canvas and numbered titles.
27764  b0=204,192,179 b1=238,228,218 b2=237,224,200 b3=242,177,121
27765  b4=245,149,99 b5=246,124,95 b6=246,94,59 b7=237,207,114
27766  c0=119,110,101 c1=249,246,242 s0=" "
27767  520,630,1,3 fc. 250,248,239
27768  t. "2048",20,10,86,1,$c0
27769  t. "Join the numbers and get to the 2048 tile!",20,90,20,1,$c0
27770  rectangle. 422,20,501,75,1,187,173,160 t. "SCORE",439,25,15,1,238,228,218
27771  repeat 12
27772    107,107,1,3 fc. ${b{min($>,7)}} +fc. ${c{$>>2}}
27773    s1={2^$>} 0 t. ${s{$>>0}},0,0,52,1,1
27774    r. ..,..,1,1,0,0,0.5,0.5 dilate_circ. 3 b. 0.5
27775    j... ..,0,0,0,0,1,.,1 rm[-2,-1]
27776  done
27777  frame_round[2--1] 10,5,1,0.5,187,173,160 frame[2--1] 7,7,187,173,160 to_rgb[2--1]
27778  r[2] 400%,400%,1,3,0,2 j[1] [2],18,130
27779  sprite3d[3--1]
27780
27781  # Run game.
27782  w[1] 100%,100%,0,"[G"{`39`}"MIC] 2048" insert_new=1
27783  repeat 2 _x_2048_setrandom done
27784  do
27785
27786    # Render game graphics at current iteration.
27787    if $insert_new
27788      _x_2048_object3d *3d. 121 j[1] [2],18,130 j3d[1] .,78,190 rm.
27789      80,25,1,3 fc. $c1 0 t. $score,0,0,25,1,1,1,1 ri. ..,0,0,0.5,0.8
27790      rectangle[1] 422,45,501,69,1,187,173,160 j[1] ..,422,45,0,0,1,. rm[-2,-1]
27791      w[1] insert_new=0
27792    fi
27793
27794    # Check for the end of the game.
27795    ++[0] 1 f. 'j(-1)==i||j(1)==i||j(0,-1)==i||j(1,0)==i||i==1'
27796    if {0,iM==11} # Game won.
27797      alert "Game Over","\nCongratulations! You got the 2048 title!\n\n   Your score: "$score,"OK"
27798      break
27799    elif !iM # Game lost.
27800      alert "Game Over","\nBad luck! You lost the game!\n\n   Your score: "$score,"OK"
27801      break
27802    fi
27803    rm.
27804
27805    # Manage user events.
27806    wait
27807    is_shift=0 um shift2048,ishift2048,vshift2048
27808    if {*,ARROWLEFT}
27809      m "shift2048:" m "ishift2048:" m "vshift2048:"
27810      is_shift=1
27811    elif {*,ARROWRIGHT}
27812      m "shift2048: rotate 180" m "ishift2048: rotate 180"
27813      m "vshift2048: s3d l[2] r 3,{h/3},1,1,-1 s x -[0,1] 3 *[0,1] -1 a x y endl a y"
27814      is_shift=1
27815    elif {*,ARROWUP}
27816      m "shift2048: rotate -90" m "ishift2048: rotate 90"
27817      m "vshift2048: s3d l[2] r 3,{h/3},1,1,-1 s x rv[0,1] -[0] 3 *[0] -1  a x y endl a y"
27818      is_shift=1
27819    elif {*,ARROWDOWN}
27820      m "shift2048: rotate 90" m "ishift2048: rotate -90"
27821      m "vshift2048: s3d l[2] r 3,{h/3},1,1,-1 s x rv[0,1] -[1] 3 *[1] -1  a x y endl a y"
27822      is_shift=1
27823    fi
27824    if {*,r} w[1] 100%,100% fi
27825
27826    # Manage tile shifts and fusions.
27827    if $is_shift
27828      wait -1
27829      shift2048[0]
27830      repeat 2
27831
27832        # Tile shifts.
27833        _x_2048_object3d
27834        +s[0] y discard[-4--1] 0 y[-4--1] x r[-4--1] 4,1,1,1,0,0 a[-4--1] y
27835        +==[0,-1] insert_new={$insert_new||!im} rm.
27836        +f[0,-1] 'if(i,x,-1)' discard[-2,-1] -1 rv[-2,-1] -[-2,-1] rv[0,-2] rm..
27837
27838        if (im||iM)&&!{*,k} # Render animation for shift.
27839          /. 5 z. 0,2 y.
27840          repeat 5
27841            j.. .,0,8,0,0,-1
27842            j[1] [2],18,130 +vshift2048.. *3d. 121 j3d[1] .,78,190 rm.
27843            w[1] wait 20
27844          done
27845        fi
27846        rm[-2,-1]
27847
27848        # Tile fusions.
27849        if !$> dscore=0
27850          [0] +f[0] 'if(i,i*16+4*y+x,-1)' discard. -1
27851          repeat h
27852            x={i[$>]&3} y={(i[$>]>>2)&3} n={i[$>]>>4}
27853            if $x>0" && "{0,i($x-1,$y)}==$n
27854              =[0] 0,$x,$y =[0] {$n+1},{$x-1},$y =.. 0,$x,$y insert_new=1 dscore+={2^($n+1)}
27855            else =. -1,0,$>
27856            fi
27857          done
27858          score+=$dscore
27859
27860          if iM<0 rm[-2,-1]
27861          else # At least one tile fusions.
27862            discard. -1
27863            rv[0,-2] _x_2048_object3d rv[0,-3] vshift2048. *3d. 121 # Only tiles that do not move in this step.
27864            j[1] [2],18,130 j3d[1] .,78,190 rm[-3,-1]
27865            N={h} repeat h v={-{1+$>},@$>} ++3d[{2+($v>>4)}] {$v&3},{($v>>2)&3} done # Only tiles that move.
27866            +3d[-$N--1] rm..
27867            0 t. +$dscore,0,0,33,1,1 100%,100%,1,3 fc. $c0
27868            repeat 6  # Render animation for fusion.
27869              +vshift2048... *3d. 121 +j3d[1] .,78,190
27870              j. ...,440,{40-3*$>},0,0,{min(1,$</3)},[-4]
27871              w. rm[-2,-1] -3d... {1/6},0,0 wait 20
27872            done
27873            rm[-3--1]
27874          fi
27875
27876        fi
27877      done
27878      ishift2048[0]
27879
27880      # Insert new tile.
27881      if $insert_new _x_2048_setrandom fi
27882    fi
27883
27884  while {*}" && "!{*,ESC}" && "!{*,Q}
27885  rm w 0 endl um _x_2048_setrandom,_x_2048_object3d
27886
27887#@cli x_blobs
27888#@cli : Launch the blobs editor.
27889#@cli : ![x_blobs](../img/x_blobs.jpg)
27890x_blobs : check_display $0
27891  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
27892  e[] "\n
27893------ "${g}"Blobs editor"$n" --------------------------\n
27894----\n
27895---- "${c}"Mouse"$n" to insert/move/delete blobs.\n
27896---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
27897---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
27898---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
27899----\n
27900-----------------------------------------------------"
27901
27902  # Create background image [0].
27903  l[] (0;0^0;128^0;255) r. 450,450,1,3,3 flower. 30,8,0,0,50%,50%,1 water. 20
27904  w {f=h<0.5*{*,v}?1.5:1;[w,h]*=f},0,0,"[G"{`39`}"MIC] Blobs Editor"
27905
27906  # Start event loop.
27907  moving=-1
27908  do
27909    x={{*,x}*{0,w}/{*,w}}
27910    y={{*,y}*{0,h}/{*,h}}
27911    b={*,b} nearest=-1
27912    fps=${-fps}
27913
27914    # Render image of blobs and find nearest blob to mouse pointer.
27915    if $!>1
27916      {0,[w,h]},1,2
27917      repeat h#1
27918        r={1,i[2]*(1+i[3]*cos(i[4]+i[5]*$|*1000))}
27919        ellipse. {1,@0,1},$r,$r,0,1,{1,@6-7}
27920        d={sqrt(($x-{1,@0})^2+($y-{1,@1})^2)}
27921        if $d<$r nearest=$> fi
27922      shift[1] 0,-1,0,0,2 done
27923      b. 15
27924      +norm. +>=. 50 <=.. 40 *[-3,-1]
27925      +*[0,-1] rm.. rv[-2,-1] *. 1.6 c. 0,255 +[-2,-1]
27926      if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi
27927      w.
27928      if {*,CTRLLEFT}" && "{*,D} w[] {2*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi
27929      rm.
27930    else
27931      +to[0] "G\47MIC Blobs Editor",75,100,35,3,1,200,128,255
27932      to. "* Left mouse button : Create and move blobs.\n\n\
27933           * Right mouse button : Remove blob.\n\n\
27934           * Middle mouse button : Remove all blobs.\n\n\
27935           * Key 'ESC' or 'Q' : Quit.\n\n\
27936           * Colors and sizes of appearing blobs are\n   chosen randomly",\
27937           50,180,18,1,1,255
27938      w.
27939      if {*,CTRLLEFT}" && "{*,D} w[] {1.5*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi
27940      rm.
27941    fi
27942    wait 20
27943
27944    # Manage blob insertion, removal or move.
27945    if $x<0||$y<0 continue fi
27946    if $b&1
27947      if $nearest>=0" || "$moving>=0 # Move existing blob.
27948        if $moving<0 moving=$nearest fi
27949        =[1] $x,0,$moving =[1] $y,1,$moving
27950        else # Insert new blob.
27951          ($x,$y,{u(20,50)},{u(-0.3,0.3)},{u(0,pi/2)},{u(0,0.009)},{u(64,255)},{u(64,255)}) a[^0] y
27952          moving={h-1}
27953        fi
27954    elif $b&2 # Remove existing blob.
27955      if $nearest>=0 l[1] s y rm[$nearest] a y endl nearest=-1 fi
27956    elif $b&4 # Remove all blobs.
27957      k[0]
27958    else
27959      moving=-1
27960    fi
27961
27962  while {*}" && "!{*,ESC}" && "!{*,Q}
27963  rm w 0 endl
27964
27965#@cli x_bouncing
27966#@cli : Launch the bouncing balls demo.
27967x_bouncing : check_display $0
27968  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
27969  e[] "\n
27970------ "${g}"Bouncing balls"$n" ------------------------------\n
27971----\n
27972---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
27973---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
27974---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
27975----\n
27976-----------------------------------------------------"
27977  l[]
27978  520,320,1,3 plasma 1,1,9 n 0,220
27979  N=12
27980  repeat $N
27981    ball[] {round(u(32,80))},${-rgb}
27982    t$>={u(200)} x$>={0,u(10,w-10)} h$>={u(150,300)} vx$>={if(u<0.5,1,-1)*u(1,8)}
27983  done
27984  mv[0] $!
27985  w. {f=w<0.5*{*,u}?1.5:1;[w,h]*=f},0,"[G"{`39`}"MIC] Bouncing Balls"
27986  (0;0.7;1) r. {-2,w},70,1,1,3
27987
27988  do
27989    [$N]
27990    repeat $N
27991      bw={$>,w} bh={$>,h}
27992      y={${h$>}*abs(cos(${t$>}*pi/60))-$bh/2}
27993      dt=1
27994      if $y<0 d={-$y} y=0 bh-=$d bw+=$d dt={max(0.2,1-($d/$bh)^2)} else dt=1 fi
27995      if ${x$>}+$bw/2>w
27996        d={${x$>}+$bw/2-w} bw-=$d bh+={0.5*$d}
27997        if ${x$>}+$bw/4>w vx$>={-${vx$>}} fi
27998      fi
27999      if ${x$>}-$bw/2<0
28000        d={$bw/2-${x$>}} bw-=$d bh+={0.5*$d}
28001        if ${x$>}-$bw/4<0 vx$>={-${vx$>}} fi
28002      fi
28003      +r[$>] $bw,$bh,1,4,3 s. c,-3
28004      j... ..,{max(0,min({$N,w-$bw},${x$>}-$bw/2))},{{$N,h}-{h}-$y-70},0,0,1,.,255 rm[-2,-1]
28005      t$>+=$dt
28006      x$>+={$dt*${vx$>}}
28007    done
28008
28009    +rows. {h-2*70},{h-1-70} mirror. y *. [{$N+1}]
28010    j.. .,0,{-2,h-71},0,0,0.5 rm.
28011    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi
28012    if {*,CTRLLEFT}" && "{*,D} w[] {1.5*w},{1.5*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
28013    w. rm. wait 20
28014  while {*}" && "!{*,ESC}" && "!{*,Q}
28015  w 0 rm endl
28016
28017#@cli x_color_curves : _colorspace={ rgb | cmy | cmyk | hsi | hsl | hsv | lab | lch | ycbcr | last }
28018#@cli : Apply color curves on selected RGB[A] images, using an interactive window.
28019#@cli : Set 'colorspace' to 'last' to apply last defined color curves without opening interactive windows.
28020#@cli : Default value: 'colorspace=rgb'.
28021x_color_curves : skip ${1=rgb}
28022  if ['"$1"']!='last'&&!{*,u} error[0--3] "Command '$0': No display available." return fi
28023  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
28024  e[^-1] "Apply color curves of image$?, in the '$1' colorspace."
28025  if ['"$1"']=='last'
28026    if !narg($_xcc_colorbase) return fi
28027    __x_color_curves[] $_xcc_colorbase
28028  else
28029    e[] "\n
28030------------------------------------------------------------------------------------------------\n
28031----\n
28032---- "${c}"Left mouse button"$n" on a curve creates a new control point (or moves an existing one).\n
28033---- "${c}"Right mouse button"$n" on a control point deletes it.\n
28034---- "${c}"Left mouse button"$n" on the main image window shows the initial image until button is released.\n
28035---- "${c}"Right mouse button"$n" on the main image window adds a keypoint to all curves from picked color.\n
28036---- Key '"${c}"R"$n"' on a curve resets it.\n
28037---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
28038---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
28039---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
28040---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n
28041----\n
28042------------------------------------------------------------------------------------------------"
28043    __x_color_curves[] $1 _xcc_colorbase=$1
28044  fi
28045
28046  to_color repeat $! l[$>]
28047
28048    if ['"$1"']!='last' # Open interactive windows to set color curves.
28049      +r[0] ${fitscreen[]\ {0,[w,h,1]},128,70%},1,100%,3
28050      +l. # Compute additional info for each image channel (histogram and color axis).
28051        xcc_goto s c histogram 256,0,255 xcc_info
28052      endl
28053      __C0= __C1= __C2= __C3= __C4=
28054      if narg($__xcc_C0) __C0=$__xcc_C0 fi
28055      if narg($__xcc_C1) __C1=$__xcc_C1 fi
28056      if narg($__xcc_C2) __C2=$__xcc_C2 fi
28057      if narg($__xcc_C3) __C3=$__xcc_C3 fi
28058      if narg($__xcc_C4) __C4=$__xcc_C4 fi
28059
28060      x={1,({*,u}-560-w)/2} y={1,({*,v}-h)/2}
28061      if $!==5 # 3 channels.
28062        parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d... __C0,"$_color0"",\
28063                 "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d.. __C1,"$_color1"",\
28064                 "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d. __C2,"$_color2"",\
28065                 "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-4]"
28066      elif $!==6 # 4 channels.
28067        parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d[-4] __C0,"$_color0"",\
28068                 "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d... __C1,"$_color1"",\
28069                 "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d.. __C2,"$_color2"",\
28070                 "w[] 256,256,0,0,"{$x+280}","{$y+300}",\"Curve: "$_title3"\" x_select_function1d. __C3,"$_color3"",\
28071                 "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-5]"
28072      elif $!==7 # 5 channels.
28073        parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d[-5] __C0,"$_color0"",\
28074                 "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d[-4] __C1,"$_color1"",\
28075                 "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d... __C2,"$_color2"",\
28076                 "w[] 256,256,0,0,"{$x+280}","{$y+300}",\"Curve: "$_title3"\" x_select_function1d.. __C3,"$_color3"",\
28077                 "w[] 256,256,0,0,"{$x+280}","{$y+600}",\"Curve: "$_title4"\" x_select_function1d. __C4,"$_color4"",\
28078                 "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-6]"
28079      fi
28080      k[0]
28081    fi
28082
28083    # Apply color curves on fullres image.
28084    xcc_goto
28085    repeat s function1d[] 1,${__xcc_C$>} *. {255%} r. 256,1,1,1,5 c. 0,255 sh[0] $> map. .. rm[-2,-1] done
28086    xcc_backto
28087
28088  endl done
28089  um xcc_goto,xcc_backto,xcc_info
28090
28091_x_color_curves :
28092  title={0,b} if narg({'{0,x}'}) title=$title.{0,x} fi ('$title') discard. {'~'} title={t} rm.
28093  +drgba. w. 100%,100%,0,"[G"{`39`}"MIC] Image: "$title rm.
28094  xcc_goto. .
28095
28096  oC0= oC1= oC2= oC3= oC4= viewmode=0
28097  do
28098    wait 100 need_refresh=0
28099
28100    # Manage user events.
28101    oviewmode=$viewmode is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}} x={*,x} y={*,y}
28102    if {*,r} need_refresh=1 # Window resize.
28103    elif $is_ctrl" && "{*,-D} w[] {{*,w}*125%},{{*,h}*125%} need_refresh=1 # Increase window size.
28104    elif $is_ctrl" && "{*,-C} w[] {{*,w}*80%},{{*,h}*80%} need_refresh=1 # Decrease window size.
28105    elif $is_ctrl" && "{*,-R} w[] {w},{h} need_refresh=1 # Reset window size.
28106    elif {*,b}&1 viewmode={x={*,x};if(x<w/3,1,if(x<2*w/3,2,3))} # Change viewmode.
28107    elif {*,b}&2" && "$x>=0" && "$y>=0 # Add control point from picked color.
28108      xc={$x*w/{*,w}} yc={$y*h/{*,h}} +z[0] $xc,$yc,$xc,$yc
28109      repeat s (${__C$>},{i[$>]/255%},{i[$>]/255%}) r. 2,{w/2},1,1,-1 sort. +,y __C$>={^} rm. done
28110      rm. wait -1
28111    else viewmode=0
28112    fi
28113    need_refresh={$need_refresh||$oviewmode!=$viewmode}
28114
28115    # Update result.
28116    repeat s if ['_${oC$>}']!=['_${__C$>}'] # Channel must be updated.
28117      function1d[] 1,${__C$>} *. {255%} r. 256,1,1,1,5 c. 0,255
28118      +channels[0] $> map. .. j[1] .,0,0,0,$> rm[-2,-1]
28119      need_refresh=1 oC$>=${__C$>}
28120    fi done
28121
28122    # Display view.
28123    if $need_refresh
28124      if $viewmode==0 # Modified view.
28125        +xcc_backto[1]
28126      elif $viewmode%2 # Split view.
28127        w2={0,int(w/2)} b={$viewmode==1} +z[{!$b}] 0,{$w2-1} +z[$b] $w2,100%
28128        xcc_backto.. xcc_backto. a[-2,-1] x line. 50%,0,50%,100%,1,0
28129      else # Original view.
28130        +xcc_backto[0]
28131      fi
28132      if s>3 drgba. fi w. rm. refresh=0
28133    fi
28134
28135  while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,SPACE}" && "!{*,ENTER}
28136  w 0
28137
28138  # Transfer curves to output variable and request curve widgets to close.
28139  repeat 5 if narg(${__C$>}) __xcc_C$>=${__C$>} __C$>=-1 fi done
28140
28141# Define colorspace conversion functions.
28142__x_color_curves :
28143  if ['"$1"']=='rgb'
28144    _color0="255,180,180" _color1="180,255,180" _color2="180,180,255" _color3="220,220,220"
28145    _title0=Red _title1=Green _title2=Blue _title3=Alpha
28146    m "xcc_goto:" m "xcc_backto:"
28147    m "xcc_info: (0,255;0,0;0,0) (0,0;0,255;0,0) (0,0;0,0;0,255) r[-3--1] 256,3,1,1,3
28148       a[0,-3] y a[1,-2] y a[2,-1] y"
28149  elif ['"$1"']=='cmy'
28150    _color0="180,255,255" _color1="255,180,255" _color2="255,255,100" _color3="220,220,220"
28151    _title0=Cyan _title1=Magenta _title2=Yellow _title3=Alpha
28152    m "xcc_goto: s c,-3 rgb2cmy[0] a c" m "xcc_backto: s c,-3 cmy2rgb[0] a c"
28153    m "xcc_info: (255,0;255,255;255,255) (255,255;255,0;255,255) (255,255;255,255;255,0) r[-3--1] 256,3,1,1,3
28154       a[0,-3] y a[1,-2] y a[2,-1] y"
28155  elif ['"$1"']=='cmyk'
28156    _color0="180,255,255" _color1="255,180,255" _color2="255,255,100" _color3="180,180,180" _color4="220,220,220"
28157    _title0=Cyan _title1=Magenta _title2=Yellow _title3=Key _title4=Alpha
28158    m "xcc_goto: s c,-3 rgb2cmyk[0] a c" m "xcc_backto: s c,-4 cmyk2rgb[0] a c"
28159    m "xcc_info: (255,0;255,255;255,255) (255,255;255,0;255,255) (255,255;255,255;255,0) (255,0) r[-4--1] 256,3,1,1,3
28160       a[0,-4] y a[1,-3] y a[2,-2] y a[3,-1] y"
28161  elif ['"$1"']=='hsi'
28162    _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220"
28163    _title0=Hue _title1=Saturation _title2=Intensity _title3=Alpha
28164    m "xcc_goto: s c,-3 rgb2hsi8[0] a c" m "xcc_backto: s c,-3 hsi82rgb[0] a c"
28165    m "xcc_info: 256,1,1,3,if(!c,x,128) 256,1,1,3,if(!c,0,if(c==1,x,128)) 256,1,1,3,if(!c,0,if(c==1,0,x))
28166       hsi82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
28167  elif ['"$1"']=='hsl'
28168    _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220"
28169    _title0=Hue _title1=Saturation _title2=Lightness _title3=Alpha
28170    m "xcc_goto: s c,-3 rgb2hsl8[0] a c" m "xcc_backto: s c,-3 hsl82rgb[0] a c"
28171    m "xcc_info: 256,1,1,3,if(!c,x,128) 256,1,1,3,if(!c,0,if(c==1,x,128)) 256,1,1,3,if(!c,0,if(c==1,0,x))
28172       hsl82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
28173  elif ['"$1"']=='hsv'
28174    _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220"
28175    _title0=Hue _title1=Saturation _title2=Value _title3=Alpha
28176    m "xcc_goto: s c,-3 rgb2hsv8[0] a c" m "xcc_backto: s c,-3 hsv82rgb[0] a c"
28177    m "xcc_info: 256,1,1,3,if(!c,x,255) 256,1,1,3,if(!c,0,if(c==1,x,128)) 256,1,1,3,if(!c,0,if(c==1,0,x))
28178       hsv82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
28179  elif ['"$1"']=='lab'
28180    _color0="180,180,180" _color1="220,180,220" _color2="220,220,180" _color3="220,220,220"
28181    _title0=Lightness _title1=Chroma-A _title2=Chroma-B _title3=Alpha
28182    m "xcc_goto: s c,-3 srgb2rgb[0] apo[0] rgb2lab8,0,4 a c" m "xcc_backto: s c,-3 apo[0] lab82rgb,0,4 rgb2srgb[0] a c"
28183    m "xcc_info: 256,1,1,3,if(!c,x,128) 256,1,1,3,if(!c,240,if(c==1,x,128)) 256,1,1,3,if(!c,240,if(c==1,128,x))
28184       lab82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
28185  elif ['"$1"']=='lch'
28186    _color0="180,180,180" _color1="220,180,220" _color2="255,220,220" _color3="220,220,220"
28187    _title0=Lightness _title1=Chroma _title2=Hue _title3=Alpha
28188    m "xcc_goto: s c,-3 srgb2rgb[0] apo[0] rgb2lch8[0],0,4 a c" m "xcc_backto: s c,-3 apo[0] lch82rgb[0],0,4
28189       rgb2srgb[0] a c"
28190    m "xcc_info: 256,1,1,3,if(!c,x,0) 256,1,1,3,if(!c,255,if(c==1,x,128)) 256,1,1,3,if(!c,220,if(c==1,128,x))
28191       lch82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
28192  elif ['"$1"']=='ycbcr'
28193    _color0="180,180,180" _color1="220,220,255" _color2="255,220,220" _color3="220,220,220"
28194    _title0=Luma _title1=Blue\ chroma _title2=Red\ chroma _title3=Alpha
28195    m "xcc_goto: s c,-3 rgb2ycbcr[0] a c" m "xcc_backto: s c,-3 ycbcr2rgb[0] a c"
28196    m "xcc_info: 256,1,1,3,if(!c,x,128) 256,1,1,3,if(!c,128,if(c==1,x,128)) 256,1,1,3,if(!c,128,if(c==1,128,x))
28197       ycbcr2rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
28198  else error[0--3] "Command 'x_color_curves': Unknown specified color space '$1'."
28199  fi
28200
28201#@cli x_colorize : _is_lineart={ 0 | 1 },_max_resolution={ 0 | >=128 },_multichannels_output={ 0 | 1 },\
28202# _[palette1],_[palette2],_[grabber1]
28203#@cli : Colorized selected B&W images, using an interactive window.
28204#@cli : When >0, argument 'max_resolution' defines the maximal image resolution used in the interactive window.
28205#@cli : Default values: 'is_lineart=1', 'max_resolution=1024' and 'multichannels_output=0'.
28206x_colorize : skip ${1=0},${3=0},${4=0},${5=0},${6=0} check "${2=1024}==0 || $2>=128" check_display $0
28207  s0="image" s1="lineart" s2="multichannel" s3="merged" use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
28208  e[^-1] "Colorize selected B&W "${s{!$1}}"$? interactively, with maximum resolution $2 and "${s{2+!$3}}" output."
28209  e[] "\n
28210--------------------------------------------------------------------------------------\n
28211----\n
28212---- "${c}"Left mouse button"$n" creates a new colored control point (or moves an existing one).\n
28213---- "${c}"Right mouse button"$n" or key '"${c}"X"$n"' over a control point deletes it.\n
28214---- "${c}"Right mouse button"$n" or key '"${c}"P"$n"' anywhere else picks a color from the image.\n
28215---- "${c}"Mouse wheel"$n", or keys '"${c}"CTRL+arrows UP/DOWN"$n"' zoom view in/out.\n
28216---- '"${c}"CTRL+mouse wheel"$n"', '"${c}"SHIFT+mouse wheel"$n"' or "${c}"arrow keys"$n" move image in zoomed view.\n
28217---- Key '"${c}"SPACE"$n"' updates the extrapolated color field.\n
28218---- Key '"${c}"TAB"$n"' toggles between markers view modes.\n
28219---- Key '"${c}"BACKSPACE"$n"' deletes the last control point added.\n
28220---- Key '"${c}"PAGE UP"$n"' increases image contrast.\n
28221---- Key '"${c}"PAGE DOWN"$n"' decreases image contrast.\n
28222---- Key '"${c}"R"$n"' toggles color replace mode.\n
28223---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
28224---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
28225---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
28226---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n
28227----\n
28228--------------------------------------------------------------------------------------"
28229  N=$!
28230  thread_main="_x_colorize[0] ${1--1}"
28231  thread_color="w[] 400,320,0,\"Palette: main\" x_select_color[] __color,255,255,255"
28232
28233  is_palette1=${"is_image_arg[] $4"}
28234  if $is_palette1
28235    pass$4 1 ('{b}') discard. {'~'} palette_title1={t} rm.
28236    thread_palette1="w[] 400,400,0,\"Palette: "$palette_title1"\" x_select_palette["{$!-1}"] __color"
28237  fi
28238
28239  is_palette2=${"is_image_arg[] $5"}
28240  if $is_palette2
28241    pass$5 1 ('{b}') discard. {'~'} palette_title2={t} rm.
28242    thread_palette2="w[] 400,400,0,\"Palette: "$palette_title2"\" x_select_palette["{$!-1}"] __color"
28243  fi
28244
28245  is_grabber=${"is_image_arg[] $6"}
28246  if $is_grabber
28247    pass$6 1 ('{b}') discard. {'~'} palette_grabber={t} rm.
28248    thread_grabber="w[] ${\"fitscreen[] {[w,h,1]},128,50%\"},0,\"Grabber: "$palette_grabber"\"
28249                    x_grab_color["{$!-1}"] __color"
28250  fi
28251
28252  __color=255,255,255
28253
28254  if !$is_palette1" && "!$is_palette2" && "!$is_grabber
28255    repeat $N l[$>]
28256      parallel $thread_main,$thread_color
28257    endl done
28258  else
28259    repeat $N l[$>,$N--1]
28260      parallel $thread_main,$thread_color,$thread_palette1,$thread_palette2,$thread_grabber
28261    endl done
28262  fi
28263  k[0-{$N-1}]
28264
28265_x_colorize :
28266
28267  # Init variables and images.
28268  name={n} title={b} if narg({x}) title=$title.{x} fi
28269  w={w} h={h}
28270
28271  if $1 # Line-art.
28272    if s==4 sh. 3 if abs(im-iM)>64 +*. -1 rm.. +. 255 else rm. sh. 0 fi
28273    else sh. 0
28274    fi
28275    n 0,255
28276    else # Regular image.
28277    if s==1 sh. 0
28278    else +luminance.
28279    fi
28280  fi
28281
28282  nm. img
28283
28284  fdim=${fitscreen[]\ $w,$h} ww={arg(1,$fdim)} wh={arg(2,$fdim)} x0=0 y0=0 x1={w-1} y1={h-1}
28285  selection=-1 view_markers=2 contrast=9 xpan=-1 ypan=-1 replace_color= current_replace_color=
28286
28287  if narg($_gui_control_points)>=6 # Import list of control points from plug-in GUI.
28288    ($_gui_control_points) r. {w/6},6,1,1,-1
28289  else 0 # Empty list of control points.
28290  fi
28291  nm. points
28292
28293  # Compute potential map.
28294  if $2>0 if $w>$h +r2dx[img] {min($2,$w)},2 else +r2dy[img] {min($2,$h)},2 fi else [img] fi
28295  __x_colorize. $1
28296  pw={potential,w} ph={potential,h}
28297
28298  # Start event loop.
28299  do
28300
28301    # Handle user events for zoom/navigation/resizing.
28302    if narg($replace_color)" && "{*,x}<0" && "{*,y}<0 wait 200 else wait fi
28303    x={*,x} y={*,y} b={*,b} o={*,-o}
28304    is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
28305    is_shift={{*,SHIFTLEFT}" || "{*,SHIFTRIGHT}}
28306    is_mouseout={$x<0" || "$y<0}
28307    x={$x0+$x*($x1-$x0+1)/$ww} y={$y0+$y*($y1-$y0+1)/$wh}
28308    oww=$ww owh=$wh ox0=$x0 oy0=$y0 ox1=$x1 oy1=$y1
28309
28310    if {*,r} # When window resized.
28311      nww={*,d} nwh={*,e} m={min($nww,$nwh)}
28312      cx={($x0+$x1)/2} cy={($y0+$y1)/2} dx={$nww*($x1-$x0+1)/$ww} dy={$nwh*($y1-$y0+1)/$wh}
28313      x0={$cx-$dx/2} x1={$cx+$dx/2}
28314      y0={$cy-$dy/2} y1={$cy+$dy/2}
28315      ww=$nww wh=$nwh
28316    elif $is_ctrl" && "{*,-D} # Increase window size.
28317      nww={min({*,u},$ww*1.25)} nwh={min({*,v},$wh*1.25)} m={min($nww,$nwh)}
28318      if $m==$nww ww=$m wh={$h*$m/$w} else ww={$w*$m/$h} wh=$m fi
28319    elif $is_ctrl" && "{*,-C} # Decrease window size.
28320      nww={$ww/1.25} nwh={$wh/1.25}
28321      if min($nww,$nwh)>=64 ww=$nww wh=$nwh fi
28322    elif $is_ctrl" && "{*,R} # Reset window size.
28323      fdim=${fitscreen[]\ $w,$h} ww={arg(1,$fdim)} wh={arg(2,$fdim)}
28324      x0=0 y0=0 x1={$w-1} y1={$h-1}
28325    elif ($is_shift" && "$o<0)" || "{*,ARROWLEFT} # Go left.
28326      dx={($x1-$x0)/6} x0-=$dx x1-=$dx
28327    elif ($is_shift" && "$o>0)" || "{*,ARROWRIGHT} # Go right.
28328      dx={($x1-$x0)/6} x0+=$dx x1+=$dx
28329    elif ($is_ctrl" && "$o>0)" || "({*,ARROWUP}" && "!$is_ctrl) # Go up.
28330      dy={($y1-$y0)/6} y0-=$dy y1-=$dy
28331    elif ($is_ctrl" && "$o<0)" || "({*,ARROWDOWN}" && "!$is_ctrl) # Go down.
28332      dy={($y1-$y0)/6} y0+=$dy y1+=$dy
28333    elif $o>0" || "($is_ctrl" && "{*,ARROWUP}) # Zoom in.
28334      if $x1-$x0>16" && "$y1-$y0>16
28335        cx={if($x>=0" && "!{*,ARROWUP},$x,($x0+$x1)/2)}
28336        cy={if($y>=0" && "!{*,ARROWUP},$y,($y0+$y1)/2)}
28337        x0={$cx+($x0-$cx)*0.75} y0={$cy+($y0-$cy)*0.75}
28338        x1={$cx+($x1-$cx)*0.75} y1={$cy+($y1-$cy)*0.75}
28339      fi
28340    elif $o<0" || "($is_ctrl" && "{*,ARROWDOWN}) # Zoom out.
28341      zfactor={max(($x1-$x0+1)/$w,($y1-$y0+1)/$h)}
28342      if $zfactor<1.3
28343        cx={if($x>=0" && "!{*,ARROWDOWN},$x,($x0+$x1)/2)}
28344        cy={if($y>=0" && "!{*,ARROWDOWN},$y,($y0+$y1)/2)}
28345        x0={$cx+($x0-$cx)/0.75} y0={$cy+($y0-$cy)/0.75}
28346        x1={$cx+($x1-$cx)/0.75} y1={$cy+($y1-$cy)/0.75}
28347        dx={$zfactor^2*($w-$x0-$x1)/2} dy={$zfactor^2*($h-$y0-$y1)/2}
28348        x0+=$dx x1+=$dx y0+=$dy y1+=$dy
28349      else
28350        dx={($w-$x0-$x1)/2} dy={($h-$y0-$y1)/2}
28351        x0+=$dx x1+=$dx y0+=$dy y1+=$dy
28352      fi
28353    elif $b&4" && "!$is_mouseout # Pan.
28354      if $panx<0" && "$pany<0 panx=$x pany=$y
28355      else dx={round($panx-$x)} dy={round($pany-$y)} x0+=$dx y0+=$dy x1+=$dx y1+=$dy
28356      fi
28357    else panx=-1 pany=-1
28358    fi
28359    if $ww!=$oww" || "$wh!=$owh" || "$ox0!=$x0" || "$oy0!=$y0" || "$ox1!=$x1" || "$oy1!=$y1 rm[baseview] fi
28360
28361    # Handle events related to control points management.
28362    N={points,w}
28363    if narg($baseview)" && "($b&3" || "{*,X}" || "{*,P})" && "$x>=0" && "$y>=0" && "$x<$w" && "$y<$h
28364      if $selection==-1" && "$N # Check for selection of an existing point.
28365        ($x;$y) r. $N,2 -. [points] *. {max($ww,$wh)/max($x1-$x0,$y1-$y0)} sqr. s. y +[-2,-1]
28366        dmin={im} selection={if($dmin>25,-1,xm)} rm.
28367      fi
28368      if narg($replace_color) # Go back from 'Replace color' mode.
28369        replace_color= wait -1
28370      elif $selection>=0
28371        if $b&1" && "$view_markers # Move existing point.
28372          +columns[points] $selection ox={i[0]} oy={i[1]} =. $x =. $y,0,1 j[points] .,$selection rm.
28373          rm[view]
28374        elif ($b&2" || "{*,X})" && "$view_markers # Remove existing point.
28375          if $N>1 +z[points] {$selection+1},100% j[points] .,$selection rm. r[points] {$N-1},100%,1,1,0
28376          else rm[points] 0 nm. points fi
28377          wait -1 rm[view]
28378        fi
28379      elif $b&1 # Add new point
28380       ($x;$y;0) ($__color) y. y +. 1 a[-2,-1] y a[points,-1] x selection=$N
28381       if !$view_markers view_markers=2 fi
28382       rm[view]
28383      elif $b&2" || "{*,P} # Pick color from image.
28384        __color={colors,I($x*$pw/$w,$y*$ph/$h)}
28385      fi
28386    else selection=-1
28387      if {*,-SPACE}" && "narg($colors) replace_color= rm[colors] # Update color map.
28388      elif {*,-TAB} view_markers={($view_markers-1)%3} rm[view] wait -1 # Toggle markers.
28389      elif !$is_ctrl" && "{*,-R}  # Switch color replace mode.
28390        if narg($replace_color) replace_color= else replace_color=$__color fi
28391        rm[baseview] wait -1
28392      elif {*,PAGEDOWN} contrast={max(0,$contrast-1)} rm[view] wait -1 # Decrease contrast.
28393      elif {*,PAGEUP} contrast={min(9,$contrast+1)} rm[view] wait -1 # Increase contrast.
28394      elif {*,BACKSPACE}" && "$N # Remove last point.
28395        if $N>1 z[points] 0,{$N-2} else i=$points rm[points] i[$i] 0 nm[$i] points fi
28396        rm[view] wait -1
28397      fi
28398    fi
28399
28400    # Manage zoomed view bounds.
28401    w2={round(($x1-$x0)/2)} h2={round(($y1-$y0)/2)}
28402    if $x0<-$w2 x1-={$x0+$w2} x0=-$w2 fi
28403    if $y0<-$h2 y1-={$y0+$h2} y0=-$h2 fi
28404    if $x1>=$w+$w2 x0+={$w-1+$w2-$x1} x1={$w-1+$w2} fi
28405    if $y1>=$h+$h2 y0+={$h-1+$h2-$y1} y1={$h-1+$h2} fi
28406
28407    # Render color map.
28408    if !narg($colors)
28409      N={points,w}
28410      if narg($view) to[view] "Processing...",5,5,20,2 w[view] fi
28411      if $N
28412        [points]
28413        sh. 0,0,0,0 *. {$pw/$w} rm.
28414        sh. 1,1,0,0 *. {$ph/$h} rm.
28415        pointcloud. -1,$pw,$ph
28416
28417        # Additional term that depends on marker's positions.
28418        +compose_channels. max !=. 0 distance. 1 *. 0.02 +. 1 ^. -1 +. [potential]
28419        if !$1 dilate.. 3 fi
28420        watershed.. . rm. -. 1
28421      else [potential],[potential],1,3,255
28422      fi
28423      nm. colors
28424      if narg($baseview) rm[baseview] fi
28425    fi
28426
28427    # Manage replace color mode.
28428    if !narg($replace_color)" && "narg($points_replaced)
28429      rm[points,colors,view] nm[colors_replaced] colors nm[points_replaced] points current_replace_color=
28430    elif narg($replace_color)" && "['$__color']!=['$current_replace_color']
28431      if narg($colors_replaced) rm[colors_replaced,points_replaced] fi
28432      current_replace_color=$__color
28433      if {points,w}
28434        +replace_color[colors] 0,0,$replace_color,$current_replace_color
28435        +rows[points] 3,5 permute. xzcy -. 1
28436        replace_color. 0,0,$replace_color,$current_replace_color
28437        +. 1 permute. xcyz +j[points] .,0,3 rm..
28438      else 0 0
28439      fi
28440      nm.. colors_replaced
28441      nm. points_replaced
28442      if narg($baseview) rm[baseview] fi
28443    fi
28444
28445    # Render base image.
28446    if !narg($baseview)
28447      nx0={$x0*$pw/$w} ny0={$y0*$ph/$h}
28448      nx1={$x1*$pw/$w} ny1={$y1*$ph/$h}
28449      +z[img] $x0,$y0,$x1,$y1
28450      r. $ww,$wh,1,100%,{if($ww<w" && "$wh<h,2,1)}
28451      if narg($replace_color)" && "{points,w} icolors=$colors_replaced else icolors=$colors fi
28452      +z[$icolors] $nx0,$ny0,$nx1,$ny1
28453      r. $ww,$wh,1,100%,{if($ww<w" && "$wh<h,2,3)}
28454      if $1 *.. -1 +.. 255 channels.. -3,0 blend. ..,alpha rm..
28455      else rgb2ycbcr. j. ..,0,0,0,0 rm.. ycbcr2rgb.
28456      fi
28457      nm. baseview
28458      if narg($view) rm[view] fi
28459    fi
28460
28461    # Render view.
28462    if !narg($view)
28463      [baseview] r. 100%,100%,1,3
28464      if $contrast<9 /. {10-$contrast} +. {128*(1-1/(10-$contrast))} fi
28465      if $view_markers
28466        if $view_markers==2 rad1=5 rad2=3 else rad1=3 rad2=2 fi
28467        if narg($replace_color)" && "{points,w} ipoints=$points_replaced else ipoints=$points fi
28468        repeat w#$ipoints
28469          +columns[$ipoints] $>
28470          x={(i[0]-$x0)*$ww/(1+$x1-$x0)}
28471          y={(i[1]-$y0)*$wh/(1+$y1-$y0)}
28472          col={i[3]-1},{i[4]-1},{i[5]-1}
28473          rm.
28474          circle. $x,$y,$rad1,1,0 circle. $x,$y,$rad2,1,$col
28475        done
28476      fi
28477
28478      if narg($replace_color)
28479        to. "Replace      by",5,5,20,2
28480        rectangle. 80,8,111,25,1,0 rectangle. 82,10,109,23,1,$replace_color
28481        rectangle. 150,8,181,25,1,0 rectangle. 152,10,179,23,1,$current_replace_color
28482      fi
28483
28484      nm. view
28485      w[view] $ww,$wh,0,$title
28486    fi
28487
28488  while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER}
28489
28490  # Recompute colors at full resolution.
28491  if narg($view) to[view] "Processing fullres...",5,5,20,2 w[view] fi
28492  k[0,img,points]
28493  N={points,w} status=
28494  if $N
28495    status={points,^}
28496    [img] __x_colorize. $1
28497    pointcloud[points] -1,$w,$h
28498    +compose_channels[points] max !=. 0 distance. 1 *. 0.02 +. 1 ^. -1 +[potential,-1]
28499    if !$1 zfact={{img,max(w,h)}/{potential,max(w,h)}} dilate[points] {int(3*$zfact)} fi
28500    watershed[points] [potential] -[points] 1 nm[points] colors
28501  else [img],[img],1,3,255 nm. colors
28502  fi
28503
28504  if $3 # Multichannels output.
28505    k[0,colors] a c
28506  else # Merge for single layer output.
28507    k[0,img,colors]
28508    if $1 +*[img] -1 +. 255 channels. -3,0 blend[colors,-1] alpha rm[0,img]
28509    else rgb2ycbcr[colors] j[colors] [img],0,0,0,0 rm[0,img] ycbcr2rgb[colors]
28510    fi
28511  fi
28512  a c nm $name
28513
28514  __color=-1 # Force color selectors to close.
28515  u $status  # Return control points.
28516  w 0
28517
28518# Compute potential function.
28519__x_colorize :
28520  if $1 # Potential for lineart.
28521    n. 0,1 ^. 5 repeat 4 +b. 0.5% min done
28522  else  # Potential for generic grayscale image.
28523    gradient_norm. n. 0,255 normalize_local. 3,3 *. -1 n. 0,255
28524    b. 0.05% n. 0,1 sqr. +b. 0.5% n[-2,-1] 0,1 min[-2,-1]
28525  fi
28526  nm. potential
28527
28528#@cli x_connect4
28529#@cli : Launch the Connect Four game.
28530x_connect4 : check_display $0
28531  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
28532  e[] "\n
28533------ "${g}"Connect Four"$n" --------------------------------------------\n
28534----\n
28535---- Connect four tokens in a row, column or diagonally\n
28536---- to win the game.\n
28537----\n
28538---- "${c}"Left mouse button"$n" on a column inserts a new token.\n
28539---- Keys '"${c}"SPACE"$n"' or '"${c}"ENTER"$n"' lets the computer play the turn\n
28540---- (or restart game when it's over).\n
28541---- Key '"${c}"ENTER"$n"' also enables autoplay for the current player.\n
28542---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' close the window.\n
28543----\n
28544----------------------------------------------------------------"
28545  l[]
28546
28547  # Create sprite graphics.
28548  7,6 nm. board
28549  R={board,u={*,u};v={*,v};int(0.5*min(u/w,v/h))}
28550
28551  {2*$R},{2*$R} circle. 50%,50%,32%,1,1 b. 2% g. xy +[-2,-1] n. 0,1
28552  +n. -1,0.5 abs. negate. +f. 200 rv[-3--1] a[-3--1] c hsv2rgb.
28553  to_rgba. circle. 50%,50%,27%,1,0
28554
28555  {4*$R},{4*$R} circle. 50%,50%,20%,1,1 b. 1% g. xy +[-2,-1] n. 0,1 negate.
28556  pow. 0.5 +n. -1,0.8 abs. negate. +f. 10 rv[-3--1] a[-3--1] c
28557  . sh. 0 f. 60 rm. hsv2rgb[-2,-1]
28558  100%,100% circle. 50%,50%,30%,1,255 a[-3,-2] .,c rm.
28559
28560  r2dx[-3--1] $R s[-3--1] c,-3 rm...
28561  n[^0] 0,255 round[^0] nm[^0] cache,cachem,token0,token1,tokenm
28562  +b[cachem] 2% shift. 1%,1%,0,0,2 max[cachem,-1]
28563  r[cache,cachem] 100%,{board,h*100}%,1,100%,0,2
28564
28565  # Define board evaluation function.
28566  evalf="const op = 3 - p;
28567         case(dx,dy) = (
28568           pgood = pbad = 0;
28569           for (k = -2, k<2, ++k,
28570             X = x + k*dx;
28571             Y = y + k*dy;
28572             if (X>=0 && X<w && Y>=0 && Y<h,
28573               val = i(X,Y);
28574               if (pgood>=0 && val==op, pgood = -1, pgood+=!!val);
28575               if (pbad>=0 && val==p,  pbad = -1, pbad+=!!val);
28576             );
28577           );
28578           pgood = arg(2+pgood,0,1,2,4,100,1e8);
28579           pbad = arg(2+pbad,0,1,2,4,10000,1e8);
28580           pgood - pbad;
28581         );
28582         case(1,0) + case(0,1) + case(1,1) + case(1,-1)"
28583
28584  # Start game.
28585  do
28586
28587    # Initialize game.
28588    if !narg($visu)
28589      {board,[w,h]*$R},1,3,64
28590      repeat h#$board,y repeat w#$board,x
28591        val={board,i($x,$y)} if $val j. [token{$val-1}],{$x*$R},{$y*$R},0,0,1,[tokenm],255 fi
28592      done done
28593      +r[cache,cachem] {board,[w,h]*$R},1,100%,0,2 a[-2,-1] c blend[-2,-1] alpha
28594      nm. visu w[visu] 100%,100%,0,"[G'MIC] Connect Four"
28595      turn=0 is_falling=0 x=-1 yv=0 dyv=0 winner=
28596      autoplayer0=0 autoplayer1=0
28597    fi
28598
28599    # Estimate ymax for each column.
28600    if !narg($ymax)
28601      {board,w},1,1,1,"y = -1; repeat (h#"$board",k, if(!i(#"$board",x,k),y = k)); y" nm. ymax
28602      if iM<0 winner=-1,-1,-1 fi # Draw game
28603    fi
28604
28605    if narg($winner) # End of game animation
28606      [visu] nm. tmpvisu
28607      x={arg(1,$winner)} y={arg(2,$winner)} c={arg(3,$winner)}
28608      if $x<0 # Draw game
28609        if !narg($text)
28610          0 t. "Draw game",0,0,57,1,1 r2dx. {tmpvisu,w/2} expand_xy. 3,0 +dilate. 5 n.. 0,255 to_rgb..
28611          nm[-2,-1] text,textm
28612        fi
28613        j[tmpvisu] [text],{tmpvisu,([w,h]-[w#$text,h#$text])/2},0,0,{0.7+0.3*sin(5*$|)},[textm]
28614      else
28615        repeat 4
28616          [token$turn]
28617          rgb2hsv. sh. 1,2 +. {0.4*sin(5*$|)} c. 0,1 rm. hsv2rgb.
28618          j. [cache],0,0,0,0,1,[cachem],255
28619          j[tmpvisu] .,{$R*[$x,$y]},0,0,1 rm.
28620          x+={$c!=2?1:0} y+={$c==1?0:$c==4?-1:1}
28621        done
28622      fi
28623      w[tmpvisu] 100%,100% rm[tmpvisu] wait 20
28624
28625      if {*,-SPACE}" || "{*,-ENTER} # Restart new game
28626        rm[visu,ymax] f[board] 0 winner=
28627      fi
28628
28629    elif !$is_falling # Manage column selection
28630
28631      if !${autoplayer$turn}
28632        x={visu,X={*,x};X<0?X:int(X*w#$board/w)}
28633        yM={i("#"$ymax,$x)}
28634        if {board,$x<0" || "$x>=w} w[visu] 100%,100%
28635        else
28636          [visu] $R,100%,1,3,($yM>=0?1:0)*($turn?[255,255,0]:[255,0,0])
28637          j.. .,{$x*$R},0,0,0,{$yM>=0?0.15:0.3} rm.
28638          w. 100%,100% rm.
28639        fi
28640        wait
28641      fi
28642
28643      if {*,-b}&1" && "$yM>=0" && "$x>=0 is_falling=1 yv=0 dyv=1 # Manual play
28644      elif ${autoplayer$turn}" || "{*,-SPACE}" || "{*,ENTER} # Computer play
28645        if {*,-ENTER} autoplayer$turn=1 fi
28646        max_score=-inf max_col=
28647        repeat w#$board,move1
28648          yM1={i("#"$ymax,$move1)}
28649          if $yM1>=0
28650            +=[board] {1+$turn},$move1,$yM1 nm. board1
28651            {board,w},1,1,1,"y = -1; repeat (h#"$board1",k, if(!i(#"$board1",x,k),y = k)); y" nm. ymax1
28652            opp_max_score=-inf opp_max_board={board,^}
28653            opp_turn={($turn+1)%2}
28654            repeat w#$board,move2
28655              yM2={i("#"$ymax1,$move2)}
28656              if $yM2>=0
28657                +=[board1] {1+$opp_turn},$move2,$yM2
28658                +f. "const p = 1 + "$opp_turn"; "$evalf score={is+u} rm.
28659                if $score>$opp_max_score opp_max_score=$score opp_max_board={^} fi
28660                rm.
28661              fi
28662            done
28663            rm[board1,ymax1]
28664
28665            {board,[w,h,1,1]},$opp_max_board
28666            f. "const p = 1 + "$turn"; "$evalf score={is+u} rm.
28667            if $score>$max_score max_score=$score max_col=$move1 fi
28668          fi
28669        done
28670        x=$max_col is_falling=1 yv=0 dyv=1
28671      fi
28672
28673    else # Manage token falling animation
28674      if !narg($column)
28675        $R,{board,h*$R},1,3,64
28676        repeat h#$board v={board,i($x,$>)} if $v j. [token{$v-1}],0,{$>*$R},0,0,1,[tokenm],255 fi done
28677        nm. column
28678      fi
28679
28680      yM={i("#"$ymax,$x)}
28681      [column]
28682      j. [token$turn],0,$yv,0,0,1,[tokenm],255
28683      j. [cache],0,0,0,0,1,[cachem],255
28684      [visu] nm. tmpvisu
28685      j[tmpvisu] ..,{$R*$x},0,0,0 rm.. w[tmpvisu] 100%,100%
28686      if $yv>=$yM*$R
28687        j[visu] [tmpvisu] is_falling=0
28688        =[board] {$turn+1},$x,$yM
28689        rm[ymax,column]
28690
28691        # Check end of game.
28692        +f[board] "if (!i,0,
28693                      case_h = i==j(1) && i==j(2) && i==j(3);
28694                      case_v = i==j(0,1) && i==j(0,2) && i==j(0,3);
28695                      case_d1 = i==j(1,1) && i==j(2,2) && i==j(3,3);
28696                      case_d2 = i==j(1,-1) && i==j(2,-2) && i==j(3,-3);
28697                      case_h?1:case_v?2:case_d1?3:case_d2?4)"
28698        if iM winner={[xM,yM,i(xM,yM)]} # Player won !
28699        else turn={($turn+1)%2}
28700        fi
28701        rm.
28702      fi
28703      rm[tmpvisu]
28704      yv={min($yM*$R,$yv+$dyv)} dyv+={visu,h/100}
28705      wait 20
28706    fi
28707
28708  while {*}" && "!{*,ESC}" && "!{*,Q}
28709  rm w 0 endl
28710
28711#@cli xz : eq. to 'x_crop'
28712xz :
28713  _gmic_s="$?" v + _x_crop
28714
28715#@cli x_crop
28716#@cli : Crop selected images interactively.
28717#@cli : (eq. to 'xz').
28718x_crop :
28719  _gmic_s="$?" v + _$0
28720
28721_x_crop :
28722  e[0--3] "Crop image"$_gmic_s" interactively."
28723  repeat $! l[$>]
28724    w ${"fitscreen "{[w,h,d]}},1,"[G'MIC] "{n}" - Interactive crop"
28725    +select 2,{round([w,h,d]/2)},0,1 u={^} z.. $u rm.
28726    w[] 0 is_change
28727  endl done u $u
28728
28729#@cli x_cut
28730#@cli : Cut selected images interactively.
28731x_cut :
28732  e[^-1] "Cut image"$_gmic_s" interactively."
28733  repeat $! l[$>]
28734    wsiz0=${"fitscreen ."}
28735    value0,value1=-1,-1
28736    w[] $wsiz0,0,"[G'MIC] "{n}" - Interactive cut"
28737    0
28738    for {*}" && "!{*,ESC}
28739
28740      if [w#1,h#1]!=[{*,w,h}] # Generate image view
28741       rm[1] +slices[0] 50% r. {*,w,h},1,100%,1 w.
28742      fi
28743
28744      mx,my,mb={*,x,y,b}
28745      if $mb" && "$mx>=0" && "$my>=0
28746        value0,value1={"dw1 = "{*,w}" - 1; value0 = "$mx"*100/dw1;
28747                        dh1 = "{*,h}" - 1; value1 = "$my"*100/dh1;
28748                        [ value0,value1 ];"}
28749        update_view=1
28750      fi
28751
28752      if $update_view
28753        if $value0>=0" && "$value1>=0
28754          +c[1] $value0%,$value1% n. 0,255
28755          to. "Min: "{_round($value0,0.1)}%"\n"\
28756              "Max: "{_round($value1,0.1)}%,1%,1%,{max(13,3.5*h%)}
28757          w. rm.
28758        else w.
28759        fi
28760        update_view=0
28761      fi
28762
28763      wait
28764      if {*,r} update_view=1 fi
28765      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D}
28766        w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 update_view=1
28767      fi
28768      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C}
28769        w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 update_view=1
28770      fi
28771      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R}
28772        w[] $wsiz0 wait -1 update_view=1
28773      fi
28774    done
28775    w[] 0 rm. u $value0%,$value1% c ${}
28776 endl done
28777
28778#@cli x_fire
28779#@cli : Launch the fire effect demo.
28780x_fire : skip "${1=G\47MIC}" check_display $0
28781  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
28782  e[] "\n
28783------ "${g}"Fire effect"$n" ------------------------\n
28784----\n
28785---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
28786---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
28787---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
28788----\n
28789-------------------------------------------"
28790
28791  # Init image data.
28792  i[0] 100,32 w[0] {[4.5*w,6.75*h]},0,"[G"{`39`}"MIC] Fire Effect"
28793  if {*,w}<0.5*{*,u} w[] {[{*,w},{*,h}]*1.5} fi
28794  i[1] (0,255,255,255,255^0,0,255,255,255^0,0,0,128,255) r[1] 256,1,1,3,3
28795  i[2] (0,0,0;0,0,0;1,1,1;0,1,0) *[2] 0.21
28796  text3d "$1",33,3,1
28797  mv. 3 c3d[3] n3d[3] *3d[3] 320 col3d[3] 255,205,130 db3d 0 f3d 300
28798  100,100 rand. 0,255 ellipse. 50%,50%,5,5,0,1,300 b. 10
28799  sharpen. 1000 shrink_xy. 1 n. 0,255 to_rgb. light3d . rm.
28800
28801  # Start animation loop.
28802  angle=0
28803  do
28804    correlate[0] [2]                                 # Apply fire effect.
28805    {0,w},1 rand. 128,256 j[0] .,0,{{0,h}-1} rm.     # Add new random values at the bottom line.
28806    +r[0] 400,200,1,1,3 map. [1]                     # Map fire palette
28807    +r3d[3] 0,1,0,$angle j3d.. .,50%,50%,0,1,5,0,0   # Draw 3D object.
28808    *3d. 0.25,0.16,1 j3d[0] .,50%,50%,0,1,3,0,0
28809    rm.
28810    angle+=3                                         # Update 3D angle.
28811    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,1,0.2 fi
28812    w.
28813    if {*,CTRLLEFT}" && "{*,D} w[] {1.5*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi
28814    rm. wait 40
28815  while {*}" && "!{*,ESC}" && "!{*,Q}
28816
28817  # Exit properly.
28818  rm[0-3] w 0
28819
28820#@cli x_fireworks
28821#@cli : Launch the fireworks demo.
28822x_fireworks : check_display $0
28823  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
28824  e[] "\n
28825------ "${g}"Fireworks"$n" --------------------------\n
28826----\n
28827---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
28828---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
28829---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
28830----\n
28831--------------------------------------------"
28832  l[]
28833  (16;64^64;32^128;32) r 320,160,1,3,3             # [-2] = Background (color gradient).
28834  .                                                 # [-1] = Rendered image.
28835  w. {1.5*w},{1.5*h},0,"[G"{`39`}"MIC] Fireworks"  # Display window.
28836  time=0
28837  do                                               # Start animation loop.
28838    time-=1
28839    if $!==2\ ||\ $time<0   # Insert new rocket.
28840      i[0] ({u(w)},\        # X-position
28841            {h},\           # Y-position
28842            {u(-3,3)},\     # X-velocity
28843            {u(2)-5},\      # Y-velocity
28844            {30+u(20)},\    # Time of explosion
28845            1.5,\           # Radius
28846            255,255,255)    # Color
28847      time={u(20)}          # Elapsed time until next rocket.
28848    fi
28849    *. 0.99                 # Create fading effect with previous frames.
28850    j. ..,0,0,0,0,0.2       # Add background.
28851    i=0
28852    repeat $!-2
28853      to_be_removed=0
28854      radius={if({$i,@4}>0,{$i,@5}/3,{$i,@5}*(1+2*({$i,@4}+2)/120))}
28855      ellipse. {$i,@0},{$i,@1},{$i,@5},{max(1,$radius)},{atan2({$i,@3},{$i,@2})*180/pi},0.6,{$i,@6-8}  # Draw rocket.
28856      ({$i,@2},{$i,@3},0,0.09,-1,0,0,0,0) +[$i,-1] # Compute new position of the rocket.
28857      if {$i,@0}<0\ ||\ {$i,@0}>=w\ ||\ {$i,@1}>=h\ ||\ $radius<0 to_be_removed=1 fi # Discard if rocket disappear.
28858      if {$i,@4}<0\ &&\ {$i,@4}>=-1 # In case of explosion -> Split current rocket into several colorful rockets.
28859        color={min(255,80+u(200))},{min(255,80+u(200))},{min(255,80+u(200))}
28860        radius={u(10)}
28861        N={5+u(10)}
28862        repeat $N
28863          angle={$>*2*pi/$N}
28864          i... ({$i,@0,1},{2*cos($angle)+{$i,@2}/1.5},{2*sin($angle)+{$i,@3}/1.5},-2,$radius,$color)
28865        done
28866        to_be_removed=1
28867      fi
28868      if $to_be_removed rm[$i] else i+=1 fi  # If processed rocket has to be removed.
28869    done
28870    fps=${-fps} if $fps>0 to. $fps" fps",3,{h-20},14,1,0.2 fi
28871    w. wait 20  # Display rendered frame.
28872    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
28873  while {*}" && "!{*,ESC}" && "!{*,Q}
28874  rm w 0 endl
28875
28876#@cli x_fisheye
28877#@cli : Launch the fish-eye effect demo.
28878x_fisheye : check_display $0
28879  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
28880  e[] "\n
28881------ "${g}"Fish-eye effect"$n" --------------------\n
28882----\n
28883---- "${c}"Mouse pointer"$n" moves fish-eye center.\n
28884---- "${c}"Mouse buttons"$n" set fish-eye size.\n
28885---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
28886---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
28887---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
28888----\n
28889-------------------------------------------"
28890
28891  if $!>0 a x n 0,255 r2dy 220 else
28892  120,90,1,3 rand. 0,255 plasma. 0.3,3 n 0,255
28893  t "  G\47MIC\nFISH-EYE\n EFFECT",20,13,23,1,255 scale3x b 5 sharpen 1000
28894  f i+150-3*abs(y-h/2) c. 0,255 frame_fuzzy. 15,10,15,1.5,0 to_rgb.
28895  fi
28896  torus3d 20,6 col3d. {u(30,255)},{u(30,255)},{u(30,255)} +r3d. 1,0,0,90
28897  col3d. {u(30,255)},{u(30,255)},{u(30,255)} +3d. 15 +3d[-2,-1] *3d. 4 db3d 0 c3d.
28898  R=30
28899  w.. {1.25*{-2,w}},{1.25*{-2,h}},0,"[G"{`39`}"MIC] Fish-Eye Effect"
28900  do
28901    wait 40
28902    if {*,b}==1 R={min(80,$R+8)} fi
28903    if {*,b}==2 R={max(3,$R-8)} fi
28904    +j3d.. .,{50+30*cos($|*2.5)}%,{50+30*sin($|*1.6)}%,{80+230*sin($|*2.6)},0.7,3,0,0 r3d.. 1,0.2,0.6,3
28905    if {*,x}>=0
28906    fisheye. {{*,x}*100/{*,w}},{{*,y}*100/{*,h}},$R
28907    fi
28908    w.
28909    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
28910    rm.
28911    if {*}==0" || "{*,ESC}" || "{*,Q} rm[-2,-1] w 0 return fi
28912  while 1
28913
28914#@cli x_fourier
28915#@cli : Launch the fourier filtering demo.
28916x_fourier : check_display $0
28917  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
28918  e[] "\n
28919------ "${g}"Fourier-filtering"$n" ----------------------------------------\n
28920----\n
28921---- "${c}"Mouse buttons"$n" on the right image to set min/max frequencies.\n
28922---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
28923---- Keys '"${c}"CTRL+C"$n"' to decrease window size.\n
28924---- Keys '"${c}"CTRL+R"$n"' to reset window size.\n
28925---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
28926----\n
28927-----------------------------------------------------------------"
28928
28929  if !$! sp ? r2dx 400 fi
28930
28931  repeat $! l[$>]
28932
28933    # Init variables.
28934    need_update=1  # need_update (boolean)
28935    freqmin=0      # min freq. (in %)
28936    freqmax=100    # max freq. (in %)
28937
28938    if w>3*{*,u}/5 r2dx. {3*{*,u}/10} fi  # Reduce image size if necessary.
28939    if h>3*{*,v}/5 r2dy. {3*{*,v}/5} fi
28940
28941    # Compute fourier transform.
28942    +fft. nm.. real nm. imag
28943
28944    # Generate log-magnitude image.
28945    +sqr[real,imag] +[-2,-1] sqrt. +. 1 log.
28946    n. 0,255 shift. {round(w/2)},{round(h/2)},0,0,2 to_colormode. {-2,s}
28947    nm. logmag
28948
28949    +r2dy. 128 frame. 1,1,0 nm. thumb
28950    w[0,-2] -1,-1,0,"[G"{`39`}"MIC] Fourier Filtering"
28951
28952    l
28953    if !narg($first_time)
28954      parallel 0,"alert[thumb] \"[G"{`39`}"MIC Fourier Filtering]\",\
28955            \"The G\47MIC Fourier filtering demo illustrates the effect\n\
28956            of bandpass frequency filtering on an image. Use your mouse\n\
28957            buttons to select low and high bounds for the frequencies\n\
28958            displayed on the Fourier representation of the image\n\
28959            (right image).\",\
28960            \"OK\""
28961      first_time=0
28962    fi
28963
28964    # Enter user event-loop.
28965    do
28966
28967      if $need_update # If image must be updated.
28968
28969        # Generated filtering mask.
28970        [logmag],[logmag] nm. mask
28971        r={sqrt(w^2+h^2)*$freqmax/200} ellipse[mask] 50%,50%,$r,$r,0,1,1
28972        r={max(0,sqrt(w^2+h^2)*$freqmin/200-1)} if $r ellipse[mask] 50%,50%,$r,$r,0,1,0 fi
28973
28974        # Compute filtered log-magnitude.
28975        +*[logmag] [mask] +. [mask] /. 2 n. 0,255
28976
28977        # Compute filtered fourier representation.
28978        shift[mask] -{mask,round(w/2)},-{mask,round(h/2)},0,0,2
28979        +*[real,imag] [mask]
28980        rm[mask]
28981
28982        # Compute filtered image by inverse fourier.
28983        ifft[-2,-1] rm. n. 0,255
28984
28985        # Display filtered image.
28986        rv[-2,-1]
28987        if {*} r[-2,-1] {{*,w}/2},{*,h} fi
28988        t. "Freq. Min/Max = "{int($freqmin)}"% / "{int($freqmax)}"%",5,5,13,1,255
28989        w[-2,-1] rm[-2,-1]
28990        need_update=0
28991
28992      fi
28993
28994      wait
28995
28996      if {*,b}" && "{*,x}>={*,w}/2  # If mouse button pressed on the right pane.
28997        r={200*sqrt(({*,x}-3*{*,w}/4)^2+({*,y}-{*,h}/2)^2)/\ # Compute selected radius (in %).
28998           sqrt(({*,w}/2)^2+{*,h}^2)}
28999        if {*,b}&1 freqmax=$r        # Update max freq. if left button.
29000        else freqmin={max(0,$r-3)}   # Update min freq. if other button.
29001        fi
29002        if $freqmin>=$freqmax freqmin=$freqmax fi   # Check that the min/max freq. are ordered.
29003        need_update=1                               # Tell that the image must be updated.
29004      fi
29005
29006      if {*,r} need_update=1 fi
29007
29008      # Increase window size.
29009      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} need_update=1 fi
29010
29011      # Decrease window size.
29012      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} need_update=1 fi
29013
29014      # Reset window size.
29015      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {2*{0,w}},{0,h} need_update=1 fi
29016
29017    while {*}" && "!{*,ESC}" && "!{*,Q}
29018    w 0
29019    endl
29020    rm[^0] # Clean images and window.
29021  endl done rm
29022
29023#@cli x_grab_color : _variable_name
29024#@cli : Open a color grabber widget from the first selected image.
29025#@cli : Argument 'variable_name' specifies the variable that contains the selected color values at any time.
29026#@cli : Assigning '-1' to it forces the interactive window to close.
29027#@cli : Default values: 'variable_name=xgc_variable'.
29028x_grab_color : skip ${1=xgc_variable} check_display $0
29029  if !$! error[0--3] "Command '$0': Missing specified input image." fi
29030  l[0] nm={n} nm. img
29031  e[^-1] "Open "${arg\ {0,s},GRAY,GRAYA,RGB,RGBA}" color grabber widget for image$?, with variable name '$1'."
29032
29033  if !{*} w[] ${fitscreen[]\ {[w,h,1]},128,50%},0,0,-1,-1,"Grab a color" fi
29034  _x_grab_color +dilate. 3 nm. icon_mask *.. 255 to_rgb.. nm.. icon_sprite
29035
29036  xc=5 yc=5 o$1=$$1
29037
29038  cursor[0] 0
29039  do
29040    if !narg($visu0)
29041      +r[img] {*,w},{*,h},1,100%,2 drgba. w. nm. visu0
29042    fi
29043
29044    x={*,x} y={*,y} b={*,b} mouse_over={$x>=0" && "$y>=0}
29045    hc={narg($$1)?40:24}
29046    yc={visu0,nhc=h-$hc-8;!$mouse_over?$yc:$y<$hc||$yc+$hc>=h?nhc:$y>=nhc?5:$yc}
29047
29048    if [0$ox,0$oy,0$ob,0$ohc,0$oyc,0$ocolor,0${o$1}]!=[$x,$y,$b,$hc,$yc,0$color,0$$1]
29049      [visu0] nm. visu
29050      if narg($color)
29051        24,$hc,1,[img] fc. $color
29052        if narg($$1) rectangle. 0,24,100%,100%,1,$$1 line. 0,24,100%,24,1,0 fi
29053        drgba. frame. 1,1,0 frame. 1,1,255 j[visu] .,$xc,$yc rm.
29054        0
29055        if narg($$1)
29056          t. "Position ("$X","$Y")\nColor    ("{``$color}")\nSelected ("{``$$1}")",1,0,15,1,255
29057        else
29058          t. "Position ("$X","$Y")\nColor ("{``$color}")",1,0,15,1,255
29059        fi
29060        +dilate. 5 r.. 100%,100%,1,3
29061        j[visu] ..,{30+$xc},$yc,0,0,0.85,.,255 rm[-2,-1]
29062      fi
29063
29064      if $mouse_over
29065        X={img,round($x*(w-1)/({*,w}-1))}
29066        Y={img,round($y*(h-1)/({*,h}-1))}
29067        color={img,round(I($X,$Y))}
29068        j[visu] [icon_sprite],$x,{icon_sprite,$y-h+1},0,0,1,[icon_mask]
29069        if $b&1 $1=$color fi
29070      fi
29071      w[visu] rm[visu]
29072
29073      ox=$x oy=$y ob=$b ohc=$hc oyc=$yc ocolor=$color o$1=$$1
29074    fi
29075
29076    if arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_' wait 50 else wait fi
29077    if {*,r} w[] -1 rm[visu0] yc=5 fi
29078    if ['$$1']=='-1' break fi # Close request
29079
29080  while {*}" && "!{*,ESC}" && "!{*,Q}
29081  w 0 k[img] nm $nm endl
29082  u $color
29083
29084# Define color grabber icon.
29085_x_grab_color :
29086  base642img[] \
29087"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMSAzMzcgMSAxICMyNzcKeJyNiNlOwlAYBi9IDDcaY+LGpiJbj7tBKEbglTQE7dcbkZS1gfNXFCx"\
29088"lK6WE83w+gDaaeG0mmWTm67NSea5s1Pz6Ot80tnrb/d3BnhkYBq3QKDyOTA5nmfqDnW2W5nK77Mj60yLXgZvrqm6Oq47MYWc4ZhnCJE2wrgjmBWFwRm"\
29089"r/hNQ3ZqivKQIlCN0YQZdeQGgxzXOD1T3XWEMhVWMtz1Wmw4h1QHEOShogqQc67YPOBwpdmgpdWzDSY9DNFJS1wWUH/NZF926FTl4oel48tgui3C6KU"\
29090"rMo7rWCYCvJg4k/fvp/5/dJq9QyuUy48UXMOZ5H7ah9ND2YRMbhUXgUsoLDgOnxsf++0/TX1zRf1fcNa0iPgw=="
29091  -. 127 decompress_rle.
29092  frame. 10,10,0
29093  r2dx. 24
29094
29095#@cli x_hanoi
29096#@cli : Launch the Tower of Hanoi game.
29097x_hanoi : check_display $0
29098  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29099  e[] "\n
29100------ "${g}"Tower of Hanoï"$n" ---------------------\n
29101----\n
29102---- "${c}"Left button"$n" and "${c}"mouse"$n" to move a disk.\n
29103---- "${c}"Right button"$n" to rotate 3D view.\n
29104---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
29105---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
29106---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29107----\n
29108-------------------------------------------"
29109  l[]
29110  l[] # Create 3D rods
29111    cylinder3d 1,10 r3d 1,0,0,90
29112    ++3d 10,0,0  +-3d.. 10,0,0
29113    box3d 30,1,10 -3d. 15,0,5
29114    +3d nm rods3d
29115    400,400 noise. 1 fftpolar. f.. 'r=sqrt((x-w/2)^2+.01*(y-h/2)^2);i/(1+r)' ifftpolar[-2,-1] n. 0,31
29116    (86,50,50;132,36,12;218,109,66;231,207,180;255,193,140) permute. yzcx r. 32,1,1,3,3
29117    map.. . rm. b. 2% b. x,1% sharpen. 100 c. 0,255
29118    r3d.. 1,0,0,-90 texturize3d.. . r3d.. 1,0,0,90 rm.
29119  endl
29120
29121  l[] # Create 3D disks
29122    6,1,1,3,'[360*x/w,0.9*(1-(x/w)^0.5),0.9]' hsi2rgb.
29123    ytop0=0
29124    repeat w#0
29125      R,r={3-0.3*$>},{1.6-0.22*$>}
29126      torus3d $R,$r,36,10
29127      300,300 plasma. 1,1,3 b. 20 sharpen. 300 n. 150,255 1,1,1,3,{0,I[$>]} r. ..,..,1,3 rv[-2,-1] blend[-2,-1]
29128      luminance,0.75 # Marble texture
29129      texturize3d.. . rm.
29130      /3d. 1,1,{0.3+$r} r3d. 1,0,0,90 -3d. 0,0.8,0
29131      a$>,x$>,y$>,h$>=0,0,$ytop0,{1.8*$r/(0.3+$r)}
29132      ytop0+=${h$>}
29133      nm. disk3d$>
29134    done
29135    rm[0]
29136  endl
29137
29138  w[] 640,400,0,"[G"{`39`}"MIC] Tower of Hano\357"
29139  1,3,1,3,'y==0?[32,128,100]:y==1?[64,16,0]:[0,0,0]' r. {*,w},{*,h},1,3,3 nm. background
29140  nb_moves,buttons,motion3d_x,motion3d_y=0
29141  x,rod,rod_source,rod_target,selected=-1
29142  fading=$| error=0
29143  do
29144
29145    # Display 3D view for current game state.
29146    repeat 6 +r3d[disk3d$>] 1,1,1,${a$>} +3d. {10*(${x$>}-1)},-${y$>},0 done +3d[-6--1] +3d. [rods3d]
29147    r3d. 1,0,0,20
29148    if !($buttons&2) r3d. 0,1,0.3,{5*cos(1.5*$|)} r3d. 0.3,0,1,{3*sin(0.8*$|)} fi
29149    r3d. 1,0,0,$motion3d_y r3d. 0,-1,0,$motion3d_x *3d. 20
29150    [background] j3d. ..,50%,70%,10,1,5,0,1,800,200,0,-3000,0.15,0.2
29151    t. "#Moves: "$nb_moves,2%,92%,20,1,255
29152
29153    if $error (255^0^0) ri. .. j.. .,0,0,0,0,$error error={max(0,$error-0.2)} rm. fi
29154    if $|-$fading<1  *. {$|-$fading} fi
29155    w. wait 40
29156    if {*,CTRLLEFT}" && "{*,D} w[] {w*1.5},{h*1.5} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
29157    rm[-2,-1]
29158
29159    # Get index of top disk for each rod.
29160    top0,top1,top2,ytop0,ytop1,ytop2=-1
29161    repeat 6
29162      rod={round(${x$>})}
29163      if $selected!=$>" && "${y$>}+${h$>}>${ytop$rod} ytop$rod={${y$>}+${h$>}} top$rod=$> fi
29164    done
29165
29166    # Manage user events.
29167    prev_buttons=$buttons
29168    mouse_x,mouse_y,buttons={*,x},{*,y},{*,b}
29169    if $mouse_x>=0 x={2.6*($mouse_x/{*,w}-0.5)+1} rod={round($x)} fi
29170
29171    if $mouse_x>=0" && "$buttons&2 # Right mouse button
29172      motion3d_x,motion3d_y={([$mouse_x,$mouse_y]/[{*,w},{*,h}]-0.5)*90}
29173
29174    elif $mouse_x>=0" && "$buttons&1 # Left mouse button
29175      if $selected<0 # Select a disk
29176        selected=${top$rod}
29177        rod_source={$selected<0?-1:$rod}
29178      fi
29179      if $selected>=0" && "$rod>=0 # Move a selected disk
29180        if ${y$selected}<11 y$selected={min(11,${y$selected}+3)}
29181        else
29182          x$selected+={d=$rod-${x$selected};sign(d)*min(0.3,abs(d))}
29183          y$selected={x=${x$selected};11+1.5*sin(pi*abs(x-round(x)))}
29184          a$selected={x=${x$selected};45*sin(pi*abs(x-round(x)))}
29185        fi
29186      fi
29187
29188    elif !$buttons # No mouse button
29189      if $rod>=0" && "$selected>=0
29190        if $rod_target<0
29191          if ${top$rod}<$selected rod_target=$rod nb_moves+={$rod_target!=$rod_source} # Allowed move
29192          else rod_target=$rod_source error=0.8 # Forbidden move
29193          fi
29194        fi
29195        x$selected=$rod_target
29196        a$selected=0
29197        ytop={max(0,${ytop$rod_target})}
29198        if ${y$selected}>$ytop y$selected={max($ytop,${y$selected}-3)}
29199        else x,rod,rod_source,rod_target,selected=-1
29200        fi
29201      fi
29202    fi
29203
29204    if !($buttons&2) # Slowly go back to initial 3D view
29205      motion3d_x-={sign($motion3d_x)*min(1,abs($motion3d_x))}
29206      motion3d_y-={sign($motion3d_y)*min(1,abs($motion3d_y))}
29207    fi
29208
29209  while {*}" && "!{*,ESC}" && "!{*,Q}
29210  w[] 0 rm endl
29211
29212#@cli x_histogram
29213#@cli : Launch the histogram demo.
29214x_histogram : check_display $0
29215  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29216  e[] "\n
29217------ "${g}"Histogram demo"$n" -------------------------------\n
29218----\n
29219---- "${c}"Mouse"$n" to set parameters.\n
29220---- "${c}"Right button"$n" or key '"${c}"SPACE"$n"' to reset.\n
29221---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29222----\n
29223-----------------------------------------------------"
29224
29225  if !$! sp ? to_rgb
29226  else k[0] to_rgb r2dy 300,2 if w>800 r 800,100%,1,3,2 fi n 0,255
29227  fi
29228
29229  # Prepare image layout.
29230  +frame. 1,1,0
29231  300,{h},1,3,220
29232
29233  t. "Gamma :",5,0,16,1,0
29234  t. "Contrast :",5,50,16,1,0
29235  t. "Brightness :",5,100,16,1,0
29236  t. "Smoothness :",5,150,16,1,0
29237  t. "Sharpness :",5,200,16,1,0
29238  t. "Clusters :",5,250,16,1,0
29239  a[-2,-1] x
29240  {w},200,1,3,255
29241  grid. 10%,10%,0,0,0.3,0xCCCCCCCC,0
29242  rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0
29243  axes. 0,255,1,0,13,1,0
29244  frame[-2,-1] 5,5,220
29245  a[-2,-1] y
29246
29247  # Initialize variables.
29248  clusters=64 sharpness=0 smoothness=0 contrast=1 brightness=0 gamma=1
29249
29250  # Start event loop.
29251  do
29252
29253    # Render corrected image and insert it in canvas.
29254    [0]
29255    ia={ia}
29256    if $gamma /. 255 ^. {1/$gamma} *. 255 fi
29257    -. $ia *. $contrast +. $brightness +. $ia
29258    b. $smoothness
29259    sharpen. $sharpness
29260    c. 0,255
29261    +j.. .,6,6
29262
29263    # Render parameter sliders.
29264    sx={{0,w}+12}
29265    _x_histogram. {$gamma*100/4} j.. .,$sx,25 rm.
29266    _x_histogram. {$contrast*100/4} j.. .,$sx,75 rm.
29267    _x_histogram. {($brightness+128)*100/256} j.. .,$sx,125 rm.
29268    _x_histogram. {$smoothness*100/10} j.. .,$sx,175 rm.
29269    _x_histogram. {$sharpness*100/2000} j.. .,$sx,225 rm.
29270    _x_histogram. {$clusters*100/256} j.. .,$sx,275 rm.
29271
29272    # Render corresponding histogram.
29273    +s.. c histogram[-3--1] $clusters,0,255 /[-3--1] {6*{0,wh}/$clusters} rm[-5]
29274    +z[-4] 5,{0,h+16},{{-4,w}-5},{{-4,h}-6}
29275    graph. [-4],3,0,1,0,0.2,255,0,0
29276    graph. ...,3,0,1,0,0.2,0,255,0
29277    graph. ..,3,0,1,0,0.2,0,0,255
29278    rm[-4--2]
29279    j.. .,5,{0,h+16} rm.
29280
29281    if {*,b}&1\ &&\ {*,x}<{0,w}\ &&\ {*,y}<{0,h}
29282      j. [0],6,6 to. Original,10,10,16
29283    fi
29284
29285    # Display rendering.
29286    w. {w},{h},0,"[G"{`39`}"MIC] Histogram Demo" rm.
29287    wait
29288
29289    # Manage user interactions.
29290    if {*,b}&1\ &&\ {*,x}>={0,w}-10
29291      if {*,y}>=25\ &&\ {*,y}<=42
29292        gamma={max(0,min(4,({*,x}-$sx)*4/280))}
29293      elif {*,y}>=75\ &&\ {*,y}<=92
29294        contrast={max(0,min(4,({*,x}-$sx)*4/280))}
29295      elif {*,y}>=125\ &&\ {*,y}<=142
29296        brightness={max(-128,min(128,({*,x}-$sx)*256/280-128))}
29297      elif {*,y}>=175\ &&\ {*,y}<=192
29298        smoothness={max(0,min(10,({*,x}-$sx)*10/280))}
29299      elif {*,y}>=225\ &&\ {*,y}<=242
29300        sharpness={max(0,min(2000,({*,x}-$sx)*2000/280))}
29301      elif {*,y}>=275\ &&\ {*,y}<=292
29302        clusters={max(2,min(256,({*,x}-$sx)*256/280))}
29303      fi
29304    fi
29305    if {*,b}&2\ ||\ {*,SPACE} clusters=64 sharpness=0 smoothness=0 contrast=1 brightness=0 gamma=1 fi
29306  while {*}" && "!{*,ESC}" && "!{*,Q}
29307  w 0 rm
29308
29309_x_histogram :
29310  val={max(0,min(100,$1))}
29311  280,2,1,3,255 line. 0,0,$val%,0,1,0,255,0 line. 0,1,$val%,1,1,240,255,62 r. 100%,16,1,3,3
29312  0 t. {round($val)}%,0,0,14,1,1 +*. -255 +. 255 r. 100%,100%,1,3
29313  j... .,{(280-w)/2},{(16-h)/2},0,0,1,.. rm[-2,-1]
29314  r. {w+2},{h+2},1,3,0,0,0.5,0.5
29315
29316#@cli x_hough
29317#@cli : Launch the hough transform demo.
29318x_hough : check_display $0
29319  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29320  e[] "\n
29321------ "${g}"Hough-transform"$n" -----------------------------------------\n
29322----\n
29323---- "${c}"Mouse buttons"$n" on the vote image to draw corresponding line.\n
29324---- "${c}"Mouse buttons"$n" on the image to vote for all lines crossing.\n
29325---- the clicked point.\n
29326---- Key '"${c}"SPACE"$n"' to reset the hough window.\n
29327---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29328----\n
29329-----------------------------------------------------------------"
29330
29331  if !$! l[] sp greece onfail testimage2d 400 endl fi
29332  n 0,255
29333
29334  repeat $! l[$>]
29335    r. ${fitscreen\ {[w,h]}},1,100%,3  # Resize to fit screen if necessary.
29336    if !narg($first_time)
29337      parallel 0,"+l[0] r2dy 128 frame 1,1,0 \
29338        alert \"[G"{`39`}"MIC Hough Transform]\",\
29339           \"The G\47MIC Hough transform demo illustrates the application\n\
29340           of the Hough transform to detect lines in an image. Use your\n\
29341           mouse buttons to explore the transform image and see how\n\
29342           lines in images are represented by points in the transform.\",\
29343           \"OK\" \
29344        rm endl"
29345      first_time=0
29346    fi
29347
29348    rhomax={sqrt(w^2+h^2)/2}
29349    +b. 1.5 hough. 512,400 b. 0.5 +. 1 log. n. 0,255
29350    w.. -1,-1,0,"[G"{`39`}"MIC] Image" w1. -1,-1,0,"[G"{`39`}"MIC] Hough Transform"
29351
29352    do
29353      wait
29354
29355      if {*,b} # When clicking on the image.
29356        x0={{*,x}-{*,w}/2}
29357        y0={{*,y}-{*,h}/2}
29358        rho0={sqrt(($x0)^2+($y0)^2)}
29359        theta0={atan2($y0,$x0)}
29360        (0,{2*pi}) ($theta0,{$theta0-2*pi})
29361        r[-2,-1] {-3,w},1,1,1,3
29362        cos. *. $rho0 +<. 0 abs..
29363        *. {pi} +[-3,-1] %.. {2*pi}
29364        *.. {0.5*{-3,w}/pi} *. {{-3,h}/$rhomax}
29365        a[-2,-1] y
29366        repeat w point.. {i($>,0)},{i($>,1)},0,0.3,255 done
29367        rm. w1.
29368
29369      elif {*1,x}>=0" && "{*1,b} # When clicking on the vote window.
29370        theta={{*1,x}*2*pi/{*1,w}}
29371        rho={{*1,y}*$rhomax/{*1,h}}
29372        x={{-2,w}/2+$rho*cos($theta)}
29373        y={{-2,h}/2+$rho*sin($theta)}
29374        x0={$x+1000*sin($theta)}
29375        y0={$y-1000*cos($theta)}
29376        x1={$x-1000*sin($theta)}
29377        y1={$y+1000*cos($theta)}
29378        ..
29379        line. $x0,$y0,$x1,$y1,1,0x0F0F0F0F,255
29380        line. {$x0+1},$y0,$x1,$y1,1,0x0F0F0F0F,255
29381        line. $x0,{$y0+1},$x1,$y1,1,0x0F0F0F0F,255
29382        line. $x0,$y0,$x1,$y1,1,0xF0F0F0F0,0
29383        line. {$x0+1},$y0,$x1,$y1,1,0xF0F0F0F0,0
29384        line. $x0,{$y0+1},$x1,$y1,1,0xF0F0F0F0,0
29385        w. rm.
29386
29387      elif {*,SPACE}" || "{*1,SPACE}
29388        rm. +b. 1.5 hough. 512,400 b. 0.5 +. 1 log. n. 0,255
29389        w1. -1,-1,0,"Hough Transform"
29390
29391      elif {*,r} w..
29392      elif {*1,r} w1.
29393      fi
29394
29395    while {*}" && "{*1}" && "!{*,ESC}" && "!{*,Q}" && "!{*1,ESC}" && "!{*1,Q}
29396    w 0 w1 0
29397    rm. endl
29398    if !{*}" || "!{*1} break fi
29399  done rm
29400
29401#@cli x_jawbreaker : 0<_width<20,0<_height<20,0<_balls<=8
29402#@cli : Launch the Jawbreaker game.
29403x_jawbreaker : check "${1=12}>0 && $1<20 && ${2=13}>0 && $2<20 && ${3=5}>0 && $3<=8" check_display $0
29404  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29405  e[] "\n
29406------ "${g}"Jawbreaker"$n" --------------------------------------------\n
29407----\n
29408---- The goal of the game is to "${c}"remove the maximum number of\n
29409---- balls on the board"$n", simply by clicking on them. But a\n
29410---- colored ball can disappear only if it is grouped with at\n
29411---- least one ball of the same color. The score is higher if\n
29412---- you destroy larger sets of connected colored balls.\n
29413----\n
29414---- "${c}"Left mouse button"$n" to select/destroy balls on board.\n
29415---- Key '"${c}"BACKSPACE"$n"' or '"${c}"SPACE"$n"' to undo the last move.\n
29416---- Key '"${c}"S"$n"' to save snapshot of the current view.\n
29417---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29418----\n
29419--------------------------------------------------------------"
29420
29421  # Init images and variables.
29422  $1,$2 nm. board rand[board] 1,$3 round[board] 1
29423  . nm. undo
29424  40,40,1,4 nm. balls _x_jawbreaker_ball.
29425  autocrop. 0 expand_xy. 1,0 *. 1.5 c. 0,255 r. {{board,w}*w},{{board,h}*h},1,1,0,2 /. 255
29426  {w},{h},1,3 nm. back l.
29427    rand 0,255 blur_xy 6,20 equalize 100,0,255 blur_xy 2,4
29428    sh 0 sh.. 1 sh... 2 /... 4 /.. 8 /. 2 rm[-3--1]
29429  endl
29430  [back] nm. visu
29431  score=0
29432  undoscore=0
29433  render_board=1
29434  shapescorey=0
29435  shapescore=0
29436
29437  # Enter user-event loop.
29438  do
29439
29440    # Render board graphics.
29441    if $render_board
29442      +abs[board] -. 1 *. {360/$3} +>=[board] 0 *. 0.9 +!=[board] 0
29443      ri[-3--1] [balls] [balls] *[-2,-1] a[-3--1] c hsv2rgb.
29444      +compose_channels. + >. 0 dilate. 3
29445      j[visu] [back] j[visu] ..,0,0,0,0,1,. rm[-2,-1]
29446      if !$shapescorey w[visu] {back,w},{back,h},0,"[G"{`39`}"MIC] Jawbreaker (Score : "$score")" fi
29447      render_board=0
29448    fi
29449
29450    # Add shape score sprite if necessary.
29451    if $shapescorey
29452      +t[visu] "+"$shapescore,{*,x},{{*,y}-64+$shapescorey},32,{($shapescorey-1)/31},255
29453      shapescorey={max(0,$shapescorey-1)}
29454      w. {back,w},{back,h},0,"[G"{`39`}"MIC] Jawbreaker (Score : "$score")" rm. wait 25
29455    else wait fi
29456
29457    # Check for the end of the game.
29458    +f[board] "if(i,j(-1)==i || j(1)==i || j(0,1)==i || j(0,-1)==i,0)"
29459    if !is rm. break fi rm.
29460
29461    # Manage user-events
29462    if {*,r} render_board=1                 # Will resize window to initial size, if resized.
29463    elif {*,S} o[visu] gmic_jawbreaker.png  # Save snapshot if requested.
29464    elif {*,BACKSPACE}" || "{*,SPACE}       # Manage undo move.
29465      abs[undo] j[board] [undo]
29466      score=$undoscore
29467      render_board=1
29468
29469    elif {*,x}>=0" && "{*,b}                # Manage button click.
29470
29471      # Retrieve board coordinates.
29472      wait -1
29473      x={"int("{*,x}"*"{board,w}"/"{*,w}")"}
29474      y={"int("{*,y}"*"{board,h}"/"{*,h}")"}
29475
29476      # When selecting a ball -> display selection and init new shape score sprite.
29477      if {{board,i($x,$y)}>0}
29478        abs[board] flood[board] $x,$y,0,0,0,1,-{board,i($x,$y)}
29479        +>=[board] 0 -. 1
29480        shapescore={(is+1)^2} shapescorey={if($shapescore,32,0)} rm.
29481
29482      # When confirming selection of a ball -> remove set of connected balls.
29483      elif {board,i($x,$y)}
29484
29485        +flood[board] $x,$y,0,0,0,1,-1 ==. -1
29486        if is>1 # If selected ball is connected to at least one ball.
29487
29488          # Save undo state.
29489          j[undo] [board]
29490          undoscore=$score
29491
29492          # Manage board shifts (vertical and horizontal).
29493          flood[board] $x,$y,0,0,0,1,0
29494          repeat w#$board
29495            +columns[board] $> mirror. y
29496            h={board,h} l. s -,0 a y if $! r 1,$h,1,1,0 mirror y else i 1,$h fi endl
29497            j[board] .,$> rm.
29498          done
29499          rows[board] -1,100% f[board] "if(y==0,if(i(x,h-1),x,w),i)" sort[board] +,x rows[board] 1,100%
29500
29501          # Update score.
29502          score+={int((is-1)^2)}
29503
29504        fi
29505        rm. # Remove selection mask.
29506
29507      else abs[board]  # Remove previous selection if clicked outside balls.
29508      fi
29509
29510      render_board=1
29511    fi
29512
29513  while {*}" && "!{*,Q}" && "!{*,ESC}
29514
29515  # Game over.
29516  if {*}" && "!{*,ESC}
29517    w[] {visu,w},{visu,h},0,"[G"{`39`}"MIC] Jawbreaker (Final Score : "$score")"
29518    260,85 nm. gameover t. "Game Over!",3,0,53,1,1 t. "Score : "$score,23,53,32,1,1
29519    +dilate. 5 nm. "mgameover" *.. 255 r.. 100%,100%,1,3
29520    repeat 25
29521      +r[gameover,mgameover] {400-12*($>+1)}%,{400-12*($>+1)}%
29522      +j[visu] ..,{({visu,w}-w)/2},{({visu,h}-h)/2},0,0,{$>/25},.
29523      w. rm[-3--1] wait 25
29524    done
29525    do
29526      wait if {*,r} w[] {*,w},{*,h} wait -1 fi
29527      while {*}" && "!{*,Q}" && "!{*,ESC}" && "!{*,b}
29528    rm[gameover,mgameover]
29529  fi
29530
29531  # End properly.
29532  rm[board,undo,balls,back,visu]
29533  w 0
29534
29535_x_jawbreaker_ball :
29536  mwh={min(w,h)}
29537  sh 3 f. 0 rm.
29538  ellipse {0.5*$mwh},{0.5*$mwh},{0.5*$mwh-4},{0.5*$mwh-4},0,1,240,240,240,1
29539  sh 0,2 *. '($mwh+y-x)/(2*w)' rm.
29540  sh 3 *.. . dilate. 5 rm.
29541  sh 0,2 +. 'if(i&&(!j(-1)||!j(1)||!j(0,-1)||!j(0,1)),240/6,0)' rm.
29542  ellipse {$mwh*0.7},{$mwh*0.3},{min(30,$mwh*$mwh/512)},{min(30,$mwh*$mwh/512)},0,{min($mwh/64,1)},255,255,255,1
29543
29544#@cli x_landscape
29545#@cli : Launch the virtual landscape demo.
29546x_landscape : check_display $0
29547  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29548  e[] "\n
29549------ "${g}"Virtual landscape"$n" -------------------------------------\n
29550----\n
29551---- Enjoy the view!\n
29552----\n
29553---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
29554---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
29555---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29556----\n
29557--------------------------------------------------------------"
29558  l[]
29559  W=150 H=350
29560
29561  # Generate global map + colors.
29562  900,900 plasma. 1,1,6 b. 0.07% n. 0,255 nm. map
29563  +g. *. 0.5 +[-2,-1] n. 0,1 ^. 2 n. -150,330
29564  equalize[map] 256 n[map] -400,160 c[map] 0,100% # Add water.
29565  (0,102,51;149,175,124;102,42,0;255,255,255) permute. yzcx srgb2rgb. r. 256,1,1,3,3 rgb2srgb.
29566  +n[map] 0,255 map. .. rm..
29567  +. .. rm.. c. 0,255 nm. colors # Colors.
29568
29569  # Pre-compute some images used on each frame.
29570  $W,$H,1,1,'x' y. x nm. x # Increasing x.
29571  $W,$H,1,1,'1+x+y*w' y. x nm. offsets # Offsets (+1).
29572  $W,$H,1,1,'0.5*y' nm. gmap Mgmap={iM} # Z-increment for altitude map.
29573  $W,$H,1,3 fc. 60,80,135 nm. ccolors # Color for the horizon.
29574  $W,$H,1,1,'(y/$H)^2' nm. mcolors # Mask for the horizon.
29575  $W,400,1,1,'b=h-1-$Mgmap;if(y>=b,256+(y-b)*255/(h-1-b),y*255/b)' round. # Background.
29576
29577  (96^16^128) (0^200^255) a[-2,-1] x r. 256,1,1,3,3
29578  (0^32^0) (0^64^128) a[-2,-1] x r. 256,1,1,3,3
29579  a[-2,-1] x map.. . rm.
29580
29581  nm. background
29582  quadrangle3d[] -0.45,0,0,0.45,0,0,0.55,1,0,-0.55,1,0 *3d. {$W/2},{$H/2} nm. viewrange3d # View range.
29583  (64^16^0) r. $W nm. groundcolor # Ground color.
29584
29585  # Start animation.
29586  w[] 600,400,0,"[G"{`39`}"MIC] Virtual Landscape"
29587  do
29588
29589    # Get part of the map to display.
29590    t={$|*0.03}
29591    xm={map,w/2+(w-$H/2)/2*cos(3.1*$t)}
29592    ym={map,h/2+(h-$H/2)/2*sin(2.8*$t)}
29593    u={map,(w-$H/2)*cos(2.5*$t)}
29594    v={map,(h-$H/2)*sin(9.7*$t)}
29595    a={atan2($v,$u)*180/pi}
29596    +r3d[viewrange3d] 0,0,1,$a y. x
29597    ({$xm+i[8]},{$xm+i[11]};{$xm+i[17]},{$xm+i[14]}^{$ym+i[9]},{$ym+i[12]};{$ym+i[18]},{$ym+i[15]}) rm..
29598    r. $W,$H,1,2,3 +warp[map,colors] .,0,1,0 rm...
29599    nm.. lmap nm. lcolors
29600
29601    # Add color shading and altitude to local maps.
29602    +!=[lmap] 0 nm. ground
29603    +[lmap] [gmap]
29604    j[lcolors] [ccolors],0,0,0,0,1,[mcolors]
29605    j[lcolors] [groundcolor]
29606    +round[lmap] f. '>m=abs(j(0,-1));i>m?i:-m' nm. y0  # Compute visible top points.
29607    +shift. 0,1 abs. +. 1 nm. y1 # Compute visible bottom points.
29608    *[y0,y1] [ground] rm[ground]
29609    r[lcolors,y0,y1] {$W*$H},1,1,100%,-1
29610
29611    # Keep only visible primitives.
29612    +>[y0] 0 *. [offsets] discard. 0 y.
29613    if h # There is something to display (ground).
29614      -. 1 +warp[x] .,0,0,0 nm. lx
29615      warp[lcolors,y0,y1] ..,0,0,0 rm..
29616
29617      # Generate 3D object.
29618      N={h} ({'CImg3d'},{2*$N},$N)
29619      +a[lx] [y0],x rm[y0] +a[lx] [y1],x rm[lx,y1] a[-2,-1] y z. 0,2
29620      1,$N,1,1,2 +f. y ++. $N a[-3--1] x
29621      mv[lcolors] $! permute. cyzx
29622      1,$N,1,1,1
29623      y[-5--1] y a[-5--1] y *3d. -1,-1
29624      +j3d[background] .,{background,w-1},{background,h},0,1,1,0,0,0 rm[-3,-2]
29625    else # Case of nothing to display (only water).
29626      rm[-5--1] [background]
29627    fi
29628
29629    r. {*,w},{*,h},1,3
29630    fps=${-fps} if $fps>0 to. $fps" fps",5,5,24,2,0.2 fi
29631    w. -1,-1,0 rm.
29632    if {*,CTRLLEFT}" && "{*,D} w[] 900,600 elif {*,CTRLLEFT}" && "{*,C} w[] 600,400 fi
29633    wait 20
29634  while {*}" && "!{*,ESC}" && "!{*,Q}
29635  rm w 0 endl
29636
29637#@cli x_life
29638#@cli : Launch the game of life.
29639x_life : check_display $0
29640  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29641  e[] "\n
29642------ "${g}"The game of life"$n" --------------------------------------\n
29643----\n
29644---- The goal is to create the "${c}"biggest possible biological\n
29645---- system"$n". You start with a stock of cells which you can\n
29646---- spread over the board. For each new cells created\n
29647---- simultaneously and spontaneously by your system, you\n
29648---- gain more new cells to scatter.\n
29649----\n
29650---- "${c}"Left mouse button"$n" to scatter cells in stock.\n
29651---- "${c}"Right mouse button"$n" to reset game.\n
29652---- Key '"${c}"S"$n"' to save snapshot of the current view.\n
29653---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29654----\n
29655--------------------------------------------------------------"
29656
29657  i[0] 90,90,1,1,0                                             # Image[0] = game state.
29658  i[1] [0] f[1] 0                                              # Image[1] = generation counter.
29659  i[2] 400,400,1,3                                             # Image[2] = visualization.
29660  i[3] 1                                                       # Image[3] = colormap (to be initialized).
29661  iteration=0                                                  # Iteration counter.
29662  score=0                                                      # Current score.
29663  bestscore=0                                                  # Best score.
29664  stock=500                                                    # Remaining cells.
29665  w[0] 400,400,0,"[G"{`39`}"MIC] The Game of Life"             # Initialize display window.
29666  cursor[0] 0
29667
29668  # Start user-event loop.
29669  do
29670    (1,1,1;1,0,1;1,1,1) +correlate[0] .,0 rm..                # Count numbers of neighboring living cells.
29671    +ir. 2,2 &. [0] ir.. 3,3 -|[-2,-1]                        # Make the game evolve (kill or create cells).
29672    rv[0,-1]                                                  # Update game state.
29673    if {*,x}>0" && "{*,b}==1" && "$stock>0                    # Add random cells to the game if mouse button pressed.
29674      nb={u*7}
29675      repeat $nb
29676        x={{*,x}/{*,w}*{0,w}+u(-4,4)}
29677        y={{*,y}/{*,h}*{0,h}+u(-3,3)}
29678        =[0] 1,$x,$y
29679        =[1] $iteration,$x,$y
29680        point[2] {$x*{2,w}/{0,w}},{$y*{2,h}/{0,h}},0,0.8,255
29681      done
29682      stock={round(max(0,$stock-$nb))}
29683    fi
29684
29685    -. [0] *. -1                                              # Compute difference between consecutive states.
29686    stock-={2*(min(0,int(is/16*$score/150)))}                 # Increment available cells if the evolution is fast.
29687    +[1] [0]                                                  # Increment generation counter for still existing cells.
29688    min. 0 +. 1 *[1,-1]                                       # Reset generation counter for died cells.
29689
29690    if {*,b}==2                                               # Reset game if right mouse button has been pressed.
29691      f[0-2] 0 iteration=0 score=0 bestscore=0 stock=500 rm[3] i[3] 1
29692    fi
29693
29694    if {3,w}==1                                               # Create color palette if necessary.
29695      rm[3] i[3] {u(3,12)},1,1,3,u(100,255)
29696      r[3] {u(100,300)}%,1,1,3,4
29697      point[3] 0,0,0,1,0
29698      r[3] {u(100,600)}%,1,1,3,5 c[3] 0,255
29699    fi
29700
29701    +r[1] {2,w},{2,h} &. 7 b. {1+$score*0.05}                  # Render colored image of the game and display it.
29702    n. 0,{3,w} map. [3] *. 0.1 +[2,-1] /[2] 1.1
29703    [2] if {*,x}>0                                             # Add a small target icon at the mouse position.
29704      opac={0.7*min(1,$stock/500)} r={min(500,$stock)*cos($iteration)/100}
29705      ellipse. {*,x},{*,y},{15+$r},{15+$r},0,$opac,0,196,0
29706      ellipse. {*,x},{*,y},{10+$r},{10+$r},0,$opac,32,64,16
29707      ellipse. {*,x},{*,y},{5+$r},{5+$r},0,$opac,255,230,0
29708    fi
29709    t. "Living cells : "$score"\n"\                            # Add score description.
29710       "Stock : "$stock"\n"\
29711       "Score : "$bestscore,5,3,22,0.7,255
29712    w. {*,w},{*,h}
29713    if {*,S} o. gmic_life.png fi                               # Save snapshot if requested.
29714    rm.
29715
29716    if !($iteration%10) # Re-compute current and best scores, every 10th iterations
29717      score={0,is} bestscore={max($score,$bestscore)}
29718    fi
29719    wait 60
29720    iteration+=1
29721  while {*}" && "!{*,ESC}" && "!{*,Q}
29722
29723  # End game and quit properly.
29724  rm[0-3] w 0
29725
29726#@cli x_light
29727#@cli : Launch the light effect demo.
29728x_light : check_display $0
29729  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29730  e[] "\n
29731------ "${g}"Light effect"$n" ------------------------\n
29732----\n
29733---- Move light position with "${c}"mouse"$n".\n
29734---- "${c}"Mouse buttons"$n" fade light in/out.\n
29735---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
29736---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
29737---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29738----\n
29739-------------------------------------------"
29740
29741  # Create warping and color images.
29742  0 t. "    G\47MIC\nLight effect",0,0,80,1,255 expand_xy. 15,0 b. 3
29743  . n.. 0,1 r.. 100%,100%,1,3
29744  sh.. 0 *. 120 rm.
29745  sh.. 1 *. 70 rm.
29746  sh.. 0,50%,0,2 *. 120 rm.
29747  25%,25%,1,1 rand. -20,20 smooth. 10,0,1,1,4 ri. ..,3 b. 3 n. -100,100
29748  +[-2,-1] g. xy a[-2,-1] c n. -150,150
29749  w[] {1.5*{-2,w}},{1.5*{-2,h}},0,"[G"{`39`}"MIC] Light Effect"  # Init display window.
29750  cursor[0] 0
29751
29752  # Create a large light image.
29753  light=70
29754  640,640 gaussian. $light n. 0,255
29755  t=0
29756
29757  # Start animation.
29758  do
29759
29760    # Manage light position and intensity.
29761    if {*,x}>=0
29762      X={round((w-{*,x})/2)}
29763      Y={round((h-{*,y})/2)}
29764    else
29765      X={round((w-{-2,w}*(1+cos(2*$t)))/2)}
29766      Y={round((h-{-2,h}*(1+sin(2.5*$t)))/2)}
29767      t+=0.02
29768    fi
29769    if {*,b}&1 light={min(200,$light+10)} gaussian. $light n. 0,255 fi
29770    if {*,b}&2 light={max(10,$light-10)} gaussian. $light n. 0,255 fi
29771
29772    # Render lightened image.
29773    +z. $X,$Y,{$X+{-2,w}-1},{$Y+{-2,h}-1}
29774    warp. ...,1,0,1
29775    r. 100%,100%,1,3 +. [-4] c. 0,255
29776    fps=${-fps} if $fps>0 to. $fps" fps",5,5,16,1,0.2 fi
29777    w.
29778    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
29779    rm. if {*,x}>=0" && "!{*,b} wait else wait 20 fi
29780  while {*}" && "!{*,ESC}" && "!{*,Q}
29781  w[] 0 rm[-3--1]
29782
29783#@cli x_mandelbrot : _julia={ 0 | 1 },_c0r,_c0i
29784#@cli : Launch Mandelbrot/Julia explorer.
29785x_mandelbrot : skip ${1=0},${2=0.317},${3=0.03} check_display $0
29786  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29787  e[] "\n
29788------ "${g}"Mandelbrot/Julia explorer"$n" -----------------\n
29789----\n
29790---- Select zooming region with "${c}"mouse"$n".\n
29791---- "${c}"Click once"$n" to reset zoom factor.\n
29792---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29793---- Key '"${c}"C"$n"' to print current fractal coordinates.\n
29794----\n
29795--------------------------------------------------"
29796
29797  # Init variables and display.
29798  rm w 512,512,0 _x_mandelbrot_coords $1 _x_mandelbrot_palette
29799
29800  # Start event loop.
29801  do
29802    siz={min({*,w},{*,h})}                                                                  # Desired window dimension.
29803    $siz,$siz mandelbrot. {0,^},256,$1,{if($1,$2,0)},{if($1,$3,0)} map. [1]                 # Render fractal.
29804    if $1 w. $siz,$siz,0,"[G"{`39`}"MIC] Julia Set c=("{0,@0-1}")-("{0,@2-3}"), c0=($2,$3)" # Display on window.
29805    else w. $siz,$siz,0,"[G"{`39`}"MIC] Mandelbrot Set c=("{0,@0-1}")-("{0,@2-3}")" fi
29806    w={w} h={h} round. select. 2,0,0,0,1                                                    # Get the user selection.
29807    if i[0]>0                                                                               # If valid selection found.
29808      M={max(i[3]-i[0],i[4]-i[1])} # Compute max dimension of selected rectangle.
29809      if $M<5 _x_mandelbrot_coords $1 rm[1] _x_mandelbrot_palette mv. 1 # If selection too small, reset the view,
29810      else ({{0,@0}+{@0}*({0,@2}-{0,@0})/$w};\                          # Else compute new fractal coordinates.
29811             {{0,@1}+{@1}*({0,@3}-{0,@1})/$h};\
29812             {{0,@0}+({@0}+$M)*({0,@2}-{0,@0})/$w};\
29813             {{0,@1}+({@1}+$M)*({0,@3}-{0,@1})/$h})
29814      fi
29815      rm[0] mv. 0                                                           # Validate new coordinates.
29816    fi
29817    rm.                                                                     # Delete latest rendering.
29818    if {*,C}                                                                # If 'C' key has been pressed.
29819      if $1 e[] "Julia set, at c = ("{0,@0-1}")-("{0,@2-3}"), with c0 = ($2,$3)."
29820      else e[] "Mandelbrot set, at c = ("{0,@0-1}")-("{0,@2-3}")."
29821      fi
29822    fi
29823    if !{*}" || "{*,ESC}" || "{*,Q} break fi
29824    wait -1
29825  while 1 rm w 0
29826
29827_x_mandelbrot_coords :
29828  if $1 (-2;-2;2;2) else (-2.1;-1.5;1.2;1.5) fi
29829
29830_x_mandelbrot_palette :
29831  6,1,1,3 rand. 20,255 r. 32,1,1,3,3 r. 1024,1,1,3,0,2 =. 0,0,0,0,0 =. 0,0,0,0,1 =. 0,0,0,0,2
29832
29833#@cli x_mask_color : _colorspace={ all | rgb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq },\
29834# _spatial_tolerance>=0,_color_tolerance>=0
29835#@cli : Interactively select a color, and add an alpha channel containing the corresponding color mask.
29836#@cli : Argument 'colorspace' refers to the color metric used to compute color similarities, and can be basically
29837#@cli : one of { rgb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq }.
29838#@cli : You can also select one one particular channel of this colorspace, by setting 'colorspace' as
29839#@cli : 'colorspace_channel' (e.g. 'hsv_h' for the hue).
29840#@cli : Default values: 'colorspace=all', 'spatial_tolerance=5' and 'color_tolerance=5'.
29841x_mask_color : check "${2=5}>=0 && ${3=5}>=0" skip ${1=all} check_display $0
29842  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29843  e[^-1] "Interactively create color mask for image$?, with color space $1, spatial tolerance $2 and
29844          color tolerance $3."
29845  e[] "\n
29846----------------------------------------------------------------------------------------------------\n
29847----\n
29848---- "${c}"Left mouse button"$n" adds a wanted color to the selection.\n
29849---- "${c}"Right mouse button"$n" adds an unwanted colors to the selection.\n
29850---- "${c}"Middle mouse button"$n" or key '"${c}"R"$n"' resets color mask.\n
29851---- Key '"${c}"SPACE"$n"' or '"${c}"TAB"$n"' toggles view modes (masked RGB or mask).\n
29852---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
29853---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
29854---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
29855---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n
29856----\n
29857----------------------------------------------------------------------------------------------------"
29858  l[] _ac_$1 onfail error[0--3] "Command '$0' : Invalid colorspace '$*'." endl
29859  m _ac_forward:$_f
29860  repeat $! l[$>] slices 0 basename {0,n} nm=${}
29861    wh=${fitscreen\ {[w,h,1]},128,1024}
29862    +r $wh,1,100%,2
29863    +_ac_forward. channels. $_s
29864    if {1,s>3} channels[1] 0,2 fi to_rgb[1]
29865
29866    w[1] 100%,100%,0,$nm
29867    colors_add=-1 colors_sub=-1 visumode=0
29868    is_clicked=0 time=0 delay=0.1
29869    do
29870
29871      # Manage user events.
29872      time={$is_clicked?$time:$|}
29873      wait
29874      x={2,round({*,x}*(w-1)/({*,w}-1))}
29875      y={2,round({*,y}*(h-1)/({*,h}-1))}
29876      b={*,b}
29877      c=$x,$y,{2,I($x,$y)}
29878      is_add={arg(1,$colors_add)>=0}
29879      is_sub={arg(1,$colors_sub)>=0}
29880      is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
29881      is_resized=0
29882      refresh=0
29883
29884      if $x>=0" && "$b&1 # Add a color
29885        if $is_add colors_add=$colors_add,$c else colors_add=$c fi
29886        is_clicked=1
29887        refresh={$|-$time>$delay}
29888      elif $x>=0" && "$b&2 # Subtract a color
29889        if $is_sub colors_sub=$colors_sub,$c else colors_sub=$c fi
29890        is_clicked=1
29891        refresh={$|-$time>$delay}
29892      elif $b&4" || "{*,R} # Reset colors
29893        colors_add=-1 colors_sub=-1 refresh=1 is_clicked=1
29894      elif !$b
29895        refresh={$is_clicked==1}
29896        is_clicked=0
29897      fi
29898      if {*,-TAB}" || "{*,-SPACE} visumode={($visumode+1)%3} refresh=1 fi # Change visu mode
29899      if {*,r} is_resized=1
29900      elif $is_ctrl" && "{*,-D} w[] {1,1.25*[w,h]} is_resized=1
29901      elif $is_ctrl" && "{*,-C} w[] {1,0.8*[w,h]} is_resized=1
29902      elif $is_ctrl" && "{*,R} w[] ${fitscreen\ {0,[w,h,1]},128,1024} is_resized=1
29903      fi
29904      if $is_resized rm[1,2] +r {*,d},{*,e},1,3,2 +_ac_forward. channels. $_s refresh=1 fi
29905
29906      # Refresh view.
29907      if $refresh
29908        _x_mask_color[2] {$2*w#2/w#0},$3,{``$colors_add},{``$colors_sub} delay=${}
29909        if $visumode==0 +. 64 c. 0,255 +a[1,-1] c drgba. w. -1,-1,$nm" [half-masked]" rm.
29910        elif $visumode==1 +a[1,-1] c drgba. w. -1,-1,$nm" [masked]" rm.
29911        else w. -1,-1,$nm" [mask]"
29912        fi
29913        rm.
29914        time=$|
29915      fi
29916
29917    while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER}
29918
29919    # Rescale to original image dimensions.
29920    if arg(1,$colors_add)>=0 ($colors_add)
29921      r. {2+s#2},{w/(2+s#2)},1,1,-1 +z. 0,1 *. '{(w#0-1)/(w#2-1)},{(h#0-1)/(h#2-1)}'
29922      j.. . colors_add={-2,^} rm[-2,-1]
29923    fi
29924    if arg(1,$colors_sub)>=0 ($colors_sub)
29925      r. {2+s#2},{w/(2+s#2)},1,1,-1 +z. 0,1 *. '{(w#0-1)/(w#2-1)},{(h#0-1)/(h#2-1)}'
29926      j.. . colors_sub={-2,^} rm[-2,-1]
29927    fi
29928    rm[-2,-1] +_ac_forward channels. $_s
29929    _x_mask_color. $2,$3,{``$colors_add},{``$colors_sub}
29930    rm.. a c
29931
29932  endl done
29933  um _ac_forward
29934
29935_x_mask_color : # Estimate color mask image.
29936  100%,100%
29937  is_add={arg(1,$3)>=0}
29938  is_sub={arg(1,$4)>=0}
29939  t0=$|
29940  if $is_add" || "$is_sub
29941    if $is_add
29942      ($3) r. {2+s#0},{w/(2+s#0)},1,1,-1
29943      N_add={h} M_add={"M = vectorw(); fill(M,k,med(crop(k,1)));M"} rm.
29944    fi
29945    if $is_sub
29946      ($4) r. {2+s#0},{w/(2+s#0)},1,1,-1
29947      N_sub={h} M_sub={"M = vectorw(); fill(M,k,med(crop(k,1)));M"} rm.
29948    fi
29949    f. "begin(
29950          invert(A,nA) = inv(A,nA); # For <v.2.9.2
29951          const is_add = "$is_add";
29952          const is_sub = "$is_sub";
29953          const ss = sqrt(2)*$1;
29954          const sc = sqrt(2)*$2;
29955          colors_add = [ $3 ];
29956          colors_sub = [ $4 ];
29957          M_add = [ 0"$M_add"];
29958          M_sub = [ 0"$M_sub"];
29959          const N_add = 0"$N_add";
29960          const N_sub = 0"$N_sub";
29961          const siz = 2 + s#0;
29962          const siz2 = sqr(siz);
29963          sigma = vectorsiz(sc);
29964          sigma[0] = sigma[1] = ss;
29965
29966          tensor(op) = (
29967            T = vectorsiz2();
29968            if (is_#op,
29969              for (k = 0, k<size(colors_#op), k+=siz, C = colors_#op[k,siz]-=M_#op; C*=sigma; T+=mul(C,C,siz));
29970              T/=1e-8 + N_#op;
29971              T+=eye(siz);
29972              T = invert(T);
29973            ); T
29974          );
29975
29976          T_add = tensor(add);
29977          T_sub = tensor(sub);
29978        );
29979        P = [ x,y,I#0 ];
29980        pot_add = is_add?(C = P - M_add; exp(-dot(C,T_add*C))):1;
29981        pot_sub = is_sub?(C = P - M_sub; exp(-dot(C,T_sub*C))):0;
29982        pot_add - pot_sub"
29983    c. 0,1 n. 0,512 c. 0,255
29984  else f. 255
29985  fi
29986  u {$|-$t0}
29987
29988#@cli x_metaballs3d
29989#@cli : Launch the 3D metaballs demo.
29990x_metaballs3d : check_display $0
29991  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29992  e[] "\n
29993------ "${g}"3D metaballs"$n" ---------------------------------------\n
29994----\n
29995---- "${c}"Mouse button"$n" or '"${c}"SPACE"$n"' key to toggle rendering mode.\n
29996---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
29997---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
29998---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29999----\n
30000-----------------------------------------------------------"
30001  l[]
30002  100,100 noise. 100,1 plasma. 1,0,10 r. 512,320,1,3 n. 0,1 b. 4,0 n. 0,255
30003  mix_channels. (0.7,0,0;0,0.9,0;0,0,1.2) c. 0,255 l3d
30004  0
30005  24,24,24,1,'X=x-w/2;Y=y-h/2;Z=z-d/2;exp(-(X*X+Y*Y+Z*Z)/100)'
30006  72,72,72 M=8 mode=3
30007  s0=Dots s1=Wireframe s2=Flat s3=Flat-shaded s4=Gouraud-shaded s5=Phong-shaded
30008  repeat $M fx$>={2*g} fy$>={2*g} fz$>={2*g} done
30009  w[0] -1,-1,0,"[G"{`39`}"MIC] 3D Metaballs"
30010  do
30011    repeat $M
30012      x$>={w/2+0.5*(w-{2,w}-4)*cos(${fx$>}*$|)}
30013      y$>={h/2+0.5*(h-{2,h}-4)*sin(${fy$>}*$|)}
30014      z$>={d/2+0.5*(d-{2,d}-4)*sin(${fz$>}*$|)}
30015    done
30016    f[3] 0 repeat $M j[3] [2],{${x$>}-{2,w/2}},{${y$>}-{2,h/2}},{${z$>}-{2,d/2}},0,-1 done
30017    +r[3] 28,28,28,1,2 isosurface3d. 0.4 -3d. 12,12,12 *3d. 13 rv3d.
30018    r3d. 1,2,1,{100*$|}
30019    N={i[7]} (255,255,150;200,96,164;50,150,230) r. 3,$N,1,1,3 y. j.. .,0,{{-2,h}-4*$N} # Do some color tweaks.
30020    if !$mode circles3d.. 4 fi
30021    if !{1,w}
30022      0 t. ${s$mode},5,5,23,0.5,255,255,255 r. {w+5},100%,1,3,0 b. 0.7 n. 0,255
30023      +dilate. 3 +j[0] ..,5,3,0,0,1,.,255 mv. 1 rm[2,-2,-1]
30024    fi
30025    +j3d[1] ..,50%,50%,0,1,{if(!$mode,3,$mode)},0,0,300,0,0,-500,0.1,1.5
30026    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,2,0.2 fi
30027    w.
30028    if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
30029    rm[-3--1] wait 20
30030    if {*,b}" || "{*,SPACE} mode={($mode+if({*,b}&2,-1,1))%6} wait -1 rm[1] i[1] 0 fi
30031  while {*}" && "!{*,ESC}" && "!{*,Q}
30032  rm w 0 endl
30033
30034#@cli x_minesweeper : 8<=_width=<20,8<=_height<=20
30035#@cli : Launch the Minesweeper game.
30036x_minesweeper : check "${1=20}>=8 && $1<=30 && ${2=$1}>=8 && $2<=30" check_display $0
30037  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30038  e[] "\n
30039------ "${g}"Minesweeper"$n" -------------------------------------------\n
30040----\n
30041---- The goal is to "${c}"clear the minefield"$n" without detonating a\n
30042---- mine.\n
30043----\n
30044---- "${c}"Left mouse button"$n" to try clearing one square.\n
30045---- "${c}"Right mouse button"$n" to flag or unflag a square.\n
30046---- "${c}"Middle mouse button"$n" to reset mine field.\n
30047---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30048----\n
30049--------------------------------------------------------------\n"
30050
30051  # Generate random mine field and player board.
30052  # Labels : 0=mine, 1=empty, 2='1-near', 3='2-near', ..., 9='8-near', 10=still unknown.
30053  $1,$2 noise. 30,2 ==. 1 nb_mines={is} (1,1,1;1,0,1;1,1,1) +convolve.. .,0 rm.. +. 1 ==.. 0 *[-2,-1] nm. field
30054  do x={round(u(w-1))} y={round(u(h-1))} while i($x,$y)!=1 # Find a good starting point.
30055  +f[field] 11 =. 12,$x,$y nm. board
30056
30057  # Generate sprite graphics.
30058  24,24,1,3,200 fc. 255,180,130
30059  ellipse. 12,12,4,4 line. 6,12,18,12 line. 12,6,12,18 line. 13,10,14,10,1,255 line. 13,11,14,11,1,255
30060  z. 1,1,{w-2},{h-2} frame. 1,1,0
30061  +fc. 230,250,255
30062  +t. "1",10,5,13,1,0,196,0 +t.. "2",9,5,13,1,0,128,0 +t... "3",9,5,13,1,0,0,255
30063  +t[-4] "4",9,5,13,1,255,0,0 +t[-5] "5",9,5,13,1,200,0,0 +t[-6] "6",9,5,13,1,150,0,0
30064  +t[-7] "7",9,5,13,1,128,0,0 +t[-8] "8",9,5,13,1,64,0,0
30065  +f. 'if(x<=1||y<=1||x>=w-2||y>=h-2,if(x<y,128,255),160+2*(y+x))'
30066  +polygon. 4,13,15,11,15,6,18,17,18,1,0 line. 12,15,12,6,1,255,0,0 polygon. 3,12,6,6,9,12,12,1,220,0,0
30067  rv[-2,-1]
30068  . 100%,100%,1,3 line. 6,14,10,18,1,0,200,0 line. 10,18,16,6,1,0,200,0 dilate. 2
30069  +channels. 1 n. 0,0.7 dilate. 3 j... ..,0,0,0,0,1,. rm[-2,-1]
30070  a[-13--1] x nm. sprites
30071
30072  # Pre-calculate offsets and canvas for faster board rendering.
30073  (0,23;0,23^0,0;23,23) r. 24,24,1,2,3 r. {board,w*24},{board,h*24},1,2,0,2 nm. offsets
30074  .,.,1,3,255 frame. 1,1,0 frame. 23,23,255
30075  0 t. "Number of mines : "$nb_mines,0,0,18,1,100,200,255 negate. j.. .,{({-2,w}-w)/2},{{-2,h}-h-2} rm.
30076  nm. canvas
30077
30078  # Start user interaction loop.
30079  failed=0 succeeded=0 nb_flags=0 started=0
30080  do
30081
30082    # Render board.
30083    +*[board] 24 r. [offsets],[offsets] channels. 0,1 +. [offsets] +warp[sprites] .,0,0 rm..
30084    j[canvas] .,24,24 rm.
30085
30086    # Wait for user's selection.
30087    wait -1
30088    if $failed
30089      0 t. "Game\nOver!",3,3,38,1,255 r. 100%,100%,1,4 sh. 3 dilate. 5 /. 2 rm.
30090      drop_shadow. 5,5,1 blend[canvas,-1] alpha
30091      0 t. "Boom! You failed!",0,0,18,1,100,255,255 r. {canvas,w},100%,1,3,0,0,0.5,0.5 negate. j[canvas] .,0,3 rm.
30092      do w[canvas] {w},{h} wait while {*}" && "!{*,ESC}" && "!{*,Q}
30093    elif $succeeded
30094      0 t. "Success!",3,3,38,1,255 r. 100%,100%,1,4 sh. 3 dilate. 5 /. 2 rm.
30095      drop_shadow. 5,5,1 blend[canvas,-1] alpha
30096      0 t. "Congratulations! ("{round($|-$tic)}" s)",0,0,18,1,255,100,255 r. {canvas,w},100%,1,3,0,0,0.5,0.5
30097      negate. j[canvas] .,0,3 rm.
30098      do w[canvas] {w},{h} wait while {*}" && "!{*,ESC}" && "!{*,Q}
30099    else
30100      +==[board] 10 nb_flags={is} rm.
30101      do
30102        if !$started tic=$| fi
30103        0 t. "Elapsed time : "{round($|-$tic)}" s / Flags : "$nb_flags,0,0,18,1,255,200,0
30104        r. {canvas,w},100%,1,3,0,0,0.5,0.5
30105        negate. j[canvas] .,0,3 rm.
30106        wait 50
30107        x={int(({*,x}-24)/24)} y={int(({*,y}-24)/24)} b={*,b}
30108        w[canvas] {w},{h},0,"[G"{`39`}"MIC] Minesweeper"
30109      while {*}" && "!{*,ESC}" && "!{*,Q}" && "!$b
30110    fi
30111
30112    # Manage selected square.
30113    if $x>=0\ &&\ $y>=0\ &&\ $x<{board,w}\ &&\ $y<{board,h}
30114      if $b&1  # Try to clean square.
30115        started=1 val={field,i($x,$y)}
30116        if $val==0 +==[field] 0 j[board] [field],0,0,0,0,1,. rm. failed=1  # Found a mine -> boom!
30117        elif $val==1 # Found an empty area
30118          +flood[field] $x,$y,0,0,1,1,-1 ==. -1 dilate. 3 j[board] [field],0,0,0,0,1,. rm.
30119        else =[board] $val,$x,$y # Close to one or several mines
30120        fi
30121      elif n={board,i($x,$y)};$b&2" && "n>=10" && "n<=11
30122        =[board] {if({board,i($x,$y)}==11,10,11)},$x,$y # Flag or unflag a square.
30123      elif $b&4 f[board] 10  # Reset minefield.
30124      fi
30125    fi
30126
30127    if $nb_mines==$nb_flags\ &&\ {board,iM}!=11 succeeded=1 fi  # Check if board is cleared.
30128  while {*}" && "!{*,ESC}" && "!{*,Q}
30129  w 0
30130
30131#@cli x_minimal_path
30132#@cli : Launch the minimal path demo.
30133x_minimal_path : check_display $0
30134  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30135  e[] "\n
30136------ "${g}"Minimal path"$n" ------------------------------------------\n
30137----\n
30138---- "${c}"Click on two points"$n" to compute and display the minimal\n
30139---- path between those points. The ending point is then\n
30140---- chosen as the next starting point for another path.\n
30141---- Key '"${c}"S"$n"' to save snapshot of the current view.\n
30142---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30143----\n
30144--------------------------------------------------------------"
30145
30146  if !$! sp ? fi
30147  n 0,200 round 1
30148  repeat $! l[$>]
30149    w[0] -1,-1,0,"[G"{`39`}"MIC] Select Starting Point P0"
30150
30151    if !narg($first_time)
30152      parallel 0,"+l[0] r2dy 128 frame 1,1,0 \
30153        alert \"[G"{`39`}"MIC Minimal Path]\",\
30154           \"The G\47MIC minimal path demo illustrates how minimal paths\n\
30155           can be computed in images to detect and track edge points.\n\
30156           Use your mouse to select desired starting and ending points,\n\
30157           and see what is the minimal path computed between these points.\",\
30158           \"OK\" \
30159        rm endl"
30160      first_time=0
30161    fi
30162
30163    +gradient_norm b. 1 f. exp(-i/10)
30164    to_rgb[0] +select[0] 0 P0={^}
30165    ellipse[0] {@0,1},3,3,0,1,255,0,255
30166    ellipse[0] {@0,1},3,3,0,1,0xFFFFFFFF,255,255,255
30167    rm.
30168    if min($P0)>=0
30169      p=1
30170      do
30171        w[0] -1,-1,0,"[G"{`39`}"MIC] Select Ending Point P"$p
30172        +select[0] 0
30173        if {*,S}
30174          rm.
30175          +to[0] "Saving snapshot...",5,5,13,1,1,255,255,255 w. rm.
30176          o[0] gmic_minimal_path.png
30177          wait -1
30178        else
30179          P1={^}
30180          ellipse[0] {@0,1},3,3,0,1,255,0,255
30181          ellipse[0] {@0,1},3,3,0,1,0xFFFFFFFF,255,255,255
30182          rm.
30183          if min($P1)>=0
30184            +to[0] "Processing...",5,5,13,1,1,255,255,255 w. rm.
30185            +minimal_path[1] $P0,$P1,1
30186            pointcloud. 0 *. 255 r. 100%,100%,1,[0],0,0,0,0,0,0.5 ri. [0],0 -|[0,-1]
30187            P0=$P1 p+=1
30188          fi
30189        fi
30190      while {*}" && "!{*,ESC}" && "!{*,Q}
30191    fi
30192    rm[1] w 0
30193  endl done
30194
30195#@cli x_morph : _nb_frames>=2,_preview_fidelity={ 0=coarsest | 1=coarse | 2=normal | 3=fine | 4=finest }
30196#@cli : Launch the interactive image morpher.
30197#@cli : Default values: 'nb_frames=16' and 'preview_fidelity=3'.
30198x_morph : check "isint(${1=16}) && isint(${2=3}) && $2>=0 && $2<=4" check_display $0
30199  if $!<2 error[0--3] "Command '$0': Requires at least two input images!" return fi
30200  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r b=$_vt100_b
30201  e[] "\n
30202------ "${g}"Interactive image morpher"$n" ---------------------------\n
30203----\n
30204---- "${b}"Source/target window:"$n"\n
30205---- "${c}"Left mouse button"$n": Add new keypoint on current image\n
30206----  and move it on the other one.\n
30207---- "${c}"Right mouse button"$n": Add/move keypoint on current image.\n
30208---- Key '"${c}"DELETE"$n"' or "${c}"middle mouse button"$n": Delete keypoint.\n
30209---- Key '"${c}"SPACE"$n"' or "${c}"mouse wheel"$n": Toggle source/target.\n\n
30210---- "${b}"In-between window:"$n"\n
30211---- "${c}"Mouse wheel"$n": Change morphing time, from 0 to 1.\n
30212---- "${c}"Left mouse button"$n": Reset morphing time to 0.5.\n\n
30213---- "${b}"Both windows:"$n"\n
30214---- Key '"${c}"TAB"$n"': Change keypoint radius.\n
30215---- Key '"${c}"ENTER"$n"': Play/stop in-between animation.\n
30216---- Key '"${c}"R"$n"': Reset keypoints.\n
30217---- Key '"${c}"K"$n"': Show/hide keypoints.\n
30218---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"': Process fullres and exit.\n
30219----\n
30220-----------------------------------------------------------"
30221
30222  offset=0
30223  radius_keypoints=3
30224  repeat $!-1 l[{[$>,$>+1]+$offset}] nm0={0,n} nm1={1,n}
30225    to_colormode 0
30226    rr2d ${-max_wh},2
30227    nm img0,img1
30228    256,1,1,1,"x>=4?x-2:1" map. 2 nm. colormap
30229
30230    # Start interactive window.
30231    selected_keypoint=-1
30232    view_keypoints=1
30233    time_inbetween=0.5
30234    animate_inbetween=0
30235    move_other=0
30236    frame=0
30237
30238    do
30239      # Generate set of keypoints (x0,y0,x1,y1,index_color)
30240      if !narg($keypoints)
30241        if narg($__x_morph_keypoints) ($__x_morph_keypoints) r. 1,{w/5},1,5,-1
30242        else 1,4,1,5,"p = y%2; q = int(y/2); [ [ p,q,p,q ]*100,y ]"
30243        fi
30244        N0={h} nm. keypoints
30245        rmn warp0,warp1
30246      fi
30247
30248      # Generate base images.
30249      if !narg($imgb0)" || "!narg($imgb1)
30250        if {*} wdims=${"fitscreen "{*,d},{img0,{*,d}*h/w}}
30251        else wdims=${"fitscreen "{img0,[w,h,1]},128,50%}
30252        fi
30253        w[] $wdims,0,"[G'MIC] Interactive Morph"
30254        repeat 2 +to_color[img$>] r. $wdims,1,100%,2 n. 0,255 nm. imgb$> done
30255        rmn imgr,warp0,warp1
30256      fi
30257
30258      # Generate warp fields.
30259      if (!narg($warp0)" || "!narg($warp1))
30260        subsamp={arg(1+$2,8,6,4,2,1)}
30261        +_x_warp_rbf[keypoints] {imgb0,round([w,h]/$subsamp)}
30262        +l[keypoints] s c,-2 rv[0,1] a c endl +_x_warp_rbf. {imgb0,round([w,h]/$subsamp)} rm..
30263        *[-2,-1] $subsamp
30264        r[-2,-1] {imgb0,[w,h]},1,100%,3
30265        nm[-2,-1] warp0,warp1
30266        rmn imgm
30267      fi
30268
30269      # Render visualization.
30270      if !narg($imgr)
30271        [imgb$frame] nm. imgr
30272        if s==4 drgba. fi
30273        if $view_keypoints
30274          eval[keypoints] "*
30275            begin(
30276              fact = ([ w#"$imgb0",h#"$imgb0" ] - 1)/100;
30277              const radius1 = "$radius_keypoints";
30278              const radius2 = radius1 + 2;
30279              const opacity = min(1,3/"($radius_keypoints-1)");
30280            );
30281            X = round((I)[2*"$frame",2]*fact);
30282            y=="$selected_keypoint"?(ellipse(#-1,X,radius2+2,radius2+2,0,1,255));
30283            ellipse(#-1,X,radius2,radius2,0,opacity,0);
30284            ellipse(#-1,X,radius1,radius1,0,opacity,I[#"$colormap",i4]); I"
30285        fi
30286        w. -1,-1,0,"[G'MIC] Interactive Morph ("${"s0=Source s1=Target u ${s"$frame"}"}")"
30287      fi
30288
30289      # Generate morph image
30290      if !narg($imgm)
30291        t,onemt={t=$animate_inbetween?0.5*(1+sin(2*($time_inbetween+$|-$animate_inbetween))):$time_inbetween;[t,1-t]}
30292        +*[warp1] $t *. -1 +warp[imgb0] .,1,2,3 rm.. *. $onemt
30293        +*[warp0] $onemt *. -1 +warp[imgb1] .,1,2,3 rm.. *. $t
30294        +[-2,-1] c. 0,255 nm. imgm
30295        w1. -1,-1,0,"[G'MIC] Interactive Morph (In-between)"
30296      fi
30297
30298      # Manage user interaction
30299      if $animate_inbetween wait 40 else wait fi
30300      mb={*,b} mxy={[{*,x,y}]*100/([{*,w,h}]-1)} mouse_over={{*,x}>=0}
30301
30302      if $mouse_over" && "($mb" || "{*,DELETE})" && "$selected_keypoint<0" && "h#$keypoints>0
30303
30304        # Determine selected keypoint.
30305        selected_keypoint={keypoints,"dmin = inf; kmin = -1; fact = ([w#"$imgr",h#"$imgr"]-1)%;
30306          repeat (h,k,
30307            dist = norm(((I[k])[2*"$frame",2] - ["$mxy"])*fact);
30308            dist<dmin?(dmin = dist; kmin = k)
30309          );
30310          kmin>=0 && dmin<"max(8,1.5*$radius_keypoints)"?kmin:-1"}
30311      fi
30312
30313      if {*,-o}" || "{*,-SPACE}" || "{*1,-SPACE} frame={!$frame} rmn imgr fi # Swap frames
30314      if {*,-K}" || "{*1,-K} view_keypoints={!$view_keypoints} rmn imgr fi # Show/hide keypoints
30315      if {*,-R}" || "{*1,-R} rmn keypoints,imgr __x_morph_keypoints= fi # Reset keypoints
30316      if {*,-TAB}" || "{*1,-TAB} # Change keypoint radius
30317        radius_keypoints={max(2,($radius_keypoints+2)%8)} view_keypoints=1 rmn imgr
30318      fi
30319      if {*,-ENTER}" || "{*1,-ENTER} animate_inbetween={$animate_inbetween?0:$|} fi # Start in-between animation
30320      if {*,r} rmn imgb0,imgb1 fi # Window resize
30321      if {*1,r} rmn imgm fi
30322      if {*1,o} # Change time of in-between
30323        time_inbetween={max(0,min(1,$time_inbetween+0.05*{*1,-o}))} animate_inbetween=0 rmn imgm
30324      fi
30325      if {*1,-b} time_inbetween=0.5 animate_inbetween=0 rmn imgm fi # Reset in-between time
30326
30327      if $mouse_over" && "($mb==1" || "$mb==2)" && "$selected_keypoint<0 # Add keypoint
30328        if $mb==1" && "!$move_other frame={!$frame} move_other=1 fi # Move on other window
30329        ({keypoints,[$mxy,$mxy,h]})
30330        permute. zycx a[keypoints,-1] y
30331        selected_keypoint={keypoints,h-1}
30332        rmn imgr
30333
30334      elif $mouse_over" && "($mb==1" || "$mb==2)" && "$selected_keypoint>=0 # Move keypoint
30335        if $mb==1" && "!$move_other frame={!$frame} move_other=1 fi # Move on other window
30336        ({[$mxy]}) permute. zycx j[keypoints] .,0,$selected_keypoint,0,{2*$frame} rm.
30337        rmn imgr
30338
30339      elif $mouse_over" && "$selected_keypoint>=0" && ("{*,-DELETE}" || "$mb==4") && "h#$keypoints>4 # Delete keypoint
30340        1,1,1,5,-1 j[keypoints] .,0,$selected_keypoint rm. discard[keypoints] -1 r[keypoints] 1,{keypoints,h/5},1,5,-1
30341        N0-={$selected_keypoint<$N0?1:0} selected_keypoint=-1
30342        rmn warp0,warp1,imgr
30343
30344      elif !{*,b}" && "$selected_keypoint>=0
30345        if $move_other frame={!$frame} move_other=0 fi
30346        selected_keypoint=-1
30347        rmn warp0,warp1,imgr
30348      fi
30349
30350      if $animate_inbetween rmn imgm fi
30351    while {*}" && "!{*,ESC}" && "!{*,Q}" && "{*1}" && "!{*1,ESC}" && "!{*1,Q}
30352    if !$< __x_morph_keypoints={keypoints,^} fi
30353
30354    # Render fullres morphing.
30355    rmn colormap,imgb0,imgb1,warp0,warp1,imgr,imgm
30356    +_x_warp_rbf[keypoints] {img0,[w,h]}
30357    +l[keypoints] s c,-2 rv[0,1] a c endl +_x_warp_rbf. {img0,[w,h]} rm..
30358    nm[-2,-1] warp0,warp1
30359
30360    repeat $1
30361      t,onemt={t=$>/max(1,$1-1);[t,1-t]}
30362      +*[warp1] $t *. -1 +warp[img0] .,1,2,3 rm.. *. $onemt
30363      +*[warp0] $onemt *. -1 +warp[img1] .,1,2,3 rm.. *. $t
30364      +[-2,-1] c. 0,255 nm. ${nm0}_$>
30365      if {*}" && "{*1} w1 0 fi
30366      if {*}" || "{*1}
30367        text="Processing frame \#"$>"/"{$1-1}"..."
30368        if {*} +r. {*,d,e},1,100%,2 to. $text,5,5,20,2 w. rm. fi
30369        if {*1} +r. {*1,d,e},1,100%,2 to. $text,5,5,20,2 w1. rm. fi
30370      fi
30371    done
30372    nm. $nm1 k[-$1--1]
30373    offset+={$!-2}
30374  endl done w0 0 w1 0
30375
30376#@cli x_pacman
30377#@cli : Launch pacman game.
30378x_pacman : check_display $0
30379  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30380  e[] "\n
30381------ "${g}"Pacman"$n" -----------------------------------------------\n
30382----\n
30383---- This is a G\47MIC implementation of the "${g}"pacman"$n" game.\n
30384----\n
30385---- Move the pacman to eat all pacdots on the different levels.\n
30386---- Eating a pacgum makes pacman invincible for "${c}"10 seconds"$n",\n
30387---- which mean pacman can eat ghosts during this time.\n
30388---- Eating a ghost earns "${c}"100 pts"$n".\n
30389---- Eating a cherry earns "${c}"10 pts"$n".\n
30390---- Eating a strawberry earns "${c}"100 pts"$n".\n
30391---- Eating an orange earns "${c}"1000 pts"$n".\n
30392---- Eating a banana earns "${c}"5000 pts"$n".\n
30393----\n
30394---- "${c}"Arrow keys"$n" to control pacman.\n
30395---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
30396---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
30397---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30398----\n
30399--------------------------------------------------------------"
30400  l[]
30401
30402  # Initialize characters gfx.
30403  m "_pacman_ghost_base_gfx : 31,19 circle. 15,15,15,1,1 31,12,1,1,'y<4+8*abs(cos(x*0.3+0.25*pi*$""1))' a[-2,-1] y"
30404  repeat 4
30405    _pacman_ghost_standard_gfx 255,0,0,$> nm. ghost0_$>
30406    _pacman_ghost_standard_gfx 0,255,222,$> nm. ghost1_$>
30407    _pacman_ghost_standard_gfx 255,184,222,$> nm. ghost2_$>
30408    _pacman_ghost_standard_gfx 255,184,71,$> nm. ghost3_$>
30409    _pacman_ghost_afraid_gfx $> nm. ghosta_$>
30410    _pacman_ghost_base_gfx $> r. 16,16,1,1,2 nm. ghostm_$>
30411    _pacman_ghost_standard_gfx 0,0,0,$> nm. ghostd_$>
30412    _pacman_pacman_gfx $> nm.. pacman_$> nm. pacmanm_$>
30413  done
30414  +channels[ghostd_0] 0 !=. 0 nm. ghostdm
30415  _pacman_cherry_gfx nm. fruit0 _pacman_strawberry_gfx nm. fruit1
30416  _pacman_orange_gfx nm. fruit2 _pacman_banana_gfx nm. fruit3
30417  20,2,1,3,200 nm. gate
30418  score0,score1,score2,score3,score4=10,100,1000,5000,"Argh!"
30419  repeat 5
30420    0 t. ${score$>},0,0,13,1,255,255,255 autocrop. 0 expand_xy. 1,0 +dilate. 3
30421    nm. scorem$> nm.. score$>
30422  done
30423  time4=255,255,255 time3=255,255,32 time2=255,128,32 time1=255,32,32
30424  repeat 11 0 t. $<" s",0,0,23,1,${time{min(4,round(($<+1)/2))}} nm. time$< done
30425  0 t. "Get Ready!",0,0,32,1,255 autocrop. 0 expand_xy. 4,0 +dilate. 8 r.. 100%,100%,1,3
30426  nm.. get_ready nm. get_readym
30427  0 t. "Game\nOver!",0,0,53,1,255 autocrop. 0 expand_xy. 4,0 +dilate. 8 r.. 100%,100%,1,3
30428  nm.. game_over nm. game_overm
30429
30430  # Start game.
30431  score=0 level=-1 lives=3 is_quit=0
30432  do
30433
30434    # Build new level if necessary.
30435    if $level<0
30436      _rlevel=33 _glevel=33 _blevel=255
30437      _pacman_map_level{((-$level-1)%6)+1} mw={w} mh={h} mw2={int(w/2)} mh2={int(h/2)}
30438      if $level<-6 replace. 3,2 fi
30439      . nm[-2,-1] map0,map
30440
30441      # Precompute valid directions on each map point, and shortest path to the ghost's home.
30442      +shift[map] -1,0 +shift[map] 0,-1 +shift[map] 1,0 +shift[map] 0,1 a[-4--1] z !=. 1 nm. can_go
30443      +==[map] 1 100%,100% =. 1,$mw2,$mh2 distance. 1,..,3 rm..
30444      f. 'if(i==2,0,if(i==8,1,if(i==1,2,if(i==4,3,i))))' nm. path
30445      +==[map] 2 pacdots={is} rm.
30446      level={-$level}
30447    fi
30448
30449    # Render board gfx.
30450    f[map] 'if(i>=4,0,i)' +==[map] 1 expand_xy. 1,0 r. 1600%,1600% erode. 9 b. 2
30451    g. xy abs[-2,-1] +[-2,-1] >=. 80% b. 2 n. 0,1 shrink_xy. 16
30452    +*. $_glevel +*.. $_blevel *... $_rlevel a[-3--1] c
30453    16,16,1,1,'x' +-[map] 1 max. 0 *. 16 r. 1600%,1600%
30454    16,16,1,1,'y' ri[-3,-1] ..,0,2 +[-2,-1] a[-2,-1] c
30455    16,16,1,3 _pacman_pacdots_gfx _pacman_pacgum_gfx a[-3--1] y
30456    warp. ..,0,0 rm.. -|[-2,-1] r. 100%,{h+24},1,3,0,0,0,1
30457    t. "Lives :",10,0,24,1,255 t. "Score :",{w-140},0,24,1,255
30458    if $lives +r[pacman_2] 12,12,1,4,2 r. {100*$lives}%,100%,1,4,0,2 j.. .,90,7 rm. fi
30459    nm. visu
30460    w[visu] {visu,f=h<0.5*{*,v}?1.5:1;[w,h]*=f},0,"[G"{`39`}"MIC] Pacman" cursor[0] 0
30461    0 t. "Level "$level,0,0,64,1,1 autocrop. 0 expand_xy. 4,0 (0,255^0,255^0,0) +map.. . rm.. dilate.. 8
30462    nm. level_N nm.. levelm_N
30463
30464    repeat 4 xg$>={16*$mw2} yg$>={16*$mh2+4*$>} dg$>=3 mg$>=0 done
30465    xp={16*10} yp={16*21} dp=-1 pacgum_timer=-1 fruit_timer=$| dying_pacman=0 is_get_ready=1
30466    xscore=0 yscore=0 nscore=0 oscore=0
30467
30468    # Start game interaction.
30469    do
30470
30471      # Display board graphics.
30472      t={int(6*$|)%4} left={if($pacgum_timer>=0,10-$|+$pacgum_timer,-1)}
30473      [visu]
30474      repeat 4
30475        mg=${mg$>} xg=${xg$>} yg={${yg$>}+24}
30476        if $mg==0 j. [ghost$>_$t],$xg,$yg,0,0,1,[ghostm_$t]
30477        elif $mg==1 t2={if($left>7,$t,if($left>3,int(12*$|)%4,int(24*$|)%4))} j. [ghosta_$t2],$xg,$yg,0,0,1,[ghostm_$t]
30478        elif $mg==2 j. [ghostd_$t],$xg,$yg,0,0,0.8,[ghostdm]
30479        else j. [ghost$>_$t],$xg,$yg,0,0,{$mg-2},[ghostm_$t] j. [ghostd_$t],$xg,$yg,0,0,1,[ghostdm]
30480        fi
30481      done
30482      if $dying_pacman
30483        _pacman_pacman_gfx {$dying_pacman/2} rotate[-2,-1] {90*(abs($dp)-1)} j... ..,$xp,{24+$yp},0,0,1,.,255 rm[-2,-1]
30484        dying_pacman+=1
30485        if $dying_pacman>64
30486          if $lives!=1 rm. break fi
30487            j. [game_over],{(w-{game_over,w})/2},{12+(h-{game_over,h})/2},0,0,{min(1,($dying_pacman-64)/50)},\
30488               [game_overm],255
30489          rectangle. 90,7,101,18,1,0
30490        fi
30491      else
30492        +rotate[pacman_$t,pacmanm_$t] {90*(abs($dp)-1)} j... ..,$xp,{24+$yp},0,0,1,.,255 rm[-2,-1]
30493        if $left>=0" && "($left>=5" || "$t<=2) j. [time{round($left)}],{(w-{time0,w})/2-10},1 fi
30494      fi
30495      t. $score,{w-60},3,20,1,255
30496      if $is_get_ready
30497        j. [level_N],{(w-{level_N,w})/2},{12+(h-1.5*{level_N,h})/2},0,0,1,[levelm_N]
30498        if int($|*4)%2 j. [get_ready],{(w-{get_ready,w})/2},{24+(h+{get_ready,h})/2},0,0,1,[get_readym],255 fi
30499      fi
30500      if $oscore>0 j. [score$nscore],$xscore,$yscore,0,0,$oscore,[scorem$nscore],255 oscore-=0.04 yscore-=1 fi
30501      j. [gate],158,223,0,0,0.6
30502      w.
30503      if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h}
30504      elif {*,CTRLLEFT}" && "{*,C} w[] {f=h<0.5*{*,v}?1.5:1;[w,h]*=f}
30505      fi
30506      rm.
30507
30508      # Manage ghosts displacements and collisions.
30509      repeat 4
30510        xg=${xg$>} yg=${yg$>} dg=${dg$>} mg=${mg$>}
30511
30512        if max(abs($xg-$xp),abs($yg-$yp))<=8              # Test collision between ghost and pacman.
30513          if $mg==0" && "!$dying_pacman dying_pacman=1    # Was in normal mode -> dying pacman.
30514            xscore=$xp yscore={$yp+12} oscore=1 nscore=4
30515          elif $mg==1 mg=2 mg$>=$mg score+=100            # Was in invicibility mode -> dying ghost.
30516            xscore=$xp yscore={$yp+12} oscore=1 nscore=1
30517          fi
30518        fi
30519        if $mg>=2" && "($xg>>4)==$mw2" && "($yg>>4)==$mh2 # Check if dying ghost has returned to home.
30520          mg+=0.01
30521          if $mg>=3 mg=0 xg&=-2 yg&=-2 fi
30522          mg$>=$mg
30523        fi
30524
30525        if !($xg&15)" && "!($yg&15) # Check if ghost can take a new direction.
30526          ({u},{u},{u},{u};0,1,2,3)
30527          if $mg<2 # Try to chase or escape pacman
30528            =. {u(0.6,1)},{if($mg==0,dX0=$xp-$xg;dY0=$yp-$yg;if(abs(dX0)>abs(dY0),if(dX0>0,0,2),if(dY0>0,1,3)),\
30529                                     dX1=$xp-$xg;dY1=$yp-$yg;if(abs(dX1)<abs(dY1),if(dX1>0,2,0),if(dY1>0,3,1)))}
30530            =. 0,{($dg+2)%4}
30531            if $is_get_ready =. 0.8,{path,i({$xg>>4},{$yg>>4})} fi
30532          else =. 1,{path,i({$xg>>4},{$yg>>4})} # If dying ghost, follow the best path to home.
30533          fi
30534          sort. -,x
30535          repeat 4 d={i($>,1)} # Try directions until it matches.
30536            if {can_go,i({$xg>>4},{$yg>>4},$d)} dg=$d break fi
30537          done rm.
30538          dg$>=$d
30539        fi
30540        u={D=${dg$>};(D==0)-(D==2)}
30541        v={D=${dg$>};(D==1)-(D==3)}
30542        xg$>={($xg+$u*(1+($mg==0)))%(16*$mw)}
30543        yg$>={($yg+$v*(1+($mg==0)))%(16*$mh)}
30544      done
30545
30546      wait 22
30547
30548      # Manage pacman displacement.
30549      if !$dying_pacman
30550        d={if({*,ARROWRIGHT},1,if({*,ARROWDOWN},2,if({*,ARROWLEFT},3,if({*,ARROWUP},4,$dp))))}
30551        if !($xp&15)" && "!($yp&15)
30552          i={map,i({$xp>>4},{$yp>>4})}
30553          if $i==2 score+=10 pacdots-=1 # Pacdot eaten.
30554          elif $i==3 pacgum_timer=$| repeat 4 if !${mg$>} mg$>=1 dg$>={(${dg$>}+2)%4} fi done  # Pacgum eaten.
30555          elif $i>=4 score+={${score{$i-4}}} xscore=$xp yscore={$yp+12} oscore=1 nscore={$i-4} # Fruit eaten.
30556          fi
30557          =[map] 0,{$xp>>4},{$yp>>4}
30558          16,16,1,3 j[visu] .,$xp,{24+$yp} rm.
30559          d={if({can_go,i({$xp>>4},{$yp>>4},{abs($d)-1})},$d,$dp)}
30560          d={if({can_go,i({$xp>>4},{$yp>>4},{abs($d)-1})},$d,-abs($dp))}
30561          dp=$d
30562        else dp={if(abs($d-$dp)==2,$d,$dp)}  # Allow to turn back on non-integer locations.
30563        fi
30564        is_get_ready={if($dp>0,0,$is_get_ready)}
30565        u={($dp==1)-($dp==3)}
30566        v={($dp==2)-($dp==4)}
30567        xp={($xp+2*$u)%(16*$mw)}
30568        yp={($yp+2*$v)%(16*$mh)}
30569
30570        if $pacgum_timer>=0" && "$|>$pacgum_timer+10 # Check if pacgum still has some effect.
30571          repeat 4 xg$>&=-2 yg$>&=-2 mg$>={if(${mg$>}==1,0,${mg$>})} done
30572          pacgum_timer=-1
30573        fi
30574
30575        if !$is_get_ready" && "($|-$fruit_timer)>=10
30576          x={round(u(0,{map0,w}))}
30577          y={round(u(0,{map0,h}))}
30578          if !{map,i($x,$y)}" && "{map0,i($x,$y)}==2
30579            n={min(3,int(abs(g*1.7)))} =[map] {4+$n},$x,$y j[visu] [fruit$n],{16*$x},{16*$y+24} fruit_timer=$|
30580          fi
30581        fi
30582
30583      fi
30584      if !{*}" || "{*,Q}" || "{*,ESC} is_quit=1 fi
30585
30586    while !$is_quit" && "$pacdots
30587
30588    if $is_quit break         # Player asked to quit
30589    elif $pacdots             # Player lost a life
30590      lives-=1
30591    else                      # Player achieved level
30592      level={-$level-1} wait[0] -1
30593      rm[map0,map,can_go,path]
30594    fi
30595    rm[visu,level_N,levelm_N]
30596  while $lives
30597  rm w 0 endl
30598
30599# The functions below create the various sprite gfx.
30600_pacman_ghost_standard_gfx :
30601  _pacman_ghost_base_gfx $4 (0,$1^0,$2^0,$3) map.. . rm.
30602  ellipse. 10,11,3,4,0,1,255 ellipse. 20,11,3,4,0,1,255
30603  r. 16,16,1,3,2 point. 5,{7-($4>=2)},0,1,1 point. 10,{7-($4>=2)},0,1,1
30604
30605_pacman_ghost_afraid_gfx :
30606  _pacman_ghost_base_gfx $1
30607  if $1<2 col=255,255,255 (0,0^0,0^0,208) else col=255,0,0 (0,248^0,248^0,248) fi
30608  map.. . rm. r. 16,16,1,3,2
30609  line. 4,4,6,6,1,$col,255 line. 4,6,6,4,1,$col,255
30610  line. 9,4,11,6,1,$col,255 line. 9,6,11,4,1,$col,255
30611  f. 'if(y>=9&&y<=10&&x>=2&&x<=13&&((int((x+1)/2)+y)%2),arg(c+1,$col),i)'
30612
30613_pacman_pacman_gfx :
30614  32,32,1,1,'X=x-15;Y=y-15;A=atan2(Y,X);R=sqrt(X^2+Y^2);R<15.5&&abs(A)>0.8*0.33*$1'
30615  (0,255^0,255^0,0^0,255) map.. . rm. r. 16,16,1,4,2 s. c,-3
30616
30617_pacman_pacdots_gfx :
30618  (255^184^151) r. 4,4,1,3 r. 16,16,1,3,0,0,0.5,0.5
30619
30620_pacman_pacgum_gfx :
30621  64,64,1,3 circle. 31,31,31,1,255,128,64 r2dx. 16
30622
30623_pacman_cherry_gfx :
30624  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KNzYgMSAxIDEgIzU5CnicFYpBCgAxDALVXvv/p5ZtmsgmIMoM7k0Cx/ySYYIXrE5qOgTmE1KGl"\
30625               "oUW1pp1qVUqmkt3Hj9Whx3SMSAyMCAxIDEgIzI2Cnicc/eNYkjPzUyONwACveTM3PQqBgA+VQX2"
30626  decompress_rle. (0,0,255,255^0,173,0,255^0,0,0,255) map.. . rm. r2dy. 14 r. 16,16,1,3,0,0,0.5,0.5
30627
30628_pacman_strawberry_gfx :
30629  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KNzIgMSAxIDEgIzYwCnicJYlJDoAwEMOyHOH/H+UANJ0RBaRIlp1tJ4HAd9HFaoUtTNWC1yIPW"\
30630               "T5ew5em+lT994guKHgAoIoa8zEgMjAgMSAxICMyNgp4nHP3jWJIz81MjjcAAr3kzNz0KgYAPlUF9g=="
30631  decompress_rle. (0,0,255,255^0,173,0,255^0,0,0,255) map.. . rm. r2dy. 14 r. 16,16,1,3,0,0,0.5,0.5
30632
30633_pacman_orange_gfx :
30634  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KNDQgMSAxIDEgIzQ2CnicBcHBDQAgDAOxXPjC/gsxU4VKi7DnAukKPU6SYjTVptxhbSv8wpVuf"\
30635               "UwDEZ4xIDIwIDEgMSAjMjYKeJxz941iSM/NTI43AAK95Mzc9CoGAD5VBfY="
30636  decompress_rle. (0,0,255,255^0,173,173,255^0,0,0,255) map.. . rm. r2dy. 14 r. 16,16,1,3,0,0,0.5,0.5
30637
30638_pacman_banana_gfx :
30639  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KNzAgMSAxIDEgIzQ4CnicNcqxDQAgDMTANx0S++8aIC9ShObceC6QQoSJ5Ai5vWW2WTI6xr+bv"\
30640               "LU/BnUW3jEgMjAgMSAxICMyNgp4nHP3jWJIz81MjjcAAr3kzNz0KgYAPlUF9g=="
30641  decompress_rle. (0,255,255^0,173,255^0,0,255) map.. . rm. r2dy. 14 r. 16,16,1,3,0,0,0.5,0.5
30642
30643_pacman_map_level1 :
30644  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjAxIDEgMSAxICM5Nwp4nGWOUQ6AMAhDKX56Be9/QnWJArEM9MdsGa9QYOsGiOy4FaZYHAo1a"\
30645               "MezIJ+QJnszLsq2aRaf9Q9GiaBnGmEZhSe8wLOdVqO+My+cFdJj9OpVQ0vzRm8l/L954AHE9jnsMSAyMCAxIDEgIzI2Cnicc/eNYk"\
30646               "jPzUyONwACveTM3PQqBgA+VQX2"
30647  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
30648  _rlevel=33 _glevel=33 _blevel=255
30649
30650_pacman_map_level2 :
30651  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjA4IDEgMSAxICMxMDAKeJxljkEOAzEIAxn32C/0/y+MdlUloAKJemgvjAOG+PkCs8ElHsjRQ"\
30652               "k0iycwS+/2jcuJlnhhhf3SxPKWw9DaJmYsbmLOqmyaXn92UsVV9Y+sweOvb7YB1vQPPDnV4a/ABHS45BDEgMjAgMSAxICMyNgp4nH"\
30653               "P3jWJIz81MjjcAAr3kzNz0KgYAPlUF9g=="
30654  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
30655  _rlevel=200 _glevel=33 _blevel=33
30656
30657_pacman_map_level3 :
30658  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjA5IDEgMSAxICMxMDEKeJxVTlsOwDAIAve5K+z+N+zStGomeyTdjwIKYT9IoHGAbpOgdQ3n1"\
30659               "i1ZinHdnO+20FuKg3nf4WIO3YolP8QEQ75CEkoKaUIDT3K95w8OZbp4lDcMj1PNUjXyDg+u4LTGC5LiOwAxIDIwIDEgMSAjMjYKeJ"\
30660               "xz941iSM/NTI43AAK95Mzc9CoGAD5VBfY="
30661  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
30662  _rlevel=33 _glevel=200 _blevel=255
30663
30664_pacman_map_level4 :
30665  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjEwIDEgMSAxICM5MAp4nHVPQQ6AMAhr8egX/P8TN+cGGSy6TBMTQgqlUPaDBDINLBJZbMQHq"\
30666               "mwq7e40eahgy8i/jDKB1QhlRWBzcPH09Rnr9ALTrHSOuN5N+IFF9LIY9uOPDitcQvExIDIwIDEgMSAjMjYKeJxz941iSM/NTI43AA"\
30667               "K95Mzc9CoGAD5VBfY="
30668  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
30669  _rlevel=200 _glevel=200 _blevel=33
30670
30671_pacman_map_level5 :
30672  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjA3IDEgMSAxICMxMDAKeJxdjlEKxCAMRPOmn3uF3v+ELXRrDE2qwrIgzPB8Mn52MDv4ihBbQ"\
30673               "yjQyuRBz96RF8NmSYAb98s65j95Wd0bFqxG1MNV/s1ealMujRHdI5wtf9X0WnWmFWO/7JmJXScPVxI1CjEgMjAgMSAxICMyNgp4nH"\
30674               "P3jWJIz81MjjcAAr3kzNz0KgYAPlUF9g=="
30675  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
30676  _rlevel=200 _glevel=255 _blevel=33
30677
30678_pacman_map_level6 :
30679  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMTgzIDEgMSAxICM5Mgp4nGVO0QqAQAhz67Ff6P+/sK66TNqJUBCozDHn5gUwW9EIBnhi2nmBP"\
30680               "QmHCeMwDbuzBNCH1oZeDBEiPSWiAoXU8Yfhn4PyiNc1X3i99NwUJPMoVyVs3PAASqEr4jEgMjAgMSAxICMyNgp4nHP3jWJIz81Mjj"\
30681               "cAAr3kzNz0KgYAPlUF9g=="
30682  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
30683  _rlevel=255 _glevel=130 _blevel=233
30684
30685#@cli x_paint
30686#@cli : Launch the interactive painter.
30687x_paint : check_display $0
30688  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30689  e[] "\n
30690------ "${g}"Interactive painter"$n" -----------------------\n
30691----\n
30692---- Use "${c}"mouse"$n" to select color and brush.\n
30693---- "${c}"Left button"$n" draws a colored stroke.\n
30694---- "${c}"Right button"$n" fills a colored region.\n
30695---- "${c}"Arrow keys"$n" or '"${c}"SPACE"$n"' and '"${c}"BACKSPACE"$n"' to swap\n
30696---- between available images.\n
30697---- Key '"${c}"S"$n"' to save snapshot of the current view.\n
30698---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30699----\n
30700--------------------------------------------------"
30701  to_rgb
30702  if !$! i[0] 512,512,1,3,255 nm[0] "[New image]" else k[0] fi
30703  1 # Brush image [-1]
30704  parallel "_x_paint[]","w[] 400,320,0,Palette x_select_color[] __color,0,0,0" k[0]
30705
30706_x_paint :
30707
30708  # Init variables.
30709  pass[-2,-1] 1 ('{-2,n}') discard. {'_c1'} nm... {t} rm.
30710  __color={0,if(ia<128,vector3(255),vector3(0))}
30711  brushsize=1
30712  brushopacity=0
30713  brushangle=90
30714  brushthickness=1
30715  image=0
30716  refresh_image=1
30717  refresh_brush=1
30718  ox1=-1
30719  oy1=-1
30720
30721  # Start user event loop.
30722  do
30723
30724    # Open/refresh brush window.
30725    if $refresh_brush
30726      rm. (32,64;64,32) r. 16,16,1,3,1 r. {8*48},{4*48},1,3,0,2
30727      repeat 4,y repeat 8
30728        ellipse. {48*$>+24},{48*$y+24},{2*$>+1},{(2*$>+1)*$brushthickness},$brushangle,{1-$y/4},255
30729      done done
30730      rectangle. {$brushsize*48},{$brushopacity*48},\
30731                  {$brushsize*48+47},{$brushopacity*48+47},\
30732                  1,0xFFFFFFFF,255,128,128
30733      {w},16,1,3 line. 0,50%,100%,50%,1,0x55555555,128,64,128
30734      bx={$brushangle*w/180}
30735      rectangle. {$bx-16},20%,{$bx+16},80%,1,128
30736      line. {$bx-16},20%,{$bx+16},20%,1,255 line. {$bx+16},20%,{$bx+16},80%,1,255
30737      line. {$bx-16},80%,{$bx+16},80%,1,64 line. {$bx-16},20%,{$bx-16},80%,1,64
30738      a[-2,-1] y
30739      16,{h-16},1,3 line. 50%,0,50%,100%,1,0x55555555,128,64,128
30740      by={$brushthickness*(h-16)}
30741      rectangle. 20%,{$by-16},80%,{$by+16},1,128
30742      line. 20%,{$by-16},80%,{$by-16},1,255 line. 80%,{$by-16},80%,{$by+16},1,255
30743      line. 20%,{$by-16},20%,{$by+16},1,64 line. 20%,{$by+16},80%,{$by+16},1,64
30744      a[-2,-1] x
30745      w3. {w},{h},0,"Brush"
30746      refresh_brush=0
30747    fi
30748
30749    # Open/refresh image window.
30750    if $refresh_image
30751      w1[$image] {$image,w},{$image,h},0,"Image "\#$image" : "{$image,b}.{$image,x}
30752      refresh_image=0
30753    fi
30754
30755    # Manage user events.
30756    x1={*1,x} y1={*1,y}
30757    x2={*2,x} y2={*2,y}
30758    x3={*3,x} y3={*3,y}
30759
30760    if $x1>=0                       # Event in the image window.
30761      if {*1,b}&1                   # Left button -> draw brush stroke.
30762        ox1={if($ox1<0,$x1,$ox1)}
30763        oy1={if($oy1<0,$y1,$oy1)}
30764        delta={max(abs($x1-$ox1),abs($y1-$oy1))}
30765        r1={2*$brushsize+1}
30766        r2={$r1*$brushthickness}
30767        dx={2*($x1-$ox1)/max(1,$delta)}
30768        dy={2*($y1-$oy1)/max(1,$delta)}
30769        o={1-($brushopacity/4)^0.04}
30770        repeat max(1,($delta+1)/2)
30771          ellipse[$image] {$ox1+$>*$dx},{$oy1+$>*$dy},$r1,$r2,$brushangle,$o,$__color
30772        done
30773        ox1=$x1 oy1=$y1
30774        refresh_image=1
30775      else
30776        ox1=-1 oy1=-1
30777        if {*1,b}&2               # Right button -> fill region.
30778          flood[$image] $x1,$y1,0,10,0,1,$__color
30779          refresh_image=1
30780        fi
30781      fi
30782    fi
30783
30784    if {*1,ARROWRIGHT}" || "{*2,ARROWRIGHT}" || "{*3,ARROWRIGHT}" || "\  # Manage image selection.
30785       {*1,ARROWUP}" || "{*2,ARROWUP}" || "{*3,ARROWUP}" || "\
30786       {*1,SPACE}" || "{*2,SPACE}" || "{*3,SPACE}
30787      image={($image+1)%($!-2)} refresh_image=1
30788    elif {*1,ARROWLEFT}" || "{*2,ARROWLEFT}" || "{*3,ARROWLEFT}" || "\
30789         {*1,ARROWDOWN}" || "{*2,ARROWDOWN}" || "{*3,ARROWDOWN}" || "\
30790         {*1,BACKSPACE}" || "{*2,BACKSPACE}" || "{*3,BACKSPACE}
30791      image={($image-1)%($!-2)} refresh_image=1
30792    fi
30793
30794    if {*1,S} o[$image] gmic_paint.png fi  # Save snapshot if requested.
30795
30796    if {*3,b}" && "$x3>=0  # Manage brush selection.
30797      if $x3<384" && "$y3>=192 brushangle={$x3*180/(w-16)}   # Bottom slider -> select brush angle.
30798      elif $x3>=384" && "$y3<192 brushthickness={$y3/(h-16)} # Right slider -> select brush thickness.
30799      elif $x3<384" && "$y3<192 brushsize={int($x3*8/(w-16))} brushopacity={int($y3*4/(h-16))}
30800      fi
30801      refresh_brush=1
30802    fi
30803    wait
30804    while {*1}" && "!{*1,Q}" && "!{*1,ESC}
30805
30806  # Exit properly.
30807  __color=-1 w1[] 0 w2[] 0 w3[] 0 rm[-2,-1]
30808
30809#@cli x_plasma
30810#@cli : Launch the plasma effect demo.
30811x_plasma : check_display $0
30812  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30813  e[] "\n
30814------ "${g}"Plasma effect"$n" ----------------------\n
30815----\n
30816---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
30817---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
30818---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30819----\n
30820-------------------------------------------"
30821  l[]
30822
30823  # Init plasma backgrounds.
30824  N=8
30825  repeat $N
30826    320,200,1,3 rand. 0,255 plasma. 1,0,7 n. 0,255
30827    amp={u(-40,40)} freq={round(u(2,6))} dir$>={if(u<0.5,-1,1)*round(u(1,2))}
30828    100%,100%,1,1,'$amp*cos(y*2*pi*$freq/h)'
30829  done
30830
30831  {w+2},100%,1,1,'x' 100%,100%,1,1,'Y=(y-80+15*cos(x/30)+10*sin(x/22));if(Y<0||Y>=50,-1,Y)' a[-2,-1] c
30832  0 t. "** Welcome to G\47MIC, a powerful image processing framework **",0,0,50,1,255
30833  b. 0.5 n. 0,255
30834  M={w}
30835
30836  # Start animation loop.
30837  w[] {0,f=1.5*h<0.5*{*,v}?3:1.5;[w,h]*=f},0,"[G"{`39`}"MIC] Plasma Effect"
30838  t=0 tt={-1.5*{0,w}}
30839
30840  do
30841    tic=$|
30842
30843    # Render interpolated background between two successive plasmas.
30844    a={int($t)} a2={2*$a} a21={$a2+1}
30845    b={($a+1)%$N} b2={2*$b} b21={$b2+1}
30846    +warp[$a2] [$a21],1,0,2
30847    +warp[$b2] [$b21],1,0,2
30848    j.. .,0,0,0,0,{$t-$a} rm.
30849
30850    shift[$a21] 0,${dir$a},0,0,2  # Animate plasma background.
30851    shift[$b21] 0,${dir$b},0,0,2
30852    if int($t+0.005)>int($t) dir$a={if(u<0.5,-1,1)*round(u(1,3))} fi
30853    t={($t+max(0.005,($|-$tic)))%$N}
30854
30855    # Render text scrolling.
30856    +z.. $tt,{$tt+w-1+2}
30857    warp. [-4],0,0,0
30858    r. 100%,100%,1,3
30859    +*. -1 +. 255
30860    j... .,0,0,0,0,1,..,255 rm.
30861    j.. .,-2,-2,0,0,1,.,255 rm.
30862
30863    tt+={max(2,($|-$tic)*250)} # Animate scrolling.
30864    if $tt>=$M tt={-1.5*{0,w}} fi
30865
30866    # Display rendered frame.
30867    fps=${-fps} if $fps>0 to. $fps" fps",5,5,16,1,0.2 fi
30868    w.
30869    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
30870    rm. wait 20
30871  while {*}" && "!{*,ESC}" && "!{*,Q}
30872  rm[{-2*$N-2}--1] w[] 0 endl
30873
30874#@cli x_quantize_rgb : _nbcolors>=2
30875#@cli : Launch the RGB color quantization demo.
30876x_quantize_rgb : check "isint(${1=16}) && $1>1" check_display $0
30877  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30878  e[] "\n
30879------ "${g}"RGB Quantization"$n" --------------------------------------\n
30880----\n
30881---- This demo shows how RGB colors can be quantified using\n
30882---- the "${c}"k-means algorithm"$n".\n
30883----\n
30884---- "${c}"Left mouse button"$n" on 3D view rotates the color cube.\n
30885---- "${c}"Right mouse button"$n" on 3D view toggles colors/clusters mode.\n
30886---- "${c}"Left mouse button"$n" on image toggles dithering mode,\n
30887---- "${c}"Left mouse button"$n" on colormap adds a random color.\n
30888---- "${c}"Right mouse button"$n" on colormap removes a color.\n
30889---- Key '"${c}"R"$n"' init colormap with random values.\n
30890---- Key '"${c}"U"$n"' init colormap with uniform sampling.\n
30891---- Key '"${c}"M"$n"' init colormap with median-cut algorithm.\n
30892---- Key '"${c}"SPACE"$n"' does a single iteration of k-means and pauses.\n
30893---- Key '"${c}"ENTER"$n"' runs k-means algorithm.\n
30894---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
30895---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
30896---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30897----\n
30898--------------------------------------------------------------"
30899
30900  if !$! sp ? fi
30901  k[0] to_rgb if h>300 r2dy 300 round 1 fi nm. img        # Resize input image if necessary.
30902  +r {w*h},1,1,3,-1 r. {min(w,8192)},1,1,3 nm. colors     # Get reduced set of image colors.
30903  $1,1,1,3 rand. 0,255 round. 1 nm. centroids             # Initialize random centroids.
30904  _x_quantize_rgb_3d (1,0,0,0;0,1,0,0;0,0,1,0) nm. pose3d # Init 3D object.
30905  _x_quantize_rgb_text "Colors",clustering0
30906  _x_quantize_rgb_text "Clusters",clustering1
30907  _x_quantize_rgb_text "Dithering: off",dithering0
30908  _x_quantize_rgb_text "Dithering: on",dithering1
30909  if {img,h<300} +r2dy[img] 300,1 else [img] fi           # Generate visualization canvas.
30910  {w+315},365,1,3,255 rm..
30911  rectangle. 4,4,305,305,1,0xFFFFFFFF,0
30912  rectangle. 309,4,{w-5},305,1,0xFFFFFFFF,0
30913  rectangle. 4,309,{w-5},360,1,0xFFFFFFFF,0
30914  .,.
30915  rectangle. 310,5,{w-6},305,1,1
30916  rectangle. 5,310,{w-6},360,1,2
30917  300,300,1,1,'(y<<11)+(x<<2)+3' j.. .,5,5 rm.
30918  a[-2,-1] c nm. visu
30919
30920  # Start k-means iterations.
30921  dithering=0 clustering=0 pause=1 s0=off s1=on
30922  do
30923
30924    # Create and display visualization.
30925    if !narg($visu_3d) # Update 3D vizualization.
30926      +-[centroids] 2 ++[centroids] 2 a[-2,-1] x permute. cxyz y. -. 128
30927      j[obj3d] .,0,8 rm.  # Update centroids position in 3D object.
30928      [obj3d]
30929      if $clustering
30930        if {colors,iM}<256 # Estimate nearest centroids for all colors.
30931          +index[colors] [centroids] *. 256 +[colors,-1]
30932        fi
30933        +channels[colors] 0 >>. 8 map. 2 permute. cxyz y. j.. .,0,{{-2,h}-$_N-h} rm.
30934      fi
30935
30936      pose3d. {pose3d,^} 300,300,1,3 j3d. ..,50%,50%,100,1,2,0,0,300 rm..
30937      j. [clustering$clustering],2,0,0,0,1,[mclustering$clustering],255
30938      nm. visu_3d j[visu] [visu_3d],5,5
30939    fi
30940
30941    if !narg($visu_img) # Update indexed image.
30942      +index[img] [centroids],{0.7*$dithering},1 if h<300 r2dy. 300,1 fi
30943      j. [dithering$dithering],2,0,0,0,1,[mdithering$dithering],255
30944      nm. visu_img j[visu] [visu_img],310,5
30945    fi
30946
30947    if !narg($visu_centroids) # Update colormap.
30948      +luminance[centroids] a. [centroids],y sort. +,x rows. 1 r. {visu,w-10},50,1,3
30949      0 t. "Colors: "{centroids,w},2,0,16,1,255,255,255 +dilate. 3 j... ..,2,2,0,0,1,.,255
30950      rm[-2,-1] nm. visu_centroids j[visu] [visu_centroids],5,310
30951    fi
30952    l[visu]
30953    w -1,-1,0,"[G"{`39`}"MIC] RGB Quantization"
30954    if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
30955    endl
30956
30957    # Check for user's interactions.
30958    x={int({*,x}*{visu,w}/{*,w})}
30959    y={int({*,y}*{visu,h}/{*,h})}
30960    b={*,b}
30961    i={visu,i($x,$y,0,3)}
30962    if $b&1" && "$i==1 # Toggle dithering.
30963      dithering={!$dithering} rm[visu_img] wait -1
30964    elif $b&1" && "$i==2 # Add new color.
30965      (${-rgb}) y. c a[centroids,-1] x  _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
30966      pause=1 wait 100
30967    elif $b&2" && "$i==2" && "{centroids,w}>2 # Remove color.
30968      r[centroids] {centroids,w-1} _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
30969      pause=1 wait 100
30970    elif $b&2" && "$i>=3 # Toggle clusters/colors mode.
30971      clustering={!$clustering} rm[visu_3d] wait -1
30972    elif {*,M} # Init colormap with median-cut.
30973      +&[colors] 255 colormap. {centroids,w},0,0 rm[centroids] nm. centroids
30974      _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
30975      pause=1 wait -1
30976    elif {*,R} # Init colormap with random values.
30977      rand[centroids] 0,255 round[centroids] 1
30978      _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
30979      pause=1 wait -1
30980    elif {*,U} # Init colormap with uniform sampling.
30981      uniform_distribution {centroids,w},3 *. 255 rm[centroids] nm. centroids
30982      _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
30983      pause=1 wait -1
30984    elif {*,ENTER} # Start k-means iterations.
30985      pause=0
30986    elif $b&1" && "$i>=3 # Manage 3D view rotation.
30987      coords={visu,i($x,$y,0,3)-3} u1={(($coords>>2)&511)-150} v1={($coords>>11)-150}
30988      if !narg($u0) u0=$u1 v0=$v1 fi
30989      if $u0!=$u1" || "$v0!=$v1
30990        n0={sqrt(($u0)^2+($v0)^2)}
30991        nu0={if($n0>135,$u0*135/$n0,$u0)} nv0={if($n0>135,$v0*135/$n0,$v0)} nw0={sqrt(max(0,18225-($nu0)^2-($nv0)^2))}
30992        n1={sqrt(($u1)^2+($v1)^2)}
30993        nu1={if($n1>135,$u1*135/$n1,$u1)} nv1={if($n1>135,$v1*135/$n1,$v1)} nw1={sqrt(max(0,18225-($nu1)^2-($nv1)^2))}
30994        u={$nv0*$nw1-$nw0*$nv1} v={$nw0*$nu1-$nu0*$nw1} w={$nv0*$nu1-$nu0*$nv1} n={sqrt(($u)^2+($v)^2+($w)^2)}
30995        rotation3d[] $u,$v,$w,{-asin($n/18225)*180/pi} mv[pose3d] $! m*[-2,-1] nm. pose3d
30996        u0=$u1 v0=$v1 rm[visu_3d]
30997      fi
30998    elif !($b&1) u0=
30999    fi
31000
31001    if !$pause" || "{*,SPACE}  # Do one iteration of k-means.
31002      pause={*,-SPACE}
31003
31004      # Estimate new centroids positions.
31005      &[colors] 255 +index[colors] [centroids] *. 256 +[colors,-1]      # Estimate nearest centroids for all colors.
31006      repeat s#$colors                                                  # Recompute centroid positions.
31007        sh[colors] $> +histogram. {centroids,w*256},0,{centroids,w*256-1} rm..
31008        i.. 256,1,1,1,'x' r.. {w},1,1,1,0,2 *.. . r[-2,-1] {centroids,w},1,1,1,2 max. 0.01 /[-2,-1]
31009      done a[-{colors,s}--1] c
31010      rm[centroids] nm. centroids
31011
31012      # Reassign unused centroids.
31013      +>>[colors] 8 channels. 0 histogram. {centroids,w},0,{centroids,w-1}
31014      cmax={xM}
31015      repeat w if !i($>) point[centroids] $>,0,0,1,{centroids,I($cmax)} point[centroids] $>,0,0,-0.001,${-rgb} fi done
31016      rm. c[centroids] 0,255
31017
31018      if $visu_3d rm[visu_3d] fi
31019      if $visu_img rm[visu_img] fi
31020      if $visu_centroids rm[visu_centroids] fi
31021      wait 20
31022
31023    else if $visu_img wait fi
31024    fi
31025
31026  while {*}" && "!{*,Q}" && "!{*,ESC}
31027  rm w 0
31028
31029 _x_quantize_rgb_3d :
31030  if $obj3d rm[obj3d] fi
31031  +distribution3d[centroids] circles3d. 5 col3d. 255      # Pre-compute 3D object.
31032  colorcube3d p3d. 1
31033  +&[colors] 255 distribution3d. circles3d. 3 o3d. 0.25 +3d[-3--1]
31034  -3d. 128,128,128 nm. obj3d _N={i[7]}
31035
31036_x_quantize_rgb_text :
31037  0 t. "$1",0,0,16,1,255 r. {w+2},15,1,1,0,0,0.5,0.5 +dilate. 3 to_rgb..
31038  nm.. $2 nm. m$2
31039
31040#@cli x_reflection3d
31041#@cli : Launch the 3D reflection demo.
31042x_reflection3d : check_display $0
31043  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31044  e[] "\n
31045------ "${g}"3D reflection"$n" ----------------------\n
31046----\n
31047---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
31048---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
31049---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31050----\n
31051-------------------------------------------"
31052
31053  # Render background.
31054  128,256,1,3 rand. 0,255 plasma. 1,100 blur_xy. 30,2
31055  sh. 0 n. 0,90 rm. sh. 1 n. 0,60 rm. sh. 2 n. 0,180 rm.
31056  +mirror. x [-2,-1] a[-4--1] x
31057  +luminance. mirror. x b. 2 n. 0,255
31058
31059  # Create 3D objects.
31060  torus3d 30,10 col3d. 255,200,0
31061  spherical3d 47,34,"80+20*abs(cos(2*theta))" s3d. rm.. i.. 3,{h},1,1,150,220,255,200,255,255 y.. a[-6--1] y
31062  spherical3d 47,34,"100*abs(1+0.6*cos(3*phi)*sin(4*theta))"
31063  r3d[-2,-1] 0,1,0,90 db3d 0
31064
31065  # Start animation loop.
31066  xb,xl,anim=0
31067  w[] 400,400,0,"[G"{`39`}"MIC] 3D Reflection"
31068  do
31069
31070    tic=$|
31071    # Recreate 3D interpolated background object.
31072    +rows. 8,{8+3*i[6]-1} +j... .,0,8,0,0,{if($anim<250,0,0.5-0.5*cos(($anim-250)/100))} rm..
31073
31074    # Render 3D background object (with flat colors).
31075    +z[-6] $xb,0,{$xb+255},255 j3d. ..,75%,50%,0,1,3,0,0
31076
31077    # Render light reflection map.
31078    +z[-6] $xl,0,{$xl+255},255
31079    xf={min(30,$anim-70)+20*cos(1.8*$|)}
31080    yf={50+20*sin(2.7*$|)}
31081    j3d. [-6],{20+$xf}%,$yf%,0,1,4,0,0
31082
31083    # Add light reflection to 3D background object.
31084    l3d . rm. +j3d. ..,75%,50%,0,1,5,0,0 j.. .,0,0,0,0,0.6 rm[-3,-1]
31085
31086    # Add 3D foreground object.
31087    j3d. [-4],$xf%,$yf%,0,1,4,0,0
31088
31089    # Display frame and update animation variables.
31090    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-19},13,1,0.2 fi
31091    w. rm.
31092
31093    if {*,CTRLLEFT}" && "{*,D} w[] 800,800 elif {*,CTRLLEFT}" && "{*,C} w[] 400,400 fi
31094    xb={($xb+6)&255}
31095    xl={($xl-6)&255}
31096    anim+=1
31097    r3d[-2,-1] {sin(0.5*$|)},{cos($|)},1,{max(0.005,$|-$tic)*33}
31098    r3d... -1,0.3,0.8,{max(0.005,$|-$tic)*100}
31099    wait 20
31100  while {*}" && "!{*,ESC}" && "!{*,Q}
31101  rm[-5--1] w[] 0
31102
31103#@cli x_rubber3d
31104#@cli : Launch the 3D rubber object demo.
31105x_rubber3d : check_display $0
31106  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31107  e[] "\n
31108------ "${g}"3D rubber object"$n" -------------------\n
31109----\n
31110---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
31111---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
31112---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31113----\n
31114-------------------------------------------"
31115  rm
31116  sphere3d 150,0 torus3d 70,15 cylinder3d 20,40
31117  col3d... 200,200,200,0.3 col3d.. 128,200,76 col3d. 200,128,76
31118  c3d[-3--1] r3d. 1,0,0,70 +3d[-3--1] +3d. 10,-8,20 *3d. 1.5
31119  400,400,64,3
31120  {w},{h},1,3,'if(c==0,x,if(c==1,y,y*{1,d}/h))'
31121  {w},{h},1,3
31122  w[] {w},{h},0,"[G"{`39`}"MIC] 3D Rubber Object"
31123  frame=0
31124  do
31125    fps=${-fps}
31126    {w},{h},1,3 fc. 16,32,32 j3d. [0],50%,50%,0,1,3,0,0 j[1] .,0,0,$frame rm.
31127    r3d[0] 0.1,1,0.6,{3*cos($|*1.25)} r3d[0] 1,0.2,0.6,-1
31128    +warp[1] [2],0,0 *[3] 0.8 *. 0.2 +[3] . rm.
31129    if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi
31130    w.
31131    if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
31132    wait[0] 20
31133    sh[2] 2 -. 1 &. {{1,d}-1} rm.
31134    frame={($frame-1)%{1,d}}
31135  while {*}" && "!{*,ESC}" && "!{*,Q}
31136  rm w 0
31137
31138#@cli x_segment : _max_resolution={ 0 | >=128 }
31139#@cli : Segment foreground from background in selected opaque RGB images, interactively.
31140#@cli : Return RGBA images with binary alpha-channels.
31141#@cli : Default value: 'max_resolution=1024'.
31142x_segment : check "${1=1024}==0 || $1>=128" check_display $0
31143  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31144  e[^-1] "Extract foreground from background in image$? interactively, with maximum resolution $1."
31145  e[] "\n
31146----------------------------------------------------------------------------------------------------\n
31147----\n
31148---- "${c}"Left mouse button"$n" or key '"${c}"F"$n"' create a new foreground control point
31149 (or move an existing one).\n
31150---- "${c}"Right mouse button"$n" or key '"${c}"B"$n"' create a new background control point
31151 (or move an existing one).\n
31152---- "${c}"Mouse wheel"$n", or keys '"${c}"CTRL+arrows UP/DOWN"$n"' zoom view in/out.\n
31153---- '"${c}"CTRL+mouse wheel"$n"', '"${c}"SHIFT+mouse wheel"$n"' or "${c}"arrow keys"$n" move image in zoomed view.\n
31154---- Key '"${c}"SPACE"$n"' updates the extraction mask.\n
31155---- Key '"${c}"TAB"$n"' toggles background view modes.\n
31156---- Key '"${c}"M"$n"' toggles marker view modes.\n
31157---- Key '"${c}"BACKSPACE"$n"' deletes the last control point added.\n
31158---- Key '"${c}"PAGE UP"$n"' increases background opacity.\n
31159---- Key '"${c}"PAGE DOWN"$n"' decreases background opacity.\n
31160---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
31161---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
31162---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
31163---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n
31164----\n
31165----------------------------------------------------------------------------------------------------"
31166  repeat $! l[$>]
31167
31168    # Init variables and images.
31169    name={0,n} title={0,b} if narg({0,x}) title=$title.{0,x} fi
31170    w={w} h={h} fdim=${fitscreen[]\ $w,$h} ww={arg(1,$fdim)} wh={arg(2,$fdim)} x0=0 y0=0 x1={w-1} y1={h-1}
31171    selection=-1 marker_mode=2 xpan=-1 ypan=-1 bg_mode=0 opacity=64
31172    to_rgb nm img
31173
31174    if narg($_gui_control_points)>=4 # Import list of control points from plug-in GUI.
31175      ($_gui_control_points) r. {w/4},4,1,1,-1
31176    else 0 # Empty list of control points.
31177    fi
31178    nm. points
31179
31180    # Compute potential map.
31181    if $1>0 if $w>$h +r2dx[img] {min($1,$w)},2 else +r2dy[img] {min($1,$h)},2 fi else [img] fi
31182    _x_segment.
31183    pw={potential,w} ph={potential,h}
31184
31185    # Start event loop.
31186    do
31187
31188      # Handle user events for zoom/navigation/resizing.
31189      wait
31190      x={*,x} y={*,y} b={*,b} o={*,-o}
31191      is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
31192      is_shift={{*,SHIFTLEFT}" || "{*,SHIFTRIGHT}}
31193      is_mouseout={$x<0" || "$y<0}
31194      x={$x0+$x*($x1-$x0+1)/$ww} y={$y0+$y*($y1-$y0+1)/$wh}
31195      oww=$ww owh=$wh ox0=$x0 oy0=$y0 ox1=$x1 oy1=$y1
31196
31197      if {*,r} # When window resized.
31198        nww={*,d} nwh={*,e} m={min($nww,$nwh)}
31199        cx={($x0+$x1)/2} cy={($y0+$y1)/2} dx={$nww*($x1-$x0+1)/$ww} dy={$nwh*($y1-$y0+1)/$wh}
31200        x0={$cx-$dx/2} x1={$cx+$dx/2}
31201        y0={$cy-$dy/2} y1={$cy+$dy/2}
31202        ww=$nww wh=$nwh
31203      elif $is_ctrl" && "{*,-D} # Increase window size.
31204        nww={min({*,u},$ww*1.25)} nwh={min({*,v},$wh*1.25)} m={min($nww,$nwh)}
31205        if $m==$nww ww=$m wh={$h*$m/$w} else ww={$w*$m/$h} wh=$m fi
31206      elif $is_ctrl" && "{*,-C} # Decrease window size.
31207        nww={$ww/1.25} nwh={$wh/1.25}
31208        if min($nww,$nwh)>=64 ww=$nww wh=$nwh fi
31209      elif $is_ctrl" && "{*,-R} # Reset window size.
31210        fdim=${fitscreen[]\ $w,$h} ww={arg(1,$fdim)} wh={arg(2,$fdim)}
31211        x0=0 y0=0 x1={$w-1} y1={$h-1}
31212      elif ($is_shift" && "$o<0)" || "{*,ARROWLEFT} # Go left.
31213        dx={($x1-$x0)/6} x0-=$dx x1-=$dx
31214      elif ($is_shift" && "$o>0)" || "{*,ARROWRIGHT} # Go right.
31215        dx={($x1-$x0)/6} x0+=$dx x1+=$dx
31216      elif ($is_ctrl" && "$o>0)" || "({*,ARROWUP}" && "!$is_ctrl) # Go up.
31217        dy={($y1-$y0)/6} y0-=$dy y1-=$dy
31218      elif ($is_ctrl" && "$o<0)" || "({*,ARROWDOWN}" && "!$is_ctrl) # Go down.
31219        dy={($y1-$y0)/6} y0+=$dy y1+=$dy
31220      elif $o>0" || "($is_ctrl" && "{*,ARROWUP}) # Zoom in.
31221        if $x1-$x0>16" && "$y1-$y0>16
31222          cx={if($x>=0" && "!{*,ARROWUP},$x,($x0+$x1)/2)}
31223          cy={if($y>=0" && "!{*,ARROWUP},$y,($y0+$y1)/2)}
31224          x0={$cx+($x0-$cx)*0.75} y0={$cy+($y0-$cy)*0.75}
31225          x1={$cx+($x1-$cx)*0.75} y1={$cy+($y1-$cy)*0.75}
31226        fi
31227      elif $o<0" || "($is_ctrl" && "{*,ARROWDOWN}) # Zoom out.
31228        zfactor={max(($x1-$x0+1)/$w,($y1-$y0+1)/$h)}
31229        if $zfactor<1.3
31230          cx={if($x>=0" && "!{*,ARROWDOWN},$x,($x0+$x1)/2)}
31231          cy={if($y>=0" && "!{*,ARROWDOWN},$y,($y0+$y1)/2)}
31232          x0={$cx+($x0-$cx)/0.75} y0={$cy+($y0-$cy)/0.75}
31233          x1={$cx+($x1-$cx)/0.75} y1={$cy+($y1-$cy)/0.75}
31234          dx={$zfactor^2*($w-$x0-$x1)/2} dy={$zfactor^2*($h-$y0-$y1)/2}
31235          x0+=$dx x1+=$dx y0+=$dy y1+=$dy
31236        else
31237          dx={($w-$x0-$x1)/2} dy={($h-$y0-$y1)/2}
31238          x0+=$dx x1+=$dx y0+=$dy y1+=$dy
31239        fi
31240      elif $b&4" && "!$is_mouseout # Pan.
31241        if $panx<0" && "$pany<0 panx=$x pany=$y
31242        else dx={round($panx-$x)} dy={round($pany-$y)} x0+=$dx y0+=$dy x1+=$dx y1+=$dy
31243        fi
31244      else panx=-1 pany=-1
31245      fi
31246      if $ww!=$oww" || "$wh!=$owh" || "$ox0!=$x0" || "$oy0!=$y0" || "$ox1!=$x1" || "$oy1!=$y1 rm[baseview] fi
31247
31248      # Handle events related to control points management.
31249      N={points,w}
31250      is_left_button={$b&1" || "{*,F}} is_right_button={$b&2" || "{*,B}}
31251      is_button={$is_left_button" || "$is_right_button}
31252      if narg($baseview)" && "$is_button" && "$x>=0" && "$y>=0" && "$x<$w" && "$y<$h
31253        if $selection==-1" && "$N # Check for selection of an existing point.
31254          ($x;$y) r. $N,2 -. [points] *. {max($ww,$wh)/max($x1-$x0,$y1-$y0)} sqr. s. y +[-2,-1]
31255          dmin={im} selection={if($dmin>25,-1,xm)} rm.
31256        fi
31257        if $selection>=0
31258          if $marker_mode # Move existing point.
31259            +columns[points] $selection ox={i[0]} oy={i[1]}
31260            =. $x =. $y,0,1 =. {1+$is_left_button},0,3
31261            j[points] .,$selection rm. rm[view]
31262          fi
31263        else # Add new foreground or background point.
31264          ($x;$y;0;{1+$is_left_button}) a[points,-1] x selection=$N if !$marker_mode marker_mode=2 fi rm[view]
31265        fi
31266      else selection=-1
31267        if {*,SPACE}" && "narg($labels) rm[labels] # Update labels.
31268        elif {*,TAB}" && "narg($baseview) # Toggle background view modes
31269          bg_mode={($bg_mode+1)%6} rm[baseview] wait -1
31270        elif {*,M}" && "narg($view) # Toggle markers view modes
31271          marker_mode={($marker_mode-1)%3} rm[view] wait -1
31272        elif {*,PAGEDOWN}" && "narg($baseview) # Decrease background opacity
31273          opacity={max(0,$opacity-32)} rm[baseview] wait -1
31274        elif {*,PAGEUP}" && "narg($baseview) # Increase background opacity
31275          opacity={min(255,$opacity+32)} rm[baseview] wait -1
31276        elif {*,BACKSPACE}" && "$N # Remove last point.
31277          if $N>1 z[points] 0,{$N-2}
31278          else i=$points rm[points] i[$i] 0 nm[$i] points
31279          fi rm[view] wait -1
31280        fi
31281      fi
31282
31283      # Manage zoomed view bounds.
31284      w2={round(($x1-$x0)/2)} h2={round(($y1-$y0)/2)}
31285      if $x0<-$w2 x1-={$x0+$w2} x0=-$w2 fi
31286      if $y0<-$h2 y1-={$y0+$h2} y0=-$h2 fi
31287      if $x1>=$w+$w2 x0+={$w-1+$w2-$x1} x1={$w-1+$w2} fi
31288      if $y1>=$h+$h2 y0+={$h-1+$h2-$y1} y1={$h-1+$h2} fi
31289
31290      # Render labels.
31291      if !narg($labels)
31292        N={points,w}
31293        if narg($view) to[view] "Processing...",5,5,20,2 w[view] fi
31294        if $N
31295          [points]
31296          sh. 0,0,0,0 *. {$pw/$w} rm.
31297          sh. 1,1,0,0 *. {$ph/$h} rm.
31298          pointcloud. -1,$pw,$ph dilate. 3
31299          watershed. [potential] -. 1
31300        else [potential],[potential],1,1,1
31301        fi
31302        nm. labels
31303        if narg($baseview) rm[baseview] fi
31304      fi
31305
31306      # Render base image.
31307      if !narg($baseview)
31308        nx0={$x0*$pw/$w} ny0={$y0*$ph/$h}
31309        nx1={$x1*$pw/$w} ny1={$y1*$ph/$h}
31310        +z[img] $x0,$y0,$x1,$y1
31311        r. $ww,$wh,1,100%,{if($ww<w" && "$wh<h,2,1)}
31312        +z[labels] $nx0,$ny0,$nx1,$ny1
31313        r. $ww,$wh,1,100%,{if($ww<w" && "$wh<h,2,3)}
31314        if $bg_mode>=3 *. -1 +. 1 fi
31315        *. {255-$opacity} +. $opacity a[-2,-1] c
31316        if $bg_mode%3>=1 i.. 100%,100%,1,3,{(($bg_mode-1)%3)*255} blend[-2,-1] alpha
31317        else drgba.
31318        fi
31319        nm. baseview
31320        if narg($view) rm[view] fi
31321      fi
31322
31323      # Render view.
31324      if !narg($view)
31325        [baseview] r. 100%,100%,1,3
31326        if $marker_mode
31327          if $marker_mode==2 rad1=5 rad2=3 opa=1 else rad1=3 rad2=2 opa=0.8 fi
31328          col0=255,0,0 col1=0,255,0
31329          repeat w#$points
31330            +columns[points] $> x={(i[0]-$x0)*$ww/(1+$x1-$x0)} y={(i[1]-$y0)*$wh/(1+$y1-$y0)} l={i[3]-1} rm.
31331            circle. $x,$y,$rad1,1,0 circle. $x,$y,$rad2,$opa,${col$l}
31332          done
31333        fi
31334
31335        nm. view
31336        w[view] $ww,$wh,0,$title
31337      fi
31338
31339    while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER}
31340
31341    # Recompute labels at full resolution.
31342    if narg($view) to[view] "Processing fullres...",5,5,20,2 w[view] fi
31343    k[img,points]
31344    N={points,w} status=
31345    if $N
31346      status={points,^}
31347      [img] _x_segment. pointcloud[points] -1,$w,$h
31348      zfact={{img,max(w,h)}/{potential,max(w,h)}} dilate[points] {int(3*$zfact)}
31349      watershed[points] [potential] -[points] 1 k[img,points]
31350      *. 255
31351    else k[img] [img],[img],1,1,255
31352    fi
31353    a c nm $name
31354
31355  endl done
31356  u $status   # Return control points of last image.
31357  w 0
31358
31359# Compute potential function.
31360_x_segment :
31361  b. 0.2% gradient_norm. f. '1/(1+i^2)'
31362  nm. potential
31363
31364#@cli x_select_color : _variable_name
31365#@cli : Display a RGB or RGBA color selector.
31366#@cli : Argument 'variable_name' specifies the variable that contains the selected color values (as R,G,B,[A])
31367#@cli : at any time.
31368#@cli : Its value specifies the initial selected color. Assigning '-1' to it forces the interactive window to close.
31369#@cli : Default value: 'variable_name=xsc_variable'.
31370x_select_color : skip ${1=xsc_variable} check_display $0
31371  rm
31372  n={narg($$1)} if !$n $1=0,0,0 fi
31373  rgba_mode={$n>=4} R={arg(1,$$1)} G={arg(2,$$1)} B={arg(3,$$1)} A={if($rgba_mode,arg(4,$$1),255)}
31374  e[^-1] "Open "${arg\ 1+$rgba_mode,RGB,RGBA}" color selector widget, with variable '$1' and starting color "\
31375             ($$1)"."
31376  if !{*} w[] {400+24*$rgba_mode},400,0,"Select a color" fi
31377  update_view=1 is_sv=0 is_h=0 is_a=0 colordb=0 is_thread_variable={arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_'}
31378
31379  # Manage color presets.
31380  m "add_preset : if !narg($_xsc_preset$""1) _xsc_preset$""1=$""2,$""3,$""4 fi"
31381  add_preset 0,0,0,0,0 add_preset 1,255,255,255 add_preset 2,255,0,0 add_preset 3,0,255,0
31382  add_preset 4,0,0,255 add_preset 5,255,255,0 add_preset 6,255,0,255 add_preset 7,0,255,255
31383  add_preset 8,50,50,50 add_preset 9,100,100,100 add_preset 10,150,150,150 add_preset 11,200,200,200
31384  um add_preset
31385  if !narg($_xsc_preset) _xsc_preset=11 fi
31386  ($R^$G^$B) c. 0,255 rgb2hsv. H={i[0]} S={i[1]} V={i[2]} rm.
31387
31388  # Start event loop.
31389  do
31390    w={*,d} h={*,e} x={*,x} y={*,y} b={*,b}
31391
31392    # Update base image.
31393    if !$!
31394      $w,$h,1,3,200
31395      if $rgba_mode x1={w-89} y1={h-57} x2={w-80} else x1={w-49} y1={h-57} x2={w-40} fi
31396      x0=8 y0=8 x3={$x2+31} x4={w-40} x5={$x4+31} x6={max($x0+232+32*$rgba_mode,w-152)} y6={$y1+7}
31397      rectangle {$x0-1},{$y0-1},{$x1+1},{$y1+1},1,0xFFFFFFFF,232
31398      line {$x0-1},{$y0-1},{$x1+1},{$y0-1},1,128
31399      line {$x0-1},{$y0-1},{$x0-1},{$y1+1},1,128
31400      (1;0) (0,1) r[-2,-1] {$x1-$x0+1},{$y1-$y0+1},1,1,3 i... 100%,100%,1,1,$H a[-3--1] c hsv2rgb. j.. .,$x0,$y0 rm.
31401      rectangle {$x2-1},{$y0-1},{$x3+1},{$y1+1},1,0xFFFFFFFF,232
31402      line {$x2-1},{$y0-1},{$x3+1},{$y0-1},1,128
31403      line {$x2-1},{$y0-1},{$x2-1},{$y1+1},1,128
31404      (359;0^1;1^1;1) r. {$x3-$x2+1},{$y1-$y0+1},1,3,3 hsv2rgb. j.. .,$x2,$y0 rm.
31405      if $rgba_mode
31406        rectangle {$x4-1},{$y0-1},{$x5+1},{$y1+1},1,0xFFFFFFFF,232
31407        line {$x4-1},{$y0-1},{$x5+1},{$y0-1},1,128
31408        line {$x4-1},{$y0-1},{$x4-1},{$y1+1},1,128
31409        (1;0) r. {$x5-$x4+1},{$y1-$y0+1},1,4,3 *. 255 drgba. j.. .,$x4,$y0 rm.
31410      fi
31411      t. "Current",$x0,{$y1+12},14,1,0
31412      if narg($_xsc_old)
31413        t. "Old",$x0,{$y1+34},14,1,0
31414        ($_xsc_old) y. c r. 48,16 drgba. r. {w+2},{h+2},1,3,0,0,0.5,0.5 j.. .,{$x0+55},{$y1+32} rm.
31415      fi
31416      repeat 12
31417        (${_xsc_preset$>}) -. 255 r. 4,1,1,1,0 +. 255
31418        y. c r. 18,18 drgba. frame. 1,1,{255*($>==$_xsc_preset)}
31419        j.. .,{$x6+($>%6)*25},{$y6+($>>=6)*25} rm.
31420      done
31421      update_view=1
31422    fi
31423
31424    # Update view.
31425    if $update_view
31426      .
31427      cx={$x0+$V*($x1-$x0)} cy={$y0+(1-$S)*($y1-$y0)}
31428      if $cx>$x0 line. {$cx-1},$y0,{$cx-1},$y1,1,200 fi
31429      if $cx<$x1 line. {$cx+1},$y0,{$cx+1},$y1,1,200 fi
31430      if $cy>$y0 line. $x0,{$cy-1},$x1,{$cy-1},1,200 fi
31431      if $cy<$y1 line. $x0,{$cy+1},$x1,{$cy+1},1,200 fi
31432      line. $x0,$cy,$x1,$cy,1,0 line. $cx,$y0,$cx,$y1,1,0
31433      cy={$y0+(359-$H)*($y1-$y0)/359}
31434      if $cy>$y0 line. $x2,{$cy-1},$x3,{$cy-1},1,200 fi
31435      if $cy<$y1 line. $x2,{$cy+1},$x3,{$cy+1},1,200 fi
31436      line. $x2,$cy,$x3,$cy,1,0
31437      if $rgba_mode
31438        cy={$y0+(255-$A)*($y1-$y0)/255}
31439        if $cy>$y0 line. $x4,{$cy-1},$x5,{$cy-1},1,200 fi
31440        if $cy<$y1 line. $x4,{$cy+1},$x5,{$cy+1},1,200 fi
31441        line. $x4,$cy,$x5,$cy,1,0
31442      fi
31443      ($H^$S^$V^$A) sh. 0,2 hsv2rgb. rm. round. R={i[0]} G={i[1]} B={i[2]}
31444      r. 48,16 drgba. r. {w+2},{h+2},1,3,0,0,0.5,0.5 j.. .,{$x0+55},{$y1+10} rm.
31445      t. "HSV ("{round($H)}","{round($S*255)}","{round($V*255)}")",{$x0+115},{$y1+24},14,1,0
31446      if $rgba_mode t. "RGBA ("$R","$G","$B","{round($A)}")",{$x0+115},{$y1+8},14,1,0
31447      else t. "RGB ("$R","$G","$B")",{$x0+115},{$y1+8},14,1,0
31448      fi
31449      ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'}
31450      f. if(i>=_'a'" && "i<=_'z',i+_'A'-_'a',i)
31451      t.. "html ""#"{t},{$x0+115},{$y1+40},14,1,0 rm.
31452      w. 100%,100%,0 rm.
31453      if $rgba_mode $1=$R,$G,$B,$A else $1=$R,$G,$B fi
31454      update_view=0
31455    fi
31456    if $is_thread_variable wait 50 else wait fi
31457
31458    # Manage window size.
31459    ww={*,w} wh={*,h}
31460    is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
31461    if {*,r} ww={*,d} wh={*,e}
31462    elif $is_ctrl" && "{*,-D} ww={1.25*$ww} wh={1.25*$wh}
31463    elif $is_ctrl" && "{*,-C} ww={0.8*$ww} wh={0.8*$wh}
31464    elif $is_ctrl" && "{*,R} ww={400+24*$rgba_mode} wh=400
31465    fi
31466    ww={max(200,$ww)} wh={max(200,$wh)}
31467    if $ww!={*,w}" || "$wh!={*,h} w[] $ww,$wh rm fi
31468
31469    # Manage user events.
31470    if $b&1" && "$x>=0" && "$y>=0
31471      if !$is_h" && "!$is_a" && "($is_sv" || "($x>=$x0" && "$x<=$x1" && "$y>=$y0" && "$y<=$y1)) # SV selection
31472        S={max(0,min(1,1-($y-$y0)/($y1-$y0)))} V={max(0,min(1,($x-$x0)/($x1-$x0)))}
31473        update_view=1 colordb=0 is_sv=1 k[0]
31474      elif !$is_sv" && "!$is_a" && "($is_h" || "($x>=$x2" && "$x<=$x3" && "$y>=$y0" && "$y<=$y1)) # H selection
31475        H={max(0,min(359,359-($y-$y0)*359/($y1-$y0)))}
31476        colordb=0 is_h=1 rm
31477      elif !$is_sv" && "!$is_h" && "($is_a" || "($x>=$x4" && "$x<=$x5" && "$y>=$y0" && "$y<=$y1)) # A selection
31478        A={round(max(0,min(255,255-($y-$y0)*255/($y1-$y0))))}
31479        colordb=0 is_a=1 update_view=1 k[0]
31480      elif !$is_sv" && "!$is_h" && "!$is_a" && "{narg($_xsc_old)}" && "$x>=$x0+55" && "$x<=$x0+102" && "\
31481           $y>=$y1+32" && "$y<=$y1+47 # Old color
31482        ($_xsc_old) y. c sh. 0,2 rgb2hsv. rm. H={i[0]} S={i[1]} V={i[2]} A={i[3]}
31483        colordb=0 rm
31484      elif !$is_sv" && "!$is_h" && "!$is_a" && "$x>=$x6" && "$x<=$x5" && "$y>=$y6" && "$y<=$y6+50" && "\
31485           ($x-$x6)%25<=20" && "($y-$y6)%25<=20 # Preset.
31486        p={int(($x-$x6)/25)+6*int(($y-$y6)/25)} (${_xsc_preset$p}) -. 255 r. 4,1,1,1,0 +. 255 y. c sh. 0,2 rgb2hsv. rm.
31487        H={i[0]} S={i[1]} V={i[2]} A={i[3]}
31488        colordb=0 rm
31489      elif !$is_sv" && "!$is_h" && "!$is_a" && "$x>=$x0+55" && "$x<=$x0+102" && "$y>=$y1+10" && "$y<=$y1+27
31490         # Add current as old and/or preset.
31491        _xsc_old=$R,$G,$B,$A colordb={($colordb+1)%2}
31492        if !$colordb # Double-click to add to preset.
31493          _xsc_preset$_xsc_preset=$R,$G,$B,$A _xsc_preset={($_xsc_preset-1)%12}
31494        fi
31495        rm wait -1
31496      else colordb=0
31497      fi
31498    elif !$b is_sv=0 is_h=0 is_a=0
31499    fi
31500    if {*,ARROWUP} colordb=0 S={min(1,$S+1/256)} update_view=1 k[0] wait -1
31501    elif {*,ARROWDOWN} colordb=0 S={max(0,$S-1/256)} update_view=1 k[0] wait -1
31502    elif {*,ARROWRIGHT} colordb=0 V={min(1,$V+1/256)} update_view=1 k[0] wait -1
31503    elif {*,ARROWLEFT} colordb=0 V={max(0,$V-1/256)} update_view=1 k[0] wait -1
31504    elif {*,PAGEUP} colordb=0 H={min(359,$H+1)} rm wait -1
31505    elif {*,PAGEDOWN} colordb=0 H={max(0,$H-1)} rm wait -1
31506    fi
31507
31508    # Check RGB variable modification from another thread.
31509    if ['$$1']=='-1' break fi # Close request
31510    if (($rgba_mode" && "['$$1']!='$R,$G,$B,$A')" || "(!$rgba_mode" && "['$$1']!='$R,$G,$B'))" && "\
31511       $x<0" && "$y<0" && "!$is_sv" && "!$is_h" && "!$is_a
31512      ($$1) y. c -. 255 r. 1,1,1,4,0 +. 255 sh. 0,2 rgb2hsv. rm.
31513      H={i[0]} S={i[1]} V={i[2]} A={i[3]} rm
31514    fi
31515
31516  while {*}" && "!{*,ESC}" && "!{*,Q}
31517  rm w 0
31518  if $rgba_mode u $R,$G,$B,$A else u $R,$G,$B fi
31519  _xsc_old=${}
31520
31521#@cli x_select_function1d : _variable_name,_background_curve_R,_background_curve_G,_background_curve_B
31522#@cli : Open an interactive window, where the user can defined its own 1D function.
31523#@cli : If an image is selected, it is used to display additional information :
31524#@cli :   - The first row defines the values of a background curve displayed on the window (e.g. an histogram).
31525#@cli :   - The 2nd, 3rd and 4th rows define the R,G,B color components displayed beside the X and Y axes.
31526#@cli : Argument 'variable_name' specifies the variable that contains the selected function keypoints at any time.
31527#@cli : Assigning '-1' to it forces the interactive window to close.
31528#@cli : Default values: 'variable_name=xsf_variable', 'background_curve_R=220', \
31529# 'background_curve_G=background_curve_B=background_curve_T'.
31530x_select_function1d : skip ${1=xsf_variable},${2=220},${3=$2},${4=$2} check_display $0
31531  e[^-1] "Open 1D function widget, with variable name '$1'."
31532  if $! k[0] fi
31533  is_additional_data=$!
31534  if !{*} w[] 400,400,0,"Create a 1D function" fi
31535  reset_w={*,w} reset_h={*,h}
31536  if !narg($$1) $1=0,0,100,100 fi
31537  ($$1) nm. points y. x r. 2,{w/2},1,1,-1
31538  is_thread_variable={arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_'} selected=-1 X=-1 Y=-1
31539  do
31540
31541    # Update base view.
31542    if !narg($baseview)
31543      {{*,d}-48},{{*,e}-48},1,3,255
31544      if $is_additional_data  # Render background graph.
31545        100%,100% +rows[0] 0 graph.. .,3,0,0,0,1,1 rm. c. 0,1
31546        +fc.. ${2-4} j... .,0,0,0,0,1,.. rm[-2,-1]
31547      fi
31548      grid. {(w-1)/8},{(h-1)/8},0,0,0.2,0xCCCCCCCC,0
31549      line. 0,100%,100%,0,0.2,0
31550       frame. 24,24,200
31551      rectangle. 23,23,{w-24},{h-24},1,0xFFFFFFFF,232 line. 23,23,23,{h-24},1,128 line. 23,23,{w-24},23,1,128
31552      if $is_additional_data" && "{0,h}>1 # Render colored X-axis guide.
31553        if {0,h}>2 +rows[0] 1,3 else +rows[0] 1 r. 100%,3 fi
31554        r. {-2,w-48},3,1,1,3 permute. xzcy r. 100%,8 frame. 1,1,0
31555        j.. .,23,{-2,h-19} rotate. -90 j.. .,{-2,w-19},23 rm.
31556      fi
31557      nm. baseview
31558      l rm[view] onfail endl
31559    fi
31560
31561    # Update view.
31562    if !narg($view)
31563      +z[baseview] 24,24,{baseview,w-25},{baseview,h-25} r. 200%,200%
31564
31565      # Draw curve.
31566      function1d[] 1,{points,^}
31567      l. c 0,100 transpose
31568        i[0] ({'CImg3d'},{h},{h-1})
31569        i.. 1,100%,1,1,y 1,100% a[-3--1] x
31570        1,{h-1},1,1,2 +f. y ++. 1 a[-3--1] x
31571        4,100%,1,1,1 y a y col3d 0
31572      endl
31573      *3d. {-2,(w-1)/100},{-2,(1-h)/100}
31574      j3d.. .,0,100%,0,1,1,0,0 rm.
31575
31576      # Draw control points.
31577      repeat h#$points
31578        x={points,i(0,$>)} y={100-{points,i(1,$>)}}
31579        circle. $x%,$y%,6,1,0xFFFFFFFF,0
31580      done
31581      if $selected>=0
31582        x={points,i(0,$selected)} y={100-{points,i(1,$selected)}}
31583        circle. $x%,$y%,3,1,0
31584      fi
31585
31586      r. 50%,50%,1,3,2
31587      +j[baseview] .,24,24 rm..
31588
31589      # Draw current coordinates.
31590      if $X>=0" && "$Y>=0 t. "X: "{min(255,round(255*$X/100))}" Y: "{min(255,round(255*$Y/100))},24,6,12,1 fi
31591      nm. view
31592      w[view]
31593    fi
31594
31595    if $is_thread_variable wait 50 else wait fi
31596
31597    # Manage user events.
31598    x={*,x} y={*,y} b={*,b} is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
31599    X={($x-24)*100/({*,w}-49)} Y={100-($y-24)*100/({*,h}-49)}
31600    oww={*,w} owh={*,h} ww=$oww wh=$owh
31601    if {*,r} ww={*,d} wh={*,e} # Resize window.
31602    elif $is_ctrl" && "{*,-D} ww={view,w*125%} wh={view,h*125%} # Increase window size.
31603    elif $is_ctrl" && "{*,-C} ww={view,w*75%} wh={view,h*75%} # Decrease window size.
31604    elif $is_ctrl" && "{*,R} ww=$reset_w wh=$reset_h # Reset window size.
31605    elif !$is_ctrl" && "{*,R} rm[points] (0,0;100,100) nm. points $1={points,^} rm[view] # Reset keypoints.
31606    elif $b&3 # Add/move/delete point.
31607      is_inside={$X>=0" && "$Y>=0" && "$X<=100" && "$Y<=100}
31608
31609      # Check for a point selection.
31610      if $selected<0 +f[points] 'sqrt((i-$X)^2+(j(1)-$Y)^2)*{*,w}%' z. 0,0 selected={if(im>8,-1,ym)} rm. fi
31611
31612      if $x>=0" && "$b&1" && "$selected>=0 # Move an existing point.
31613        if {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} X={points,i(0,$selected)} fi
31614        if {*,CTRLLEFT}" || "{*,CTRLRIGHT} Y={points,i(1,$selected)} fi
31615        if {points,$selected>0" && "$selected<h-1}
31616          =[points] {points,max(min($X,i(0,$selected+1)-0.5),i(0,$selected-1)+0.5)},0,$selected
31617        fi
31618        =[points] {min(100,max(0,$Y))},1,$selected $1={points,^} rm[view]
31619
31620      elif $b&1" && "$is_inside # Create new point.
31621        ($X,$Y) a[points,-1] y sort[points] +,y
31622        +f[points] 'sqrt((i-$X)^2+(j(1)-$Y)^2)*{*,w}%' z. 0,0 selected={if(im>8,-1,ym)} $1={points,^} rm[view,-1]
31623      elif $b&2" && "$selected>0" && "$selected<{points,h-1}" && "$is_inside # Delete an existing point.
31624        l[points] s y rm[$selected] a y endl wait -1 selected=-1 $1={points,^} rm[view]
31625      fi
31626    elif !($b&1) selected=-1
31627    fi
31628
31629    # Manage window size.
31630    ww={min(90%*{*,u},max(200,$ww))}
31631    wh={min(90%*{*,v},max(200,$wh))}
31632    if $oww!=$ww" || "$owh!=$wh w[] $ww,$wh rm[baseview,view] fi
31633
31634    # Check points variable modification from another thread.
31635    if ['$$1']=='-1' break fi # Close request
31636    if ['$$1']!=['{points,^}'] # Keypoints changed
31637      rm[points] ($$1) nm. points y. x r. 2,{w/2},1,1,-1 l rm[view] onfail endl
31638    fi
31639
31640  while {*}" && "!{*,ESC}" && "!{*,Q}
31641  w[] 0 u {points,^}
31642  if $is_additional_data rm[^0] else rm fi
31643
31644#@cli x_select_palette : _variable_name,_number_of_columns={ 0=auto | >0 }
31645#@cli : Open a RGB or RGBA color selector widget from a palette.
31646#@cli : The palette is given as a selected image.
31647#@cli : Argument 'variable_name' specifies the variable that contains the selected color values (as R,G,B,[A])
31648#@cli : at any time.
31649#@cli : Assigning '-1' to it forces the interactive window to close.
31650#@cli : Default values: 'variable_name=xsp_variable' and 'number_of_columns=2'.
31651x_select_palette : skip ${1=xsp_variable},${2=0} check_display $0
31652  if !$! error[0--3] "Command '$0': Missing specified palette image." fi
31653  k[0] +r {w*h*d},1,1,{s},-1 to_color. rgba_mode={s==4} to_rgba. nm. palette
31654  e[^-1] "Open "${arg\ 1+$rgba_mode,RGB,RGBA}" color selector widget for palette$?, with variable name '$1'."
31655
31656  if w>1024 error[0--3] "Command '$0': Too much colors ("{w}") in selected palette." fi
31657  if !{*} w[] 400,400,0,0,-1,-1,"Palette: "{0,b} fi
31658
31659  selected=-1 oselected=-1
31660  do
31661    ww={*,w} wh={*,h}
31662    R={palette,round(i($selected,0,0,0))} G={palette,round(i($selected,0,0,1))}
31663    B={palette,round(i($selected,0,0,2))} A={palette,round(i($selected,0,0,3))}
31664
31665    # Update color in specified variable.
31666    if $selected>=0" && "$oselected!=$selected
31667      if $rgba_mode $1=$R,$G,$B,$A else $1=$R,$G,$B fi
31668    fi
31669
31670    # Check close request from external thread.
31671    if ['$$1']=='-1' break fi
31672
31673    # Create base view.
31674    if !narg($baseview) l[palette]
31675      {w},1,1,1,x +. 1
31676      s. x append_tiles[^0] $2
31677      M={w} N={h} 100%,100%,1,1,1
31678      +r. {$ww-17},100%,1,1,4
31679      r.. 100%,{$wh-57},1,1,4
31680      r[-2,-1] .,.. -|[-2,-1]
31681      line. 100%,0,100%,100%,1,1
31682      line. 0,100%,100%,100%,1,1
31683      -. 1 *. -1
31684      r.. .,.,1,1,1 -.. 1
31685      +map.. [0],0 drgba.
31686      rv[-2,-1] *[-2,-1]
31687      +!=.. -1 dilate. 3
31688      mv... $! +. 1 a[-3--1] c
31689      nm. baseview
31690    endl
31691    if narg($view) rm[view] fi
31692    fi
31693
31694    # Create and display view.
31695    if !narg($view)
31696      $ww,$wh,1,3,200
31697      if $selected<0 sh[baseview] 0,2
31698      else
31699        +channels[baseview] 0,2 +channels[baseview] 4,4
31700        !=. {$selected+1} rectangle. 0,0,100%,100%,1,0xFFFFFFFF,1
31701        +dilate. 5 -[-2,-1] *. -1 +dilate. 5 *.. 255
31702        r.. 100%,100%,1,3 j... ..,0,0,0,0,1,. rm[-2,-1]
31703        if $rgba_mode t.. "RGBA ("$R","$G","$B","$A")",8,{$wh-45},14,1,0
31704        else t.. "RGB ("$R","$G","$B")",8,{$wh-45},14,1,0
31705        fi
31706        ($R^$G^$B) rgb2hsv. H={round(i[0])} S={round(i[1]*255)} V={round(i[2]*255)} rm.
31707        t.. "HSV ("$H","$S","$V")",8,{$wh-31},14,1,0
31708        ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'}
31709        f. if(i>=_'a'" && "i<=_'z',i+_'A'-_'a',i)
31710        t... "html ""#"{t},8,{$wh-17},14,1,0 rm.
31711      fi
31712      sh[baseview] 3 j... ..,8,8,0,0,1,. rm[-2,-1]
31713      nm. view w[view]
31714    fi
31715
31716    if arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_' wait 50 else wait fi
31717
31718    # Manage window size.
31719    is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
31720    if {*,r} ww={*,d} wh={*,e}
31721    elif $is_ctrl" && "{*,-D} ww={1.25*$ww} wh={1.25*$wh}
31722    elif $is_ctrl" && "{*,-C} ww={0.8*$ww} wh={0.8*$wh}
31723    elif $is_ctrl" && "{*,R} ww=400 wh=400
31724    fi
31725    ww={max(200,$ww)} wh={max(200,$wh)}
31726    if ($ww!={*,w}" || "$wh!={*,h})" && "narg($baseview) w[] $ww,$wh rm[baseview] fi
31727
31728    # Handle user events.
31729    oselected=$selected
31730    if narg($baseview)
31731      x={*,x} y={*,y} b={*,b}
31732      if $b&1" && "$x>=0" && "$y>=0  # Select color.
31733        if {baseview,i($x-8,$y-8,0,4)} selected={baseview,i($x-8,$y-8,0,4)-1} else selected=-1 fi
31734        rm[view] wait -1
31735      elif {*,ARROWUP}" && "$selected>=$M selected-=$M rm[view] wait -1
31736      elif {*,ARROWDOWN}" && "$selected<{0,w-$M} selected+=$M rm[view] wait -1
31737      elif {*,ARROWRIGHT}" && "$selected<{0,w-1} selected+=1 rm[view] wait -1
31738      elif {*,ARROWLEFT}" && "$selected>0 selected-=1 rm[view] wait -1
31739      fi
31740    fi
31741
31742  while {*}" && "!{*,ESC}" && "!{*,Q}
31743  w 0 k[0]
31744  if $selected>=0 if $rgba_mode u $R,$G,$B,$A else u $R,$G,$B fi else u -1 fi
31745
31746#@cli x_shadebobs
31747#@cli : Launch the shade bobs demo.
31748x_shadebobs : check_display $0
31749  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31750  e[] "\n
31751------ "${g}"Shade bobs"$n" -------------------------------\n
31752----\n
31753---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
31754---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
31755---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31756----\n
31757-------------------------------------------------"
31758  rm t=100 w 512,512,0,"[G"{`39`}"MIC] Shade Bobs"
31759
31760  # Start animation loop.
31761  do
31762    t+=0.015
31763    if $t>4*pi" || "{*,b} # Reset motions variables if necessary.
31764      rx={u(-1,1)} ry={u(-1,1)} rz={u(-1,1)} rt={u(-1,1)} rcx={u(-0.6*0.6)} t=0
31765      N={20+round(u(80))} R={(2+round(u(40)))*min({*,w},{*,h})/300}
31766      if $obj3d rm[colormap,img,obj3d] fi
31767      {4+round(u(12))},1,1,3 noise[0] 255,2 ==. 1 r[0] 256,1,1,3,3 *[0] 255 shift[0] 1 nm. colormap
31768      (67.5;73.5;109.5;103.5;51.5;100.5;{2*$N};$N) 3,{2*$N},1,1,0
31769      1,$N,1,1,5 2,$N,1,1,'y+x*$N' a[-2--1] x z. 0,5
31770      4,$N,1,1,1 y[-3--1] a[-4--1] y nm. obj3d
31771      {*,w},{*,h} nm. img
31772      wait -1
31773    fi
31774
31775    # Compute bobs coordinates.
31776    r={$ry+$rx*cos(6*$rz*$t)+(1-$rx)*sin(6*$rt*$t)}
31777    (0;{30*$ry*($N-1)}) ($t;{2*pi*($N-1)/$N+$t}) r[-2,-1] 1,$N,1,1,3
31778    +.. {360*sin($rz*$t)} *.. {pi/180}
31779    +sin[-2,-1] cos[-4,-3] *[-4,-2] $r *[-3,-1] $rcx +[-4,-3] +[-2,-1]
31780    *.. {{*,w}/2} *. {{*,h}/2} a[-2,-1] x
31781    ++. $R -.. $R a[-2,-1] y z. 0,2 y. j[obj3d] .,0,8 rm.
31782
31783    # Draw bobs, map colors and display.
31784    j3d[img] [obj3d],50%,50%,0,-1,2,0,0
31785    &[img] 255 +map[img] [colormap] w. rm. wait 20
31786    if {*,CTRLLEFT}" && "{*,D} w[] 1024,1024 elif {*,CTRLLEFT}" && "{*,C} w[] 512,512 fi
31787  while {*}" && "!{*,ESC}" && "!{*,Q}
31788  rm w 0
31789
31790#@cli x_spline
31791#@cli : Launch spline curve editor.
31792x_spline : check_display $0
31793  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31794  e[] "\n
31795------ "${g}"Spline curve editor"$n" --------------------------\n
31796----\n
31797---- "${c}"Mouse"$n" to insert/move/delete points.\n
31798---- Key '"${c}"R"$n"' to reset the curve.\n
31799---- Key '"${c}"SPACE"$n"' to shows/hide spline curve.\n
31800---- Key '"${c}"P"$n"' to shows/hide control points.\n
31801---- Key '"${c}"ENTER"$n"' to shows/hide control polygon.\n
31802---- Key '"${c}"T"$n"' to shows/hide point tangents.\n
31803---- Key '"${c}"I"$n"' to shows/hide point indices.\n
31804---- Key '"${c}"C"$n"' to shows/hide point coordinates.\n
31805---- Keys '"${c}"+"$n"' and '"${c}"-"$n"' to increase/decrease roundness.\n
31806---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31807----\n
31808-----------------------------------------------------"
31809
31810  # Init display and variables.
31811  if $! a x n 0,255 to_rgb else (0;0^0;128^0;0) r. 512,512,1,3,3 nm. "[G"{`39`}"MIC] Spline Editor" fi
31812  w[0] {0,w},{0,h},0,0,{n} r[0] {*,w},{*,h},1,3,1
31813  i[1] 1         # Point coordinates
31814  roundness=0.5  # Curve roundness
31815  visuflags=23   # Visualisation flags
31816  nearest=-1     # Nearest point
31817  active=-1      # Active point
31818
31819  # Start event loop.
31820  do
31821
31822    # Init coordinates [1] if necessary.
31823    if {1,whds}==1
31824      rm[1] roundness=0.5 nearest=-1 active=-1
31825      i[1] ({0.2*w},{0.2*h};\
31826            {0.2*w},{0.8*h};\
31827            {0.8*w},{0.8*h};\
31828            {0.8*w},{0.2*h})
31829    fi
31830
31831    # Estimate screen-normalized coordinates [2], curve tangents [3] and tangent orientations [4].
31832    [1] ({{*,w}/{0,w}},{{*,h}/{0,h}}) *[-2,-1]                       # Normalized coordinates.
31833    +shift[2] 0,-1,0,0,2 +shift[2] 0,1,0,0,2 -[-2,-1] *. $roundness  # Curve tangents.
31834    +s. x sqr[-2,-1] +[-2,-1] sqrt. r. 2 +/[-2,-1] rm..              # Tangent orientations.
31835
31836    # Display curve, control points, polygon and tangents.
31837    +r[0] {*,w},{*,h},1,3
31838    if $visuflags&4 polygon. {2,h},{2,^},0.3,128,200,255 fi
31839    repeat h#1
31840      line. {2,@0-3},0.3,255,255,0
31841      if $visuflags&1 spline. {2,@0-1},{3,@0-1},{2,@2-3},{3,@2-3},1,255 fi
31842      if $visuflags&8 line. {{2,@0}-{4,@0}*20},{{2,@1}-{4,@1}*20},{{2,@0}+{4,@0}*20},{{2,@1}+{4,@1}*20},1,0,255,0 fi
31843      if $visuflags&16 t. $>,{{2,@0}-3},{{2,@1}-18},13,1,255,255,0 fi
31844      if $visuflags&32 t. "("{round({1,@0})}","{round({1,@1})}")",{{2,@0}-16},{{2,@1}+10},13,1,100,200,255 fi
31845      shift[1-4] 0,-1,0,0,2
31846    done
31847    if $visuflags&2 repeat h#1
31848      ellipse. {2,@0-1},4,4,0,1,0,0,0 ellipse. {2,@0-1},2,2,0,1,255,100,155 shift[2] 0,1,0,0,2
31849    done fi
31850    w. rm[3,4,-1] wait
31851
31852    # Handle key events.
31853    if {*,SPACE} visuflags+={if($visuflags&1,-1,1)} wait -1 fi  # Show/hide spline
31854    if {*,P} visuflags+={if($visuflags&2,-2,2)} wait -1 fi      # Show/hide points
31855    if {*,ENTER} visuflags+={if($visuflags&4,-4,4)} wait -1 fi  # Show/hide polygon
31856    if {*,T} visuflags+={if($visuflags&8,-8,8)} wait -1 fi      # Show/hide tangents
31857    if {*,I} visuflags+={if($visuflags&16,-16,16)} wait -1 fi   # Show/hide indices
31858    if {*,C}" && "!{*,CTRLLEFT}" && "!{*,CTRLRIGHT} # Show/hide coordinates
31859      visuflags+={if($visuflags&32,-32,32)} wait -1 fi
31860    if {*,PADADD}" && "$roundness<1 roundness*=1.1 wait -1 fi    # Increase roundness
31861    if {*,PADSUB}" && "$roundness>0.1 roundness*=0.9 wait -1 fi  # Decrease roundness
31862    if {*,R}" && "!{*,CTRLLEFT}" && "!{*,CTRLRIGHT} rm. i[1] 1 wait -1 fi  # Reset curve
31863    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} fi # Increase window size
31864    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} fi # Decrease window size
31865    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} fi             # Reset window size
31866    if {*,r} w[] fi # Resize window if necessary.
31867
31868    # Set/unset active point.
31869    if {*,b}==0 active=-1                            # Unset active point if mouse button is released
31870    elif {*,x}>=0" && "{*,b}" && "$active==-1        # Find new active point
31871      [2] ({*,x},{*,y}) -[-2,-1] sqr. s. x +[-2,-1]  # Compute distance vector to points
31872      nearest={ym}                                   # Set nearest point
31873      if im<64 active=$nearest fi                    # Set it as active point, if near enough
31874      rm.
31875    fi
31876    rm[2]
31877
31878    # Move active point.
31879    if {*,b}&1" && "{*,x}>=0" && "$active!=-1
31880      =[1] {{*,x}*{0,w}/{*,w}},0,$active
31881      =[1] {{*,y}*{0,h}/{*,h}},1,$active
31882
31883    # Delete nearest point.
31884    elif {*,b}&2" && "{*,x}>=0" && "{1,h}>3
31885      l[1] s y rm[$nearest] a y endl wait -1
31886
31887    # Insert new active point.
31888    elif {*,b}&1" && "{*,x}>=0
31889      xy=({{*,x}*{0,w}/{*,w}},{{*,y}*{0,h}/{*,h}})  # Point coordinates in the image basis
31890      +shift[1] 0,-1,0,0,2 +. [1] /. 2              # Compute center of segments
31891      $xy -[-2,-1] sqr. s. x +[-2,-1]               # Compute distance vector to segments
31892      ns={ym} rm.                                   # Get nearest segment
31893      l[1] s y i[{$ns+1}] $xy a y endl              # Insert new point at right position
31894      active={$ns+1}                                # Set new active point as newly inserted
31895    fi
31896
31897  while {*}" && "!{*,ESC}" && "!{*,Q}
31898
31899  # Render spline as a tertiary mask for output.
31900  +shift[1] 0,-1,0,0,2 +shift[1] 0,1,0,0,2 -[-2,-1] *. $roundness
31901  [0],[0],1,1,2 rm[0]
31902  repeat h#1 spline. {0,@0-1},{1,@0-1},{0,@2-3},{1,@2-3},1,1 shift[0] 0,-1,0,0,2 shift[1] 0,-1,0,0,2 done
31903  flood. 0,0,0,0,0,1,0
31904
31905  # Exit properly.
31906  rm[0,1] w 0
31907
31908#@cli x_starfield3d
31909#@cli : Launch the 3D starfield demo.
31910x_starfield3d : check_display $0
31911  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31912  e[] "\n
31913------ "${g}"3D starfield"$n" ---------------------------------------\n
31914----\n
31915---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31916----\n
31917-----------------------------------------------------------"
31918  l[]
31919    ('G\47MIC') s x
31920    x=0 N=$! repeat $N 0 t. {$>,t},0,0,48,1,1 x$>=$x y$>=0 z$>={-3200-150*$>} x={$x+w+8} done k[50%--1]
31921    expand_xy 6,0 dilate_circ 5 b 0.5 expand_z 1,0 isosurface3d 10% *3d 1,1,5 rv3d
31922    repeat $N col3d[$>] ${-rgb} done
31923    0 t. "Version "${-strver},0,0,48,1,1 r2dy. 18 +f. 255 to_rgb.
31924    random3d 2500 col3d. 255 *3d. 320,200,1000 -3d. 160,100
31925    l3d 0,0,-600
31926    w[] 640,400,0,"[G"{`39`}"MIC] 3D Starfield"
31927    t0=0 t=0
31928
31929    do
31930      320,200,1,3
31931
31932      # Starfield.
31933      l.. s3d
31934        r[2] 3,{2,h/3},1,1,-1 s[2] x %[4] 1000
31935        +/[4] 1000 *. -1 n. 0,2 c. 0,1 sqr. j.. . rm.
31936        a[2-4] x
31937        y a y
31938      endl
31939      j3d. ..,50%,50%,-600,1,0,0,0,240 -3d.. 0,0,{min(12,$t0/10-4)}
31940
31941      # Torus.
31942      torus3d 100,30 col3d. 255,64,255
31943      +col3d. 64,64,255 r3d. 1,0,0,-90 +3d. 65,0,0
31944      +3d[-2,-1] c3d.
31945      r3d. 1,1,0,{-6*$t} r3d. 0,0,1,{2*$t}
31946      j3d.. .,{($t-200)*2}%,50%,0,0.25,3,0,0 rm.
31947
31948      # Letters.
31949      repeat $N
31950        +r3d[$>] 1,{$>%4},1,{-${z$>}/2}
31951        j3d.. .,{90+${x$>}},{60+${y$>}},${z$>},1,4,0,0 rm.
31952        z$>={tl=280+6*$<;if($t<tl,min(0,${z$>}+20),-20*($t-tl))}
31953      done
31954
31955      # Presents.
31956      if $t<280 op={max(0,min(1,($t-200)/20))}
31957      else op={max(0,1-($t-280)/20)}
31958      fi
31959      j. ...,{(w-{-3,w})/2},120,0,0,$op,[-4]
31960
31961      w. wait 30 rm.
31962      t0+=1 t={$t0%350}
31963      if !$t x=0 repeat 5 z$>={-3200-150*$>} done fi
31964
31965    while {*}" && "!{*,ESC}" && "!{*,Q}
31966    w[] 0 rm endl
31967
31968#@cli x_tetris
31969#@cli : Launch tetris game.
31970x_tetris : check_display $0
31971  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31972  e[] "\n
31973------ "${g}"Tetris"$n" --------------------------------------------\n
31974----\n
31975---- This is a G\47MIC implementation of the "${g}"Tetris"$n" game.\n
31976----\n
31977---- "${c}"Arrow keys"$n" to move/rotate the triominos.\n
31978---- Key '"${c}"SPACE"$n"' to make the current triomino falling.\n
31979---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31980----\n
31981----------------------------------------------------------"
31982  rm
31983
31984  # Init board and triominos shapes.
31985  4,1,1,1,1,1,1,1
31986  3,2,1,1,1,0,0,1,1,1
31987  3,2,1,1,0,0,1,1,1,1
31988  2,2,1,1,1,1,1,1
31989  3,2,1,1,0,1,1,1,1,0
31990  3,2,1,1,0,1,0,1,1,1
31991  3,2,1,1,1,1,0,0,1,1
31992  nm[-7--1] m0,m4,m8,m12,m16,m20,m24
31993
31994  if u<0.25  # Enable extended set.
31995    i 2,1,1,1,1,1
31996    i 2,2,1,1,1,1,0,1
31997    i 3,1,1,1,1,1,1
31998    i 1,1,1,1,1
31999    i 3,2,1,1,1,1,1,1,0,1
32000    i 3,3,1,1,1,1,1,1,0,1,1,1,1
32001    nm[-6--1] m28,m32,m36,m40,m44,m48
32002  fi
32003  repeat $! i={4*$>} l[m$i] repeat 3 +rotate[0] {90*($>+1)} nm. m{$i+$>+1} done endl done
32004  N=$!
32005
32006  # Render triomino colored sprites.
32007  3,$N,1,1,'u(16,224)' r. 3,400% nm. colors
32008  (0,-1,0;1,0,-1;0,1,0) *. 120 nm. mask
32009  repeat $N
32010    +r[m$>] 500%,500%,1,3 +correlate. [mask],0 r. 200%,200%,1,1,3 ri.. . *[-2,-1] c. 30%,100%
32011    +r[m$>] .,.,1,3 +replace_color. 0,0,1,1,1,{colors,@{3*$>}-{3*$>+2}} rv[-3,-1] +[-3,-1] c.. 0,255
32012    channels. 0 *. 255 a[-2,-1] c nm. s$>
32013  done
32014  rm[colors,mask]
32015  fact={{s0,w}/{m0,w}}
32016
32017  # Generate board and background.
32018  W=12 H=20
32019  $W,$H . nm[-2,-1] board,curr_board
32020  {$fact*$W},{$fact*$H},1,3 . nm[-2,-1] render,curr_render +channels. 0 nm. curr_render_mask
32021  +rows[render] 0,50% plasma. 1,2 noise. 20 blur_y. 40%,1 +mirror. y a[-2,-1] y ri. [render]
32022  n. 0,64 blur_x. 1 100%,100% noise. 0.5,2 ==. 1 b. 1 *. 300 +[-2,-1] c. 0,255 nm. background
32023
32024  # Start game.
32025  time=$| score=0 fall_mode=0 gameover=0 n=-1 nn={round(u(0,$N-1))}
32026  do
32027    wait {if($fall_mode,-1,-20)}
32028
32029    # In case of game over.
32030    if $gameover
32031      +j[background] [curr_render],0,0,0,0,0.7,[curr_render_mask],255
32032      to. "Game\nOver!",22,30%,32,2,1,255 w. rm.
32033      continue
32034    fi
32035
32036    # Check for completed lines and select new random triomino.
32037    if $n<0
32038      l[board] s y i=-1 repeat $! if {$<,im} i=$<,$i fi done
32039      0 rm[$i] a y score+={2^(narg($i)-1)-1} r $W,$H,1,1,0,0,0,1 nm board endl
32040      if narg($i)>1 l[render] s y,$H 0 rm[$i] a y r {$fact*$W},{$fact*$H},1,3,0,0,0,1 nm render endl fi
32041      n=$nn nn={round(u(0,$N-1))} x={$W/2} y=0 do_render=1 fall_mode=0
32042    fi
32043
32044    # Render board at current time.
32045    if $do_render
32046      rm[curr_board,curr_render,curr_render_mask]
32047      [board] nm. curr_board j[curr_board] [m$n],{$x-int({m$n,w}/2)},$y,0,0,1,[m$n]
32048      [render] nm. curr_render sh[s$n] 3 j[curr_render] [s$n],{$fact*($x-int({m$n,w}/2))},{$fact*$y},0,0,1,.,255 rm.
32049      +*[curr_board] 255 r. [curr_render],[curr_render] nm. curr_render_mask
32050      0 t. "Score : "$score"    Next :",4,0,25,1,200 r. 50%,50%,1,3,2 +!=. 0 *. 255
32051      j[curr_render] ..,0,0,0,0,1,.,255 j[curr_render_mask] .,0,0,0,0,1,.,255 rm[-2,-1]
32052      +*[m$nn] 196 r. 300%,300%,1,3 j[curr_render,curr_render_mask] .,{{curr_render,w}-w-4},3,0,0,1,.,196 rm.
32053      do_render=0
32054    fi
32055
32056    +shift[background] 0,{round(-13*$|*1.04^$score)},0,0,2
32057    j. [curr_render],0,0,0,0,1,[curr_render_mask],255
32058    w. {2.25*w},{2.25*h},0,"[G"{`39`}"MIC] Tetris" rm. cursor[0] 0
32059
32060    # Manage user interactions.
32061    if {*,SPACE} fall_mode=1 fi
32062    if {*,ARROWUP}" || "{*,ARROWLEFT}" || "{*,ARROWRIGHT}
32063      an={if({*,ARROWUP},n=$n+1;if(n%4,n,n-4),$n)}
32064      nx={w2=int({m$an,w}/2);max(w2,min($x-{*,ARROWLEFT}+{*,ARROWRIGHT},$W-({m$an,w}%2)-w2))}
32065      +j[board] [m$an],{$nx-int({m$an,w}/2)},$y,0,0,-1,[m$an]
32066      if iM==1 x=$nx n=$an fi
32067      rm.
32068      do_render=1
32069    fi
32070
32071    if {*,ARROWDOWN}" || "$|-$time>0.9^int($score/2)" || "$fall_mode # Piece goes down.
32072      y+=1
32073      +j[board] [m$n],{$x-int({m$n,w}/2)},$y,0,0,-1,[m$n]
32074      if iM>1" || "$y+{m$n,h}>$H
32075        if $y<=1 gameover=1 fi  # Game over!
32076        j[board] [curr_board] j[render] [curr_render] n=-1
32077      fi
32078      rm.
32079      time=$| do_render=1
32080    fi
32081
32082  while {*}" && "!{*,ESC}" && "!{*,Q}
32083  rm w 0
32084
32085#@cli x_threshold
32086#@cli : Threshold selected images interactively.
32087x_threshold :
32088  e[^-1] "Threshold image"$_gmic_s" interactively."
32089  repeat $! l[$>]
32090    wsiz0=${"fitscreen ."}
32091    value=-1
32092    w[] $wsiz0,0,"[G'MIC] "{n}" - Interactive threshold"
32093    0
32094    for {*}" && "!{*,ESC}
32095
32096      if [w#1,h#1]!=[{*,w,h}] # Generate image view
32097       rm[1] +slices[0] 50% r. {*,w,h},1,100%,1 w.
32098      fi
32099
32100      mx,my,mb={*,x,y,b}
32101      if $mb" && "$mx>=0" && "$my>=0
32102        value={"w>h?( dw1 = "{*,w}" - 1; val = (dw1 - "$mx")*100/dw1; ):
32103                    ( dh1 = "{*,h}" - 1; val = (dh1 - "$my")*100/dh1; )"}
32104        update_view=1
32105      fi
32106
32107      if $update_view
32108        if $value>=0
32109          +ge[1] $value% *. 255
32110          to. "Threshold: "{_round($value,0.1)}%,1%,1%,{max(13,3.5*h%)}
32111          w. rm.
32112        else w.
32113        fi
32114        update_view=0
32115      fi
32116
32117      wait
32118      if {*,r} update_view=1 fi
32119      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D}
32120        w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 update_view=1
32121      fi
32122      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C}
32123        w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 update_view=1
32124      fi
32125      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R}
32126        w[] $wsiz0 wait -1 update_view=1
32127      fi
32128    done
32129    w[] 0 rm. u $value% ge ${}
32130 endl done
32131
32132#@cli x_tictactoe
32133#@cli : Launch tic-tac-toe game.
32134x_tictactoe : check_display $0
32135  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32136  e[] "\n
32137------ "${g}"Tic-Tac-Toe game"$n" -----------------\n
32138----\n
32139---- Use "${c}"mouse"$n" to select positions of the\n
32140---- symbols. Close window to exit game.\n
32141----\n
32142-----------------------------------------"
32143
32144  # Allocate variables.
32145  message=0                    # [-7] : State message.
32146  counter=0                    # [-6] : Turn counter (0 to 8).
32147  player=0                     # [-5] : Current player (0 or 1).
32148  state=0                      # [-4] : Board state.
32149  tmp3=0                       # [-3] : Temporary variable 3.
32150  tmp2=0                       # [-2] : Temporary variable 2.
32151  tmp1=0                       # [-1] : Temporary variable 1.
32152  _x_tictactoe2                # Generate board.
32153  w. -1,-1,0," "               # Init display window.
32154
32155  # Start main loop.
32156  do
32157
32158    # Set state message depending on the current player.
32159    if $player message="Tic-Tac-Toe (O to play)"
32160    else message="Tic-Tac-Toe (X to play)"
32161    fi
32162
32163    # Select position by the user.
32164    do # Enter event loop.
32165      w[] {w},{h},0,"[G"{`39`}"MIC] "$message wait # Wait for events and force window size if necessary.
32166      if !{*}" || "{*,ESC}" || "{*,Q} w[] 0 rm return fi # Quit properly if window has been closed.
32167      if {*,b}&1" && "{*,x}>20" && "{*,y}>20" && "{*,x}<400" && "{*,y}<400 # If mouse button pressed on the board area.
32168        tmp3={int(({*,x}-15)/130)}                                           # X of the selected position (0,1 or 2).
32169        tmp2={int(({*,y}-15)/130)}                                           # Y of the selected position (0,1 or 2).
32170        tmp1={4^($tmp2*3+$tmp3)}                                             # Get state code of the selected position.
32171        if int($state/$tmp1)%4 tmp1=-1 fi                                    # Check availability of position.
32172      else tmp1=-1 fi                                                        # If no mouse button, do nothing but loop.
32173    while $tmp1<0                                                            # Go on until a valid position selected.
32174
32175    # Draw symbol on selected position and update board state.
32176    _x_tictactoe{$player%2}                                                  # Generate the symbol sprite and his mask.
32177    j... ..,{"130*"$tmp3" + 15+u(-5,5)"},\                                   # Draw symbol (with some fuzzyness).
32178               {"130*"$tmp2" + 15+u(-5,5)"},0,0,1,.
32179    rm[-2--1]                                                                # Delete sprite and mask.
32180    w.                                                                       # Update display window.
32181    state+={(1+$player)*$tmp1}                                               # Update the board state.
32182
32183    # Check for a winning configuration.
32184    (21,1344,86016,4161,16644,66576,65793,4368;\                             # The list of winning configurations.
32185     0,0,0,0,1,2,0,0;\                                                       # Corresponding X coords for the stroke.
32186     0,1,2,0,0,0,0,0;\                                                       # Corresponding Y coords for the stroke.
32187     3,3,3,4,4,4,5,6)                                                        # Corresponding index of the stroke sprite.
32188    repeat w                                                                 # Start to check configurations.
32189      tmp1={@$>}                                                             # Save the current configuration code.
32190      if ($state&$tmp1)==$tmp1||($state&(2*$tmp1))==2*$tmp1                  # If a winner has been found.
32191        _x_tictactoe{i($>,3)}                                                # Generate the stroke symbol and his mask.
32192        j[-4] ..,{130*{-3,i($>,1)}+u(-5,5)},\                                # And display it on the board.
32193                        {130*{-3,i($>,2)}+u(-5,5)},0,0,1,. rm[-2--1]
32194        if ($state&$tmp1)==$tmp1 w.. -1,-1,0,"Tic-Tac-Toe (X won!)"
32195        else w.. -1,-1,0,"Tic-Tac-Toe (O won!)"                             # Update display window.
32196        fi
32197        do wait
32198          if {*} w[] {*,w},{*,h} fi
32199        while {*}" && "!{*,ESC}" && "!{*,Q}                                # Wait for the window to be closed.
32200        rm w[] 0 return                                               # And return properly.
32201      fi
32202    done                                                                     # Go on until all configurations checked.
32203    rm.                                                                      # Delete winning configuration data.
32204
32205    player={($player+1)%2}                                                  # Select next player.
32206    counter+=1                                                              # Increment turn counter.
32207  while $counter<9                                                       # Loop to next move until all positions filled.
32208
32209  # Here, the game has been ended without winners.
32210  w[] -1,-1,0,0,"Tic-Tac-Toe (Tied game!)"                                 # Change window title.
32211  do wait
32212    if {*} w[] {*,w},{*,h} fi
32213  while {*}" && "!{*,ESC}" && "!{*,Q}                                    # Wait for the window to be closed.
32214  w[] 0 rm                                                           # Return properly.
32215
32216# Generate Tic-Tac-Toe graphics.
32217_x_tictactoe : # Apply a hand-drawing effect.
32218  spread. 4 b. 6,1,0 sharpen. 0.8 n. 0,1
32219
32220__x_tictactoe : # Apply color to last image and generate corresponding opacity mask.
32221  +f. 1-i +n.. $2,255 +n... $3,255 n[-4] $1,255 a[-4,-2,-1] c
32222
32223_x_tictactoe0 : # Generate a 'X' and his mask.
32224  128,128,1,1,1 line. 15%,15%,85%,85%,1,0 line. 15%,85%,85%,15%,1,0 erode. 12
32225  _x_tictactoe deform. 4
32226  __x_tictactoe 40,40,160
32227
32228_x_tictactoe1 : # Generate a 'O' and his mask.
32229  128,128,1,1,1 ellipse. 50%,50%,22%,22%,0,1,0 ellipse. 50%,50%,15%,15%,0,1,1
32230  _x_tictactoe deform. 4
32231  __x_tictactoe 160,40,160
32232
32233_x_tictactoe2 : # Generate the board.
32234  391,391,1,1,"!(x%130) || !(y%130)" r. 421,421,1,1,0,0,0.5,0.5 dilate. 3 _x_tictactoe f. 1-i
32235  100%,100% noise. 10 b. 8,0 sharpen. 1.5 n. 220,255 *[-2,-1] to_rgb.
32236
32237_x_tictactoe3 : # Generate an horizontal stroke and his mask.
32238  421,130,1,1,1 line. 10%,60%,90%,60%,1,0 erode. 6
32239  _x_tictactoe rotate. {u(-6,6)},1,1,50%,50%
32240  __x_tictactoe 180,10,10
32241
32242_x_tictactoe4 : # Generate a vertical stroke and his mask.
32243  _x_tictactoe3 transpose[-2--1]
32244
32245_x_tictactoe5 : # Generate a ++ diagonal stroke and his mask.
32246  421,421,1,1,1 line. 10%,10%,90%,90%,1,0 erode. 6
32247  _x_tictactoe
32248  __x_tictactoe 180,10,10
32249
32250_x_tictactoe6 : # Generate a +- diagonal stroke and his mask.
32251  421,421,1,1,1 line. 10%,90%,90%,10%,1,0 erode. 6 _x_tictactoe __x_tictactoe 180,10,10
32252
32253#@cli x_warp : _nb_keypoints_xgrid>=2,_nb_keypoints_ygrid>=2,_nb_keypoints_contours>=0,\
32254# _preview_fidelity={ 0=coarsest | 1=coarse | 2=normal | 3=fine | 4=finest },\
32255# _[background_image],0<=_background_opacity<=1
32256#@cli : Launch the interactive image warper.
32257#@cli : Default values: 'nb_keypoints_xgrid=nb_keypoints_ygrid=2', 'nb_keypoints_contours=0' and 'preview_fidelity=1'.
32258x_warp : check "isint(${1=2}) && $1>=2 && isint(${2=$1}) && $2>=2 && isint(${3=0}) && $3>=0 &&
32259                isint(${4=1}) && $4>=0 && $4<=4 && ${6=0.5}>=0 && $6<=1" skip "${5=}" check_display $0
32260  if !$! error[0--3] "Command '$0': Requires at least one input image!" return fi
32261  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32262  e[] "\n
32263------ "${g}"Interactive image warper"$n" -----------------------------\n
32264----\n
32265---- "${c}"Left mouse button"$n": Add and move keypoint.\n
32266---- "${c}"Right mouse button"$n": Delete keypoint.\n
32267---- Key '"${c}"SPACE"$n"' or "${c}"middle mouse button"$n": Show/hide keypoints.\n
32268---- Key '"${c}"TAB"$n"': Change keypoint radius.\n
32269---- Key '"${c}"SHIFT"$n"': Toggle to original image.\n
32270---- Key '"${c}"R"$n"': Reset keypoints.\n
32271---- Keys '"${c}"ESC"$n"', '"${c}"ENTER"$n"' or '"${c}"Q"$n"': Process fullres and exit.\n
32272----\n
32273-------------------------------------------------------------"
32274
32275  if ${"is_image_arg $5"} pass$5 store. background fi
32276  radius_keypoints=4
32277
32278  repeat $! l[$>] nm={n}
32279    nm img
32280    if narg($background) $background rr2d. {-2,[w,h]},2,3 store. rbackground fi
32281
32282    # Start interactive window.
32283    selected_keypoint=-1
32284    view_keypoints=1
32285    do
32286
32287      # Generate base image.
32288      if !narg($imgb)
32289        if {*} wdims=${"fitscreen "{*,d},{img,{*,d}*h/w}}
32290        else wdims=${"fitscreen "{img,[w,h,1]},128,60%}
32291        fi
32292        w[] $wdims,0,"[G'MIC] Interactive Warp"
32293        +to_color[img] r. $wdims,1,100%,2 n. 0,255 nm. imgb
32294        rmn warp,imgw,imgr
32295      fi
32296
32297      # Generate set of keypoints.
32298      if !narg($keypoints)
32299        if narg($__x_warp_keypoints) ($__x_warp_keypoints) r. 1,{w/4},1,4,-1
32300        else
32301
32302          # Keypoints located on regular grid.
32303          nbp,nbq=$1,$2
32304          1,{$nbp*$nbq},1,4,"const nbp = "$nbp"; const nbq = "$nbq";
32305            p = y%nbp;
32306            q = int(y/nbp);
32307            x = p*100/(nbp - 1);
32308            y = q*100/(nbq - 1);
32309            [ x,y,x,y ]"
32310
32311          # Keypoints located on contours.
32312          nbc=$3
32313          if $nbc>0
32314            +b[imgb] 0.5 gradient_norm. sqrt. {round([w,h]/4)} gaussian. 20%
32315            1,$nbc,1,4,">
32316              begin(ref(crop(#-1),gauss));
32317              st = stats(#-2);
32318              iM = st[1];
32319              xM = st[8];
32320              yM = st[9];
32321              img = vector(#w#-1*h#-1,-iM);
32322              draw(#-2,img,xM - w#-1/2,yM - h#-1/2,0,0,w#-1,h#-1,1,1,-1,gauss);
32323              nxyM = [ xM,yM ]*100/([w#-2,h#-2]-1);
32324              [ nxyM,nxyM ]"
32325            rm[-3,-2]
32326            a[-2,-1] y
32327          fi
32328        fi
32329        N0={h} nm. keypoints
32330        rmn warp,imgw,imgr
32331      fi
32332
32333      # Generate warp field.
32334      if !narg($warp)
32335        subsamp={arg(1+$4,8,6,4,2,1)}
32336        +_x_warp_rbf[keypoints] {imgb,round([w,h]/$subsamp)} *. $subsamp
32337        r. {imgb,[w,h]},1,100%,3 +. '[x,y]'
32338        nm. warp
32339        rmn imgw,imgr
32340      fi
32341
32342      # Generate warped image.
32343      if !narg($imgw)
32344        +warp[imgb] [warp],0,1,3 nm. imgw
32345        rmn imgr
32346      fi
32347
32348      # Render visualization.
32349      if !narg($imgr)
32350        [imgw]
32351        if narg($rbackground) $rbackground ri. .. j.. .,0,0,0,0,$6 rm. fi
32352        nm. imgr
32353
32354        if s==4 drgba. fi
32355        if $view_keypoints
32356          eval[keypoints] "*
32357            begin(
32358              col1 = [ 64,200,255 ];
32359              col2 = [ 255,255,255 ];
32360              fact = ([ w#"$imgw",h#"$imgw" ] - 1)/100;
32361              const radius1 = "$radius_keypoints";
32362              const radius2 = radius1 + 2;
32363              const opacity = min(1,3/"($radius_keypoints-1)");
32364            );
32365            X = round((I)[0,2]*fact);
32366            ellipse(#-1,X,radius2,radius2,0,opacity,0);
32367            ellipse(#-1,X,radius1,radius1,0,opacity,y<"$N0"?col1:col2); I"
32368        fi
32369        w.
32370      fi
32371
32372      # Manage user interaction
32373      wait
32374      mb={*,b} mxy={[{*,x,y}]*100/([{*,w,h}]-1)} mouse_over={{*,x}>=0}
32375
32376      if $mouse_over" && "$mb" && "$selected_keypoint<0" && "h#$keypoints>0 # Determine selected keypoint
32377        selected_keypoint={keypoints,"dmin = inf; kmin = -1; fact = ([w#"$imgr",h#"$imgr"]-1)%;
32378          repeat (h,k,
32379            dist = norm(((I[k])[0,2] - ["$mxy"])*fact);
32380            dist<dmin?(dmin = dist; kmin = k)
32381          );
32382          kmin>=0 && dmin<"max(8,1.5*$radius_keypoints)"?kmin:-1"}
32383      fi
32384
32385      if {*,-SPACE}" || "$mb==4 view_keypoints={!$view_keypoints} rmn imgr fi # Show/hide keypoints
32386
32387      if {*,-R} rmn keypoints __x_warp_keypoints= fi # Reset keypoints
32388
32389      if {*,-TAB} # Change keypoint radius
32390        radius_keypoints={max(2,($radius_keypoints+2)%16)} view_keypoints=1 rmn imgr
32391      fi
32392
32393      if {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} # View original
32394        if {imgb,s==4} +drgba[imgb] w. rm. else w[imgb] fi
32395        do wait while {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} rmn imgr
32396      fi
32397
32398      if {*,r} rmn imgb fi # Window resize
32399
32400      if $mouse_over" && "$mb==1" && "$selected_keypoint<0 # Add keypoint
32401        wxy={warp,I([$mxy]*([w,h]-1)/100,1)*100/([w,h]-1)}
32402        ({"[ "$mxy,$wxy" ]"}) permute. zycx a[keypoints,-1] y
32403        selected_keypoint={keypoints,h-1}
32404        rmn warp
32405
32406      elif $mouse_over" && "$mb==1" && "$selected_keypoint>=0 # Move keypoint
32407        ({"[ "$mxy" ]"}) permute. zycx j[keypoints] .,0,$selected_keypoint rm.
32408        rmn warp
32409
32410      elif $mouse_over" && "$mb==2" && "$selected_keypoint>=0" && "h#$keypoints>4 # Remove keypoint
32411        1,1,1,4,-1 j[keypoints] .,0,$selected_keypoint rm. discard[keypoints] -1 r[keypoints] 1,{keypoints,h/4},1,4,-1
32412        N0-={$selected_keypoint<$N0?1:0} selected_keypoint=-1
32413        rmn warp
32414
32415      elif !$mb selected_keypoint=-1
32416      fi
32417    while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER}
32418    if !$< __x_warp_keypoints={keypoints,^} fi
32419
32420    +drgba[imgw] to. "Processing fullres...",5,5,20,2 w. rm.
32421    k[img,keypoints]
32422    _x_warp_rbf[keypoints] {img,[w,h]} +. '[x,y]'
32423    warp[img] .,0,2,3 rm. nm $nm
32424  endl done c 0,255 w 0
32425
32426# Generate 2d warping field from set of keypoints, using RBF reconstruction.
32427# $1,$2 = width,height
32428_x_warp_rbf :
32429  if !h $1,$2,1,2,[x,y] return fi
32430  $1,$2,1,2,"*
32431    begin(
32432      fact = ([w,h] - 1)/100;
32433      xy(p) = (I[#0,p])[0,2]*fact;
32434      const N = h#0;
32435      ref(vector(#N*N),A);
32436      ref(vector(#N*2),B);
32437      repeat (N,p,
32438        repeat (N,q,  A[q + N*p] = A[p + N*q] = norm(xy(p) - xy(q)));
32439        copy(B[2*p],(I[#0,p])[2,2]*fact - xy(p),2);
32440      );
32441      W = solve(A,B,2);
32442    );
32443    res = [ 0,0 ];
32444    repeat (N,p,res += W[2*p,2]*(norm([x,y] - xy(p))));"
32445  k.
32446
32447#@cli x_waves
32448#@cli : Launch the image waves demo.
32449x_waves : check_display $0
32450  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32451  e[] "\n
32452------ "${g}"Image waves"$n" --------------------------\n
32453----\n
32454---- "${c}"Left mouse button"$n" to drop balls.\n
32455---- "${c}"Right mouse button"$n" to rotate view.\n
32456---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
32457---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
32458---- Keys '"${c}"CTRL+F"$n"' to switch fullscreen mode.\n
32459---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
32460----\n
32461---------------------------------------------"
32462
32463  if !$! l[] # Generate fractal image
32464    200,200 x={-1.06-u*0.1} y={-0.26-u*0.1}
32465    mandelbrot $x,$y,{$x+0.1},{$y+0.1},256
32466    16,1,1,3,u r. 256,1,1,3,3 shift. 1
32467    map[0] . rm. r2dx 100
32468    +mirror y +mirror x + n 0,128
32469    shape_fern {2*w},70%,25 r2dx. {-2,3*w/4} to_rgb. ri. ..,0,0,0.5,0.5 n. 0,196 +[-2,-1] c. 0,255
32470  endl else k[0] r[0] 100,100,1,3,2 fi
32471  i[0] (20;80;0^20;80;0^20;80;0) r[0] 400,300,1,3,3 water[0] 100,2
32472  w[0] {0,1.25*w},{0,1.25*h},0,"[G"{`39`}"MIC] Image Waves"
32473  w={w} elevation3d. 0 rv3d.
32474  sh. 8,{7+3*i[6]},0,0 r. 3,{h/3},1,1,-1
32475  (0,1,0;1,0,1;0,1,0) /. 2
32476  ball[] 20,200,255,128,1,0.7,3.5
32477
32478  0 $w,$w .
32479  l3d {$w/2},-200,-1000 sl3d 0.4 ss3d 0.8 f3d 500 time0=$|
32480  do
32481    +convolve. [3],1 -. ... rm... b. 0.8 -. {ia} # Update height map.
32482    r. 1,{$w*$w},1,1,-1 j[2] .,2,0 r. $w,$w,1,1,-1 # Set 3D object coordinates.
32483    [1]
32484    if {5,h} +l[5] rows 0,2
32485      nb={w}
32486      i[0] ('CImg3d') i[1] ($nb,$nb) transpose[2]
32487      (1,0;1,{$nb-1}) r. 2,$nb,1,1,3 round.
32488      1,{4*$nb},1,1,1 y a y
32489    endl [4] sprites3d.. .,1 rm. +3d[-2,-1] fi
32490    -3d. {$w/2},{$w/2} *3d. {0,0.9*max(w,h)/$w} # Center and scale 3D object.
32491    r3d. 0,0,1,{if({*,b}&2,{*,x}*360/{*,w},$|*30)} r3d. 1,0,0,120 # Get rotated 3D object.
32492    +j3d[0] .,50%,65%,30,1,3,0,0
32493    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,2,0.2 fi
32494    w.
32495    if {*,CTRLLEFT}" && "{*,D} w[] {2.25*w},{2.25*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.25*w},{1.25*h}
32496    elif {*,CTRLLEFT}" && "{*,F}
32497      if !narg($is_fs) is_fs={*,w},{*,h} fw={min({*,u}*h/w,{*,v}*w/h)} w[] $fw,{$fw*h/w},0,1
32498      else w[] $is_fs,0,0 is_fs=""
32499      fi
32500    fi
32501
32502    rm[-2,-1] wait 20
32503    if {*,b}&1||($|-$time0)>1 ({u*$w};{u*$w};70;0) a[5,-1] x time0={$|-u} fi # Insert new ball.
32504    if {5,h} l[5,-1] # Manage ball motion and collision.
32505      sh[0] 2,2,0,0 sh[0] 3,3,0,0 -.. . +. 0.2 rm[-2,-1]
32506      s[0] x repeat $!-1 coords={$<,@0-1} if {$<,@2}<i($coords) =. {80+{i($coords)}},$coords rm[$<] fi done
32507      if $!==1 i[0] 0 else a[0--2] x fi
32508      endl
32509    fi
32510  while {*}" && "!{*,ESC}" && "!{*,Q}
32511  rm w 0
32512
32513#@cli x_whirl : _opacity>=0
32514#@cli : Launch the fractal whirls demo.
32515#@cli : Default values: 'opacity=0.2'.
32516x_whirl : check "${1=0.2}>=0" check_display $0
32517  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32518  e[] "\n
32519------ "${g}"Fractal whirls"$n" ----------------------------\n
32520----\n
32521---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
32522---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
32523---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
32524----\n
32525--------------------------------------------------"
32526
32527  5,5,1,3 256,256,1,3 [-1] w. 384,384,0,"[G"{`39`}"MIC] Fractal Whirls"
32528  tangle=0 tzoom=0 xc={(w-{-3,w})/2} yc={(h-{-3,h})/2}
32529  do
32530    rand... 0,255 j.. [-3],$xc,$yc,0,0
32531    f.. "*begin(R = rot(8*sin(-"$tangle")°)/(1.03+0.02*sin("$tzoom")); C=[w,h]/2); I((R*([x,y]-=C))+=C,0,0)"
32532    tangle+=0.001
32533    tzoom+=0.02
32534    j. [-2],0,0,0,0,$1 w.
32535    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
32536    wait 20
32537  while {*}" && "!{*,ESC}" && "!{*,Q}
32538  rm[-3,-2] w[] 0
32539
32540#-------------------------------------
32541#
32542# Define menu entries for
32543# the G'MIC plug-in
32544#
32545#-------------------------------------
32546
32547# Function that returns the list of external sources to be included in the plug-in.
32548# This command can be superseded on the user '.gmic' file to add sources for the plug-in.
32549# $1 = try network update
32550# $2 = try 'gui_filter_sources' defined in local update file.
32551gui_filter_sources : skip ${1=0},${2=1}
32552
32553  # Try to update the command 'gui_filter_sources' itself.
32554  if $2
32555    local=${_path_rc}update$_version.gmic
32556    if isfile(['{/$local}']) m $local fi
32557    if isfile(['{/$_path_user}']) m $_path_user fi
32558    gui_filter_sources $1,0
32559  fi
32560
32561  # Try to clean older update files to save disk space.
32562  if u>0.95 l[]
32563    files=${"files "$_path_rc"/update*.gmic"}
32564    repeat narg($files)
32565      file=${arg\ 1+$>,$files}
32566      ('$file') z. {[w-8,w-6]} ver={t} rm.
32567      if isnum($ver)" && "$ver<$_version delete $file rm. fi
32568    done
32569  onfail
32570  endl fi
32571
32572  # Build list of filter sources.
32573  ({'https://gmic.eu/update$_version.gmic'},1) # Include stdlib update (last 1 -> Override default filters).
32574  l[] _gui_filter_sources $1 onfail rm endl # Invoke custom command to include other filter sources.
32575  l[] i cimgz:${_path_rc}gui_filter_sources onfail endl # Include other user-defined filters sources.
32576  ('{/$_path_user}') # Include local '.gmic' (or 'user.gmic') file.
32577
32578# Function that always returns a valid preview size.
32579gui_preview_wh :
32580  u {0$_preview_width?[0$_preview_width,0$_preview_height]:[400,400]}
32581
32582# Function used for filters based on parallelization with spatial splitting.
32583gui_parallel_overlap :
32584  apo "$1",$3,{if($2,2^($2-1),0)}
32585
32586# Function that renders a single preview image from multiple preview images.
32587# Pre-cond : Number of image is $!>1.
32588gui_preview :
32589  frame 1,1,0,0,0,255 montage B
32590
32591# Return name of a layer.
32592gui_layer_name :
32593  u ${"_gui_merge_layers[0] name,[unnamed]"}
32594
32595# Return blending mode of a layer.
32596gui_layer_mode :
32597  u ${"_gui_merge_layers[0] mode,alpha"}
32598
32599# Return opacity of a layer.
32600gui_layer_opacity :
32601  u ${"_gui_merge_layers[0] opacity,100"}
32602
32603# Return (x,y) position of a layer.
32604gui_layer_pos :
32605  u ${"_gui_merge_layers[0] pos,0,0"}
32606
32607# Set name of a layer.
32608gui_set_layer_name :
32609  repeat $! l[$>]
32610    opacity=${-gui_layer_opacity} mode=${-gui_layer_mode} pos=${-gui_layer_pos}
32611    nm "name($1),mode("$mode"),opacity("$opacity"),pos("$pos")"
32612  endl done
32613
32614# Set blending mode of a layer.
32615gui_set_layer_mode :
32616  repeat $! l[$>]
32617    name=${-gui_layer_name} opacity=${-gui_layer_opacity} pos=${-gui_layer_pos}
32618    nm "name("$name"),mode($1),opacity("$opacity"),pos("$pos")"
32619  endl done
32620
32621# Set opacity of a layer.
32622gui_set_layer_opacity :
32623  repeat $! l[$>]
32624    name=${-gui_layer_name} mode=${-gui_layer_mode} pos=${-gui_layer_pos}
32625    nm "name("$name"),mode("$mode"),opacity($1),pos("$pos")"
32626  endl done
32627
32628# Set position of a layer.
32629gui_set_layer_pos :
32630  repeat $! l[$>]
32631    name=${-gui_layer_name} mode=${-gui_layer_mode} opacity=${-gui_layer_opacity}
32632    nm "name("$name"),mode("$mode"),opacity("$opacity"),pos("{round("$1")},{round("$2")}")"
32633  endl done
32634
32635# Flatten list of input layers according to their blending modes, opacities and positions,
32636# set in each layer name by the G'MIC plug-in.
32637gui_merge_layers :
32638  if !$! return fi
32639  mode0=${"_gui_merge_layers. mode,alpha"}
32640  opacity0=${"_gui_merge_layers. opacity,100"}
32641  pos0=${"_gui_merge_layers. pos,0,0"}
32642  if $opacity0<100" || "['$pos0']!='0,0' 100%,100%,1,4 fi
32643  wh0={w},{h}
32644  wh=${-max_wh} r. $wh,1,100%,0
32645  repeat $!-1 l[-2,-1] rv
32646    mode=${"_gui_merge_layers[1] mode,alpha"}
32647    opacity=${"_gui_merge_layers[1] opacity,100"}
32648    pos=${"_gui_merge_layers[1] pos,0,0"}
32649    to_a[1] r[1] $wh,1,100%,0
32650    shift[1] ${u\ $pos},0,0
32651    to_colormode[0,1] 0
32652    blend $mode,{max(0,min(1,$opacity/100))}
32653  endl done
32654  r $wh0,1,100%,0
32655
32656_gui_merge_layers :
32657  u {`"
32658    str = ["{'{n}'}"]; const sstr = size(str);
32659    def = ['${2--1}'];
32660    ker = ['$1(']; const sker = size(ker);
32661    const N = max(size(str),size(def));
32662    ref(vectorN(0),res);
32663    p = q = find(str,ker);
32664    p>=0?(
32665      q+=sker;
32666      r = find(str,'),',q);
32667      q = r>=0?r:(str[sstr-1]==_')'?sstr-1:-1);
32668    );
32669    q>=0?copy(res,str[p + sker],q - p - sker):(def = ['${2--1}']; copy(res,def,size(def)));
32670    res"`}
32671
32672# gui_split_preview : "command",_split_type,_split_posx,_split_posy
32673# 'split_type' can be { 0=no split | 1=forw. horiz. | 2=forw. vert. | 3=back. horiz. | 4=back. vert. |
32674# 5=dupl. left | 6=dupl. top | 7=dupl. bottom | 8=dupl. right | 9=dupl. horiz.l | 10=dupl. vert. |
32675# 11=checkered | 12=checkered inv. }
32676# if 'posx' or 'posy' are equal to 'nan', splitting is done at the middle, and moving is not possible.
32677# 'compute_entire_image' can be { 0=no | 1=yes }.
32678gui_split_preview : check "isint(${2=0}) && $2>=0 && $2<=12 && ${5=0}>=0" skip "${3=nan},${4=nan}"
32679  __split_preview="$1" m "_split_preview : run $__split_preview k[0]"
32680  is_movable={!isnan($3)" && "!isnan($4)}
32681  posx,posy={$is_movable?cut([$3,$4],0,100):[50,50]}
32682  pw,ph=${-gui_preview_wh}
32683  repeat $! l[$>]
32684    is_failed=0
32685    +l
32686      apply_timeout _split_preview,0$_preview_timeout
32687    onfail
32688      if 0$_is_timeout gui_timeout_preview
32689      else gui_error_preview ${}
32690      fi
32691      is_failed=1
32692    endl
32693
32694    if $is_failed" || "!$2 k.
32695    else
32696      drgba rr2d $pw,$ph,0,2
32697      r {[max(w#0,w#1),max(h#0,h#1)]},1,100%,0,0,0.5,0.5
32698      posx,posy={round([$posx,$posy]*([w,h]-1)%)}
32699
32700      if $2==1" || "$2==3 # Forward/backward horizontal
32701        1,[0],1,1,'y>=$posy?($2==1):($2==3)' r. [0],[0],1,1 j[0] [1],0,0,0,0,1,.
32702      elif $2==2" || "$2==4 # Forward/backward vertical
32703        [0],1,1,1,'x>=$posx?($2==2):($2==4)' r. [0],[0],1,1 j[0] [1],0,0,0,0,1,.
32704      elif $2==5 # Duplicate top
32705        j[0] [1],0,$posy
32706      elif $2==6 # Duplicate left
32707        j[0] [1],$posx
32708      elif $2==7 # Duplicate bottom
32709        j[0] [1],0,{$posy-h}
32710      elif $2==8 # Duplicate right
32711        j[0] [1],{$posx-w}
32712      elif $2==9 # Duplicate horizontal
32713        if !$posy k. elif $posy>=h k.. else r[0] 100%,$posy,1,100%,0,0,0,0.5 r[1] 100%,{h-$posy},1,100%,0,0,0,0.5 a y fi
32714      elif $2==10 # Duplicate vertical
32715        if !$posx k. elif $posx>=h k.. else r[0] $posx,100%,1,100%,0,0,0.5 r[1] {w-$posx},100%,1,100%,0,0,0.5 a x fi
32716      elif $2==11 # Checkered
32717        1,[0],1,1,'y>=$posy' [0],1,1,1,'x>=$posx' r[-2,-1] [0],[0],1,1 xor[-2,-1]
32718        j[0] [1],0,0,0,0,1,.
32719      elif $2==12 # Checkered reverse
32720        1,[0],1,1,'y<=$posy' [0],1,1,1,'x>=$posx' r[-2,-1] [0],[0],1,1 xor[-2,-1]
32721        j[0] [1],0,0,0,0,1,.
32722      fi
32723      k[0]
32724
32725      dir=0
32726      if isin($2,1,3,5,7,9,11,12)
32727        dir+=1
32728        line 0,$posy,100%,$posy,0.75,0xF0F0F0F0,255 line 0,$posy,100%,$posy,0.75,0x0F0F0F0F,0
32729        if $is_movable
32730          coords={p=[$posx,$posy];[p+[-10,-1],p+[10,-1],p+[0,-11]]}
32731          polygon 3,$coords,0.7,255
32732          polygon 3,$coords,0.7,0xFFFFFFFF,0
32733          coords={p=[$posx,$posy];[p+[-10,1],p+[10,1],p+[0,11]]}
32734          polygon 3,$coords,0.7,255
32735          polygon 3,$coords,0.7,0xFFFFFFFF,0
32736        fi
32737      fi
32738      if isin($2,2,4,6,8,10,11,12)
32739        dir+=2
32740        line $posx,0,$posx,100%,0.75,0xF0F0F0F0,255 line $posx,0,$posx,100%,0.75,0x0F0F0F0F,0
32741        if $2!=9" && "$is_movable
32742          coords={p=[$posx,$posy];[p+[-1,-10],p+[-1,10],p+[-11,0]]}
32743          polygon 3,$coords,0.7,255
32744          polygon 3,$coords,0.7,0xFFFFFFFF,0
32745          coords={p=[$posx,$posy];[p+[1,-10],p+[1,10],p+[11,0]]}
32746          polygon 3,$coords,0.7,255
32747          polygon 3,$coords,0.7,0xFFFFFFFF,0
32748        fi
32749      fi
32750      l[]
32751        0 +t. "After",0,0,20,1,255 t.. "Before",0,0,20,1,255
32752        autocrop 0 z[0] {0,[-1,-1,w,h]} z[1] {1,[-1,-1,w,h]}
32753        if isin($2,3,4,7,8,12) rv fi
32754        +dilate[-2,-1] 3 /[-2,-1] 255 r[-4,-3] 100%,100%,1,3
32755      endl
32756
32757      if {-4,"const c1 = "$posx">w+2; const c2 = "$posy">h+2; arg("$dir",c2,c1,c1&&c2)"}
32758        j[0] [-4],2,2,0,0,1,..
32759      fi
32760      if {-3,"const c1 = "$posx"<w#0-w-3; const c2 = $2<11?("$posy"<h#0-h-3):("$posy">h+2); arg("$dir",c2,c1,c1&&c2)"}
32761        j[0] [-3],{[w#0-3-w,isin($2,11,12)?2:h#0-3-h]},0,0,1,.
32762      fi
32763      k[0]
32764    fi
32765  endl done
32766  um _split_preview
32767
32768# Command to display text on a the G'MIC plug-in preview.
32769# $1 : header message
32770# $2 : header font size.
32771# $3 : main message
32772# $4 : main font size.
32773gui_print_preview : skip "${1=},${3=}" check "${2=32}>=0 && ${4=20}>=0"
32774
32775  # Resize image to output resolution.
32776  if $! k[0] fi
32777  drgba
32778  siz={0$_preview_width?[0$_preview_width,0$_preview_height]:[${fitscreen\ {[max(w,1),max(h,1),1]},400}]}
32779  sizw={arg(1,$siz)-8}
32780  if $! rr2d $siz,2,3 drgba else $siz,1,3,128 fi
32781  (1;0.5^1;0.5^0;1)
32782  (0,0.5,0;0.5,1,0.5;0,0.5,0) *. 0.65
32783  ri[-2,-1] ...,3 * c 0,255
32784
32785  # Render title.
32786  l[]
32787    0 t. "$1",0,0,$2,1,255 i.. 100%,100%,1,3 fc.. 255,200,120 a[-2,-1] c r2dx. {min(w,arg(1,$sizw)-8)}
32788    r. 100%,140%,1,100%,0,0
32789  onfail rm
32790  endl
32791
32792  # Render message.
32793  l[]
32794    0 t. "$3",0,0,$4,1,255 i.. 100%,100%,1,3,255 a c
32795    ('"$3"') is_err={"crop(0,4)=='*** '"} rm.
32796
32797    if $is_err
32798      x={"ref(crop(0,0,0,3,32,h,1,1),T);
32799          for (c = 32, c<w, ++c, crop(c,0,0,3,32,h,1,1)==T?break());
32800          c<w?for (0, c<w, ++c, max(crop(#-1,c-2,0,0,3,5,h,1,1))<=0?break());
32801          c<w?c:0"}
32802      if $x +z. {$x+1},100% z.. 0,$x sh.. 0,2 fc. 0,255,0 rm. i.. 1,10 fi
32803    fi
32804
32805    repeat $! l[$<]
32806      for w>$sizw
32807        x={"const c0 = "$sizw"-1;
32808            const c02 = c0/2;
32809            for (c = c0, c>=c02, --c, max(crop(#-1,c-2,0,0,3,5,h,1,1))<=0?break());
32810            c<c02?for (c = c0, c>=c02, --c, max(crop(#-1,c,0,0,3,1,h,1,1))<=0?break());
32811            c<c02?c0:c"}
32812       +z. {$x+1},100% z.. 0,$x
32813      done
32814    endl done
32815    a y,0
32816  onfail rm
32817  endl
32818
32819  a[^0] y,0.5
32820  r. [0],[0],1,100%,0,0,0.5,0.5
32821  blend alpha
32822
32823gui_no_preview : skip "$*"
32824  gui_print_preview "",0,"No preview available",32
32825
32826gui_timeout_preview :
32827  gui_print_preview "",0,"Preview timeout",32
32828
32829gui_warning_preview :
32830  gui_print_preview "Preview warning:",32,"$*",22
32831
32832gui_error_preview :
32833  gui_print_preview "Preview error:",32,"$*",20
32834
32835gui_check_version :
32836  if $_version<$1
32837    gui_error_preview "This filter requires at least version *"${"strver $1"}"* of the G'MIC framework.\n\n"\
32838                      "https://gmic.eu/download.html"
32839    u 0
32840  else u 1
32841  fi
32842
32843# Function to auto-crop layers and set their positions.
32844gui_autocrop_layers :
32845  repeat $! l[$>]
32846    nm=${-gui_layer_name}
32847    mode=${-gui_layer_mode}
32848    opacity=${-gui_layer_opacity}
32849    coords=${autocrop_coords\ auto}
32850    z $coords
32851    nm mode($mode),opacity($opacity),pos({arg(1,$coords)},{arg(2,$coords)}),name($nm)
32852  endl done
32853
32854
32855#@gui ____<i>About</i>
32856#---------------------
32857
32858#@gui ♥ Support Us ! ♥ : _none_,fx_support_us
32859#@gui : note = note{"<center><a href="https://gmic.eu"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAA\
32860# BXCAMAAACJMYjxAAAAXVBMVEX+/v4cO0s7OU4wPnEiJSju6+qiqrCMlZu3u8BufoHd3+FDY2JRLzljS2bLz9Lp9vrteR34lyZqHE3puI3S7/hbX6GUW4z\
32861# wza97QiOaYUa6mqTdfz25i22+VxzqoGYqrht5AAAWRklEQVR42uyabZfiKBCFFYUCDDg7TqIh0f//M7duBSEx2p09Z18+7NzTPUYaE/J4qyjI7H7rt/5m\
32862# nXe/9VtfS+vdb21U7Me/hNbblPzu/6czEs/Y39LmT/jU36C0+7/p/OvXeaf7S3+7bkQ19uN4uVzGG3/i31X0/3GyEFjXnu+991sCMN36dIGu479tLR9Cl\
32863# +J/ll3PLMBKbJXbipbW+nq9Lgan2U/cl8V/4Tz3r8r+hEKMu/9CDCrDStdLz7RKXDGly+MxTBqZjM4hyJ3GR4Y1/suwOoHVuf8EFmgxK6YFWMyBI0yaGd\
32864# Two+qPH8PwuDAvLX36fiyw/oFKRcdEHYuS9UtXB4GVSP/9HLw15JwjMtGfzx/jELwyLCjp3fUx/AEBUzlgYI8rh6oItBCGl+/TjH975Rj9p5QI+4gaVug\
32865# YWPnQxOrvN5an0yFrv9+rE9m3oz7zD5xlxyviEBpHRiV+KqQgHAzoIeLAxAe+nw2tifq88pKn5P3bUTccZqzC69g0nc0f7f4ZY3l3OLQioQUdTsb/eu0W\
32866# /RSKu8iwsrXuHHWvKsju2Vlgde3774cd2dxRe0s2xjI4psEhtl5ZpvAzdIVVNVgQXDo8jaWJQ9RGCdLtgeZjtAi26BftBqgyrCrg0suqxdvcoEdm1fdgx\
32867# Uiqo1YOuxdjbasc6DmO1j5haeqgkLxe5e9uxQpid/EdpoYPA5FGxwYi+55WtCzDIlaMku6EgFL7gzJ2Hvbu0BKrsKq07KKfib/K150uOQoZzicVWr0E4a\
32868# 3XG2AZMhgGw/LzcollzeLzuiu2Wotj0YcmG8sz6aYBMPM+fSl1UECzVyxjcEnFDS2i7HCYw9KnfQukS1Lod2ILzg1oTfTPW/DjBOtebPVKaUZrFFa3DYl\
32869# WOzLmgKGbikaH0DIr8i+Z/QXVglqiCD6pMxwFAqthWDTjXRWVaqkFLdx1hnUgk3OStTNWh/YV1WmC5cwsN6K+MtZH87QW0wKrAmjOavq3RGJ/vT5uN7sp\
32870# dz7tbWNtVbCWNFSljmml9POtGjaUt10TkN2TIBVrBeN3azlmxbBYFRY72+QmYxcxiAEKo6JWPjY3FiZBS9YXJ/N67zGxenwOQ2hAJG5k1eLKIuNrs7E2d\
32871# fB0VQwpMau3MZgzlY+J/bVLPRJeppXsGzerYLquPYSDaILVSjZol7DoUPP66XQCMMkYMBbRnBVEZA35PLVzgr8Lq8vlBRfauLHQYlQbqgYGUFmpEP0MVp\
32872# QEUBU7NhZ+wwoU+CWHzjryffq+76q1yOxWsgod2swqCCxdqVRYtrDKtFrhJZnNOL+Edf4VnTPGABZ07X9kLqBVA5KboFrV3/uL3jBZW6SJIoql3NJ4lbm\
32873# wZrEEUBlV6JKoe2b3TpjggxptcBYnLT56F4cyfTzDsM2wGN0rLH+qrNDTiMidOK85fAsra52MLcGphwGskL3FRiVjXbIeBdYGVlKmE3+1eTjB+o/rHNsh\
32874# BGEWqEsO8wIlFrCwscqH/E9+3zMxwFrFYc2IXeCJMKgShqDxhGLnQVjKKoOLEpEAY2OtYJ396eScezrrDmMB1eUKWktjQUOGdU+bHmboGC2PrQWq0JqPh\
32875# HUnsEJmRS7q536ssYnZ2YoCjPpeWCEO3eqsRmAFwOLL5jD0cqzmzvJBWO0zK0dRT9eMfFGy690ZnPp0Utlyo0ShFPLjDNYPbuJlRz+D9dFaZ1zK4BuyXg\
32876# LOU2gxYMvKCN4ZC4KHEHNUawrhZRPpJSx0E3EcpvgahSqwlKgmeD5+gUXzIKQCPQPT7zaUz+HIbtWzlDU2Y2qa8TJz1tiH4/EYZrDub/O7NxzvEM/IJlq\
32877# pryPKQH4X/eddziCsWAp371531crIK6wGKvNhsfKEBcZ6AwuqsHQ7M5Yj0lv2SO2xCYrw9oLCgac6e22OxwbOyrQefeIW1fRDhTW+OR0dGBOEuGcjTTmK\
32878# z26snnWrL1m2EVaN8MqFc+2Onh9gsRWbzujF+QhRWKz1ARab3s6NhRT1LSzQcrBWRBRKlXUftQ4Mq6+whhEtoPWjwOr96mwtk5LrE5lqBm0dWHnnQDDGC\
32879# IMtAThJMpAKKDu/2vjygMRgWRlbiuhUunHiE1TfwNJUWSEOdltgcZzg4o4v2Aus4bo7O0ZzGzIrNHmGtVfuNoO1SvEGrKRWIDsnqWUNaLFIZFkogViRlr\
32880# oo3yAS1lznNSyo0mpSwr3nE8rdFFisAmuvFrCe6X2/0ViSs1gEa1meC5+wrFNHNYel6XRUh1PqMyvWsIrDYixeKmj9mnK8td5r7JIYsqGbP3mwKvwEK9F\
32881# 6t+r8AuvYzMVLnuRn/nPcxD6tsMISViuw5KLFWOC5VVodQwhaYLEuu2idc/0cliEub91lmMHq9RtYeTxEbrXXiCmZnCDpQkewUt7Vo9Bx5DQKCmS/WUFV\
32882# TjXFV6Y6iLPU2lnsCGzRnDIsUihSJ4DOxbwP+oWtOGGJDJ8oULrdp9WOzNZpnIfh1aAeyLDu0GO13rHTOgHI2N5tG5zxHknKskzX5jsIiiFxW2JcTUD0c\
32883# NleYJHzfxEWth7qTRrZilZvYYFVmQ15NO0hwzKoeeVHz3iti9KcFNlaamRYU6Glr8yKYZUEz0V9onTlakJYyXpnlbR0ttbkbqQDHLYQv1VZoTOWlZg+Xm\
32884# yEGQIytmr4l0hvhFUVbAW8NlYNQ2EFeja/z3WDMjQRgfjlLatKKx4bsleeDbGl/ED9joUgU2IBliyBuHHIrADrNr6eMeLiUFs8VqWODArFFyhFryGplQF\
32885# LyQ0GFk9Lm2Dd5rSsnf9VWK1z1mQslWHF4/4QcnpHg6ACj/fOmmDlN2TxPLUfhMZDuNwHNhQzY1x3pgUN8NRkrsdlvafsY766WGlBCqBkp9eua9MISp3t\
32886# Al7IboA1Lo01W/KkEF5ZlZ1SNa/gLb/l99NwKWZWrC+sVdo1i601INCYDfYXJlbyoJXh8KF4amKFh/1xt6ZFe/7GXtUSMwIopK8YnERNlT2q4iyi+D2s/\
32887# trfFrCaso8YXmDBS7MELw0Cy4BjzllTnnzCOn/I7/gLDsCq0GIhhXPsZY0XpgUNhdXt8X4DXsdIbYXUGgk6cPI+GpkKwyotGYEl6Yxh+e9h3fopDsfQZC\
32888# Uz+SEWVqbQqrBYBRbhgOGV/H6usM7vH0aDE5gJLAnEsgU/cAAKKn6Qwz4rAkjeg+8/bJT6aCFjJ0X8IjWRUxDGR+YTLAG5ARaDAi2imuJ1jsJMy5njN7A\
32889# wOeYwRH6vzvpsrHOOxgLrxt7KsC5PVgKrosLj6C+egmmfgcXImKKhVsbUYnABG7avOZxU0SZYRc7WFB/zTk92lnErWDUuAcsdi7P2Aqsm+PPnyVAOsrPw\
32890# KJBpAcqAnA5cMjMWWBOqy4b/+BaJKK8mltPinsdaVBYQrLCCpckVAQfkb002FvupxCFZuWZZNNl3sMISFqZGHt9+goUYg/jwa1jnJ6x0Y2sJLZ4KJ00lA\
32891# 5rQeAeqccsGvJ0/tMRx0SusKWKCc2tY6lj0/IuvK8OQahwaL1GYa1tHQX3tLJKL7iGEIaQ/FfGCaOI00cxR2PR4ajGwYCxgkRfOX9CIRu6QNmwqWzCCch\
32892# lRZa3Wi55SW/OQD29gcdZvBJYrsIpCSuUYAHRN71tgIWWJDGUoAuxLWKwCK/AUAx4PcdTY31h9P/KxmExQjVHvNsiAi7B6kVnDkoG70wqW54bUHdewssi\
32893# EfCSzhi2wyLyDFWqCFztzBIqmi+pC6nMYZmkA6dnfNyZ1haRBlN9PpvK7bTLqhdXzKBirX4pSZLa9fOmO/CwWvMIzH0ahlmFYCRFeGixtTdy55mksZ9ew\
32894# JDPu1T4Xpcc/STe7JSdCIArLCDQ97OCFVZaxtnz/x7RPA2kGknEsj7pGFoT56NPLTyKdbgZLDXYLFhTEakiaHw/kJQix1d71h5ACKQTVTZFHMhhZ4Tdep\
32895# Cln7QJJB67r/NLt8E1h0Q/W5x5hCRMABKyi3/NAkvYeO0ehfZth1fDdXIO1g1I7AscMic0uYInqOsti67tsI/LHQ7K46PMTyATWdymvhvz8l0+osHdu9S\
32896# BglTQ1wATXBLJlzf5BWSkskeSsGRbiSURpP8DKq/MSFmp4ySUBlptguZbQNWcFvK6spKDU1eaF1rgLvz4O2tVvKknsYkqQgnCSfF8H4hyimdZkQ3BVXJv\
32897# IUQxQxbVL+DQABsvj9A9FgBWifAtcDk4kVTU+iUqosNwJVjt1UFjfssFyKDAat5GVoid0WMdUXHCfkoIeTLHs98JLYBmfZ4Q5PXoOq2Mx45DBUht6ZBqV\
32898# RZZHDuuVi7oQKnwIANCKHELF5s42bCldo5eciaTGLVZjkk+Uh5XNQ9VYiR/l3gfF2Evsd05fDZYh02hLU/OCvFu1HbFYaFnmGWBhCB0Wznd0TKKUcw2nL\
32899# M/eYTlL8K6haqvi5A3WelF7eQIIUoy+vOkDYSVSVhBgVW3HJa8e9i/k5HcqpyEALLrGL03UQVUja8ubW2E1gQrJv7UG91Umc6mw6hT0yAIm9A4bosANSu\
32900# WfYEVMS84SWabP7w0X1qi/DVaLr8sOkt9ew+ozawo7t8gCF05liCy4cIK1mWCfIiOWlmNC59BhbQYLL5VV6z+wM3EKtz5cUWkxYjgzyVeLLGwOdSPd3kE\
32901# zwNqgdLlyyPkNrK+Uzldde3FQfd4eWl/GyHIjLH+CFTKG02BDWUs396RlsKAO61u8H1q2iAeruLHg4lTGpHUAkpyatn2jhNaHsVJdTEc+udCNtPJ5HkMI\
32902# AsC5nmMyQgsVDJYbYdXeUZaVOnzYLdddqClT9Tz8m374KT5TnvNo2FcDthPAmGMUDxIx3OiruO5xoF1pPc6s/ArL0pDHuF7JidrIbEeW9HFt4KEvSl2H5\
32903# aacpZ5VWMVXyL15REXg7wUtZ3VWHVZgb7A8lzAMKWFC12zVLjSqDTNYVeHdDsdD94Vd338aKQ/F91GbBlYrrjgHPZ7NlKPSUlgZjzzDciOskFuyao0p2n\
32904# 84wvLKqsGCihvk8/OisxCcTeXFogGs4GC1/ihXE9cvoVRhsT/puLI5e4WVF1D6hdIC17tRWI+LSAKrP4zB6qGFWu08rBuzubDD+noJCyk+D7g2pogT3ea\
32905# eF2nsuT8MkbPwaTnL9MGfDwGW6BhLN06X+RDDtgvqSS4ve8ydR1q+yWV7mBFWNx3FZ4ScXGg5C3QGWF8NFhpml+deTWbEFRYiTXhx9quwvxhB0d821LEm\
32906# 3RlUv632UzpF1nInSXNBknONDQNjNrSyb1nBmAtf2vDJzvkOK0Rnsn67Ynp7hQ9YUGEkrEWW2incOXLoUQ9ABsrpnaL3a4jv5CcnGirIs8GC3UZYZC3hw\
32907# hVWAKxngVHYyUpN/pnFymqZad21H6qtHxqdJN+If2cVEtbvdSJNHVx2jAx/acRVRH1vZFQpPvlV6bHXfVihcG26yox4+VnNwIdpm1jdg6WbELcIsNQoGL\
32908# 7pYuAmP8Lq9Sh9OfvQbxR12idYAbC8wbrq1GDd2AX9NyyoxNwAQQZL1wX1jGalRRe0PNsKXoXHabBCREGtxQWwQoWlf3yNLG+KaezU+TewMnG6AevHD4N\
32909# 1y4arSoq8GaSmzDFiO/PGvDE3XNgVTWmXDFZXjpbHqhxqIbJGpP2noTVLp07f+v7ChpY88NnJ/4SFOcNCibNrws4zTqjWJrSt0ZWdd0wU57tEijFNZZGG\
32910# EmplKZ6bUUxTp1NwecwqMZalN2CB1ltY9OWmQr/KF9W/UtnDdd/gi6A4jZuJmPqZUIhkKj08qMpqpakWCqzS0mluy6xOCqjCHQMd17D4Hiq7yy9JoI1vg\
32911# 7/GFYUGtvW5hgEpqWHkwbQWDj2b1mZnhd4pVDu8iP/l0xzM/BoWDpj+tHN2u23DMBSObfBXG32Tvf+r7hxaabu18RJsHXYxAi4tRTSlLyRttU3kc79oS4\
32912# pTf5GTcPxDc9in0xePTzi09V51n/WxLp8tu2Rm4WAU/DWBM7p8yuTrAevuY/xqv/N2quf5AHakvG2p5W3gp0vKU058nbDu7njiBH5FWIqbOrZRUGrlxJO\
32913# aqRF+jeEvEFw+xherypvWMuwnj6n56Jry0VLdUks8lTcBWBEBWPfkq5/Uu3WM8LFCe8YCHYal77HaWMYY9gorl6FCtf38mQoQz9cWjOrdmiwfXP2g6CO0\
32914# XBNohz/BqsBqwno+tBJs1LWwPgWVAClFKyq3wQBxVw1Xc2lYvWRtWKWK3KOqynSX7iEr3wZa2a/vVX1f9CUyq0yvr3ZXKlP3lN21ZuQFSLk7wri7Kt386\
32915# kajKp6zD5eH08B0kuaean5clM2L/Dqw7sP6cje0fA2HEswwdbFgTInY6rZg1kQj6YwwK8ICBgTOtkXagJQ4foKpDhWGRDhmGhimxYgtgaKHhhVbS/+Vyb\
32916# axvZpnQIM0JDuq9sseS3fFyhHrgBEuDG2JyHVkhGr/cWBE1cLLaBbVaVr6OmGhvj8fWgbnByzfcR6LeSXiDSx0GX4Dam7eaXhAWSwdhKCip3s7VXV+FnY\
32917# bhrw+1rGiN2+wQgHJdH1vDlOGYw+sozwMM3jjVYPnMIqFQ5U8d5gU2swJL0DTMcpWqsizW2GzOof1ZdV7YWkyYXF6sSwMBOFvh3va8832LCGsGIypI/yz\
32918# 5zv8ipcdPWAr0hfjV40wFXXV2qImg6QvrhnwfzIPrHZgRA9stdNT4YihbXSg1UxYHbBMjPNoWFHUNqzH32UV6yOw8GCadyIL11b47z1zsWYxhoq5cYO1O\
32919# xYyYcXwMQwTTR3s9aExRgHW621p375Vt9i7de+MLKxxM3GsPZXJTHOD2/nsf8BqtTOGZRCnXgBBJqx+5wb0hCVywMrWDMaTeq9kdVqyzp+1dDUhMRBYzY\
32920# HektEhDJMxXjdyI6phKcaFYXIsUAByjU5N3ybbvWGNyg0Bou9gXRrWBnOk4zbNScEKIh3GhwEjBE4OWOsLLKnlPiwxFMqTu649BIvyMSxG+gFLbXh2gSc\
32921# e1isbrEBEJ9kReKRAVxss3glLtzDPC2F9iwKrCWv/9g3H9jGstc3txVyUYZTSxjGKHnVRHDOylglr9jWXhqU/Rhbv5XW2h57lnVl4JncqvOgIG13PK47i\
32922# EVYXWUcx4q3LRJjh9IBly1BlZEV0Gm6DFdyPkm/hcsAivgDxn9Nw1qxCBNzM6YGTCG/UhfMIB5F2SljxAot91CyurGMc9zYNIWfPc6LY7JwG1vlTaSo9K\
32923# JdZYWywfO4WIFaBRpRoKxbIqMRRQTRcoBpUELTJVedUD2NlpqVYZLuhSejOgfnOPOkpfOY8vRqctNPDSIU+Y/QwtGkWyQ638MT5ruHK7hNakDTgWk8fHF\
32924# ROzOfnmRJwvAWnO9vo4EvHECi0S3ik144+x/gRWjas5jBYQcrl1pI+B0BvswtN5Y35gPkaKdUeWrKUlrMrcToNBSDQJ+CJEe4ihYHlKY6ghA5OhHXlTMR\
32925# jPdnrnG8d9sc3vHsfrWbjGsPiw80Jx/F4Z/1Wprkxd0926Tcd7ej+Fa3vhiw455Ia64dPDNyTfp4cGWE5574/bz5T9yFLDz+HYEe9eECk+h9aXzn1feqT\
32926# halb8huwnzGXXwyVdH9qySJZkPz/XfH/5b/88/IdcfPxH+J0LVIAAAAASUVORK5CYII="/></a></center>"}
32927#@gui : note = note{"<center>is proposed to you by</center>"}
32928#@gui : note = note("<center><a href="https://tschumperle.users.greyc.fr/"><img src="data:image/png;base64,iVBORw0KGgo\
32929# AAAANSUhEUgAAAEAAAABBCAMAAABW61JJAAADAFBMVEX27vz06viVWEaZZlbz5/Tv3urwxMeFXE2icmuBUT7w4u7QzeKaaFqRVUPs5vWKVEKBRyvr4vKR\
32930# WEfSj4d/aGWuaVjkqqaca1+ATjnAfHCVWkh7UD/z6Pbv1d6DVUSGUT2TY1ONWUji1ubv0NiHeoaLd3zEgHW8d2qQYFCOX0+LUDri2uvcnpeQen+md3GKY\
32931# laYYlGXXk3p3u3Z0uTFyN7nr62Aa2uoaFehYE93Tz/tvb7su7yYf4XIhHqgb2WRZVieY1KKXk/u1+Lvys/uwcPPi4KkZ1eiZladXk2ZW0nn2unXxNHpwM\
32932# XqtrXntLPfr7DhpqKtk5yHdXuEcXSoc2uSbWeeZlaIWUjj3u/uzdPTvsrdu8LHrrmjipKQfYZ2ZWalbF9zYF+TaV6gaVurZ1aCYFakYlF9WEyJW0uDWUm\
32933# PUz5yTDyETDTd1efSyNrRt8HOtsG8pK7go52IgpTMlJCbhY+rbF2QXEt7U0RiSTxXQjJ1PiLv5/bx4/Hw2+Xfztvmxs6WpMLlvMHNsbvCp7CalqqkjpuR\
32934# h5iefX+iene3eW23c2VhXGWpbmKLZl2wa1unZFN0U0hrT0U/OzlRPC5aPCsrKyrKzOHCw9nnzdffx9K3u9KvudKnsszAtca+r76tqL2UnLfcqqp6iqiGj\
32935# KWxmKK5mZ7VnZmbipbZmpLUlo68jY1rdY3JioC7f3eveXGfdG6wdmxpY2yIbGqxc2ZRTVNkUk9LQTt6STGFSjBHOS8qJR4gHhkWFxLd2ey5wNjGvM2Jmr\
32936# mvobGCka+mnK6Pk6zZpKNpgaJ+g5mtjJB4eo6UgYu4hYHBh3+ZeHl2anBsV1RwSDM9MytmPig7IxPSz+ShsM3lubvetbl4k7TOq7LFoqnSpqd1hKLHnZ9\
32937# paXmVcm6FaWRMU2F7XVZDRUpeRTZTMBxgMReaqsezrcC8n6mklaKfkaC/l5pbcY+nhoqvhYNMY4Cnfnx3b3xbY3eOc3W1cGFcWGBiVVZbTEliS0O4qbjS\
32938# oKBQaYhBSFRDMiYCeYKqAAAKU0lEQVRYw42WBVhaaxjH4XDOoUREUhBEBQRFBLu7u2N2TqdzttO56czN3Kauu7u77rq7u7fb3X2/g9db3OeBv/Dg83D+P\
32939# /7v+d7vPR8OhmE8Jhx4GSLsSuytFYyHceD9l/+fiPFrwD9/0/FjiHEM3tiYMXaZ9vt/+rVULNxfoHE39oH5MTcex8DhYB03EDCmL+z6+NTPVStWLj+xPA\
32940# oGtH+GGxNAjPPB57/lOPNS/74D+18m7d7d359/3btTCAj/QowVB0LouLGvnbd+8/2Bw7cOHz584Kf9+3+4keSdMAXRvQxE0Amvzb/ss1f7h5tv3Rr5/ff\
32941# Dzc0/zct/kZ2QMAHWJWgpugu17Mt9Tc0HRppHm0dGR9++bX7blP8y4aMOoc5CG4M/XRkv+/LNyEhzU/lUUVrARKKDQ/GNn35smnVxymKK7sX/V8Hdz74f\
32942# fjdKLi1tJBBS85KTe32uSH840OSQKTTXTWv8P7yH3+w/cFBOAApzcxvY2ysLYSqz9428zqQY1qrOX/7wI9FI6w8NbRjYa2MWwuTEf/5jQSZiEIBx/rf9x\
32943# SQMkOof6ubG7sMANS5xl1OqFxkEeO/X/te+JMyv9ncLZbP7zMyYTE6MV/vG9Y8M2m4Pn+c7+HIJav9UcAfC3NhsGzOmkhODCk90P55hgP/4+eusAl9QQV\
32944# hqqFtDQ2gD20bG5Lh4oojd2exoAxIseOq6hwQAqeAWlLiFjiXgeAajCLKkPhM24BY83UMikQYVRAGZrvEPCy3SAoJRAKD2PKLoByzbmkIicec2jb7//m0\
32945# yFD6nKLShoXfbhlg0EGntemwA4IPnJBWpeG75zZu3D5LpUAm7kJDWVB6wY3MQJfJUpwGAmbt3qmbtKSb67fDzOGgLsWXbjSx5Nz3SemMojmc+MgBwMufa\
32946# i8+/HSwOMJo41YOn+S7u2UTBQQuaenvcDMrZ9QYA1vTv/Mo75fU8oHKBwK8vfhaoR6D29fklg7JqPWxACf2k2df2TJw7r7z8puBguE2NN2tu01uLbXVxb\
32947# cKVj/QCjFf2sJJJvgUOxLKpHqIyD3ez2E7XufNul+/d4rIoavlivN4usJ+VsgPsJEVpY5qInkYrkcU8uvhyIj2sZvXqzJ+r2/QCloEm2EEqJKghuoUF3R\
32948# SaI4uPjd+yk7tXpryfuXij/k78QOWbDABhBD5EmwRB4SV9yuDVzO1m25gcNIOS5WmtL8LSr/J2qUh5oe4REA2CNCVFQ/FI9BYwEWqCUSEyIUGmD7Bw1sC\
32949# u2aS8VPdwQACvkqJN94VbGmxC4tEg6xkZCdtgfXvRPg8DhLlH8EEJ797R2HmjVwfY3/nUoNbW1tHrz+lLcFySl0wiFWIl2NraVlTQbOp2MZPDi8yUwRnW\
32950# 1pkbMvQ++2eqVMmkwtQICAIEMrlizjaO57PC8O+YMWhG0ON4/Z1ot+KiimTkD4EloNF5PAtan9Kz/aI/2ycWRdEn7doK9N7HnVwN1gOgFeg0/lBI7P3Hu\
32951# 9x8OGhQUHQgzgDdrVUZhdPJPBML3qFDkyLYPkrlFzsIZvEAYA0bkoCxlsRVQ4BQMc3E5BDE3hx/pUAcZlaDZgSO+/VM99OkQjUEKjA1nUQDvczxav80ZQ\
32952# CUkAGPGVsWdnbM+BuhC5v5woig4YM+VJta0N3BAnrGbfFhuqBjBeDxH9SqZLGANX5i021newJB3Qjx1RpL8sEKdkgs+uDJBiUHHdsHeOcLKYShDfBfBz1\
32953# dQPoF8FhpbOTz+TSybbgN08WrPY7DcUG1sc0ZJ7+Va4qYQlhrNsebm//PTQAPRjkxjUa3oEUMhShdUM/ouDgvdLr2YHX3aQqB7w4AY348rAvArykAE4Uo\
32954# IpN5FlCJjdIlGkWjY70QGPPjl1wqEPPdNyEw5ofxxuYtuhuq1k8sJhInepDJZAtoKORce3Bb8APwk1rAUkmBBhriAABsDsPGeGNYdxXsxfIAAJgqwAjuv\
32955# XWrH7Q/CMJjUUHiBbVG/PC+GCwB3MLAw/8F4BlLan3VmlIsgQfIwKMXXa17sjpa+OdpFLa7QIhwt8EA5uCkyzBnMIBpvL9aIiuXbH3lQNBAjUTR1LkeHg\
32956# IyT6BW1f0SgyIUBA8DAmy3JiVijpknBYbh6S3TGY7gsA1WogWhWFm1RmV19fT8Np8M8fmNpSKBQHCTTBaQ1bvqXLxiAx0roygYIP00FyrZ5oVMh7VuR0d\
32957# HnFNr1ASgqhWf5OS4SoeP3qZBfFNLOkYANYgK6+I80csJFKvjkaAKSvoabvicTVgiBoNh55iezsBNAerI6ly3MTExKTf36LE7h2iQKd80TUTmlfMEctBI\
32958# KPrxDSF1uqMdSEC9IIaKNnkhiB0jMjLdzsrODldVVZXVvTGhW+LqmpSbf+TYHZMKGoSFEIl4ZNO9Sk8UDTrzZrWTFeVuZIvxaVeyRYQsVohgXiuryZOtc\
32959# Fkd1R9vrM++LGW5uubemH/s2DSTCjCQTC1NIRotXAYGOprRtefqs+qsrI4JCy8Npvn3bj7XBqxO4AWE6zy1rt47J8dbwmK5sjDA+yYmk0ACrcAo8vIK/v\
32960# QayTc5WUXaqbpy5eoGl/uZ0dZWVKoV1cmJSnXCJdRftgfyTsQA+fNvHQUAWzpkSoNABnaIC4cDxqQvF6iggEtKlm2KRgIXzYCXfeh4nIoJJ5FIpVJ7b3v\
32961# pdUDInX/k6DsT8FyEsIliCRUx489tAac2LtihRlyuEbeQa8MxpyAIbunZVvMxQE99vSRJai+1T8rPZwHAkTsmh2xtaYBgSYfmMDfXzeYSjLTy8xODUeHu\
32962# EwgjM6Y7nZxCXagFdHd3f5KTKJV6S3YXu+4ePnLrzjQTW1ADEI3G9pEpr2lP3sCuKA5QyBXQ3hgKskgYebJj5konZ2olrn7FihVnahMT7bt357KuDx89C\
32963# gAmFSCCRgNBYQ1mLi8xO8FPEUAEe0xhaem/yTqwdfHi89UPOxnOzlSc9/qoyhMf1kq9O7bmsr59M3/fkWKyCbYQ7AECBJWEfDWSJldYitJ4PB65jEgUiS\
32964# CzGCRwwpTn1acDcZOdnXE52YuWUyvPSj+PWvM1K2ff4a3De1hTsYluI/NtpLtvfk0QK0rp4LwzelsgArtcpOj1QiitWd+cWgkz0gFgbXbb2irqPUm29Um\
32965# Jq/T7fScubEzCCJPMNs8qtXD3zzMCBWAjoqwMBAgQc7c/80QolMpLH0Y5Uo2PO+POZEedX9e6tKfe+t7axKTrw9Vbq2pZL/wqbIdqvnCg204jgFvvJ5dj\
32966# Q6ZULh64uiGuLRBBKJEnVlktWMrAOePWfVL19LJwydps6/fWSpKS3kz49Yvll1gklT+bOTuAZzJNQxAPOsjFAQqxQk4gpPadC0LwAECx6oq8t9IRPxnn3\
32967# fPRWUnWkq51Ue+tkiQmvqr+LP/TrueslO2y7XLgt9wpHhx0cCBiUmBrwAxGhEIEyGrBw+WRduk4+5yEVZL1lavOLHZeJZEmvlp3vn/2qa9zWbOvXCsDgC\
32968# dfOwz6OWgJZZallnS+j+eitqBABHTjvTWVTlTGH/5VIUZ4CClbAAAAAElFTkSuQmCC" />   David Tschumperlé</a>     and    
32969#@gui : <a href="https://foureys.users.greyc.fr/"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAM\
32970# AAACdt4HsAAAC/VBMVEW3xMQAAADUqJoDAwEICALFmozFqIwGBQG1jYA7O0EfICUdHiMPDAoODASth3svLzYFBQQoKS8LCQM1NTwaGx0SDg0bHSEHBgYJ\
32971# DQTRpZfCmIpuVk4XFxh5XlU3N0C+lIemgnaif3OPcGZ2XFM/P0clJSoXHAoUFQfAloi4kINrVE0QFAgJCAece3BZRj9WQz03LCYODw8UFRjNo5WXdmuEa\
32972# F6AY1plTkYpKysuJSEYGBwlHhsYFRELEAWotbWxin2ffXEyMzlPPjhMOzVHODMkJi4yJyILCQi0wcGTnp6CZlxPVVVDQ1VxWE9oUUopKzQsLTJDNC8iIy\
32973# UrIh9nbm6LbWRZWGOGaWBJU15eS0IxMT0gJCkgGhYSEhUUEg8XFguyv7+Ejo7Ep4u7koWphHibeW6ZeG2VdGqJa2JzWlJERE00O0NSQjouMzkhGxkfJgo\
32974# cFgkQEARBQUk3P0diU0ZBREQxNj4uLzoqMDZAMyoyKSY1PyEQERMcIQ8LCwwiGwgYEgYSDwSturqfqqrKoJOvlX2UfmpXW1tJSVFHR1BIT087O0hhTEVZ\
32975# S0UiHxsoIA8nMA6wvLyMlpZ+iIh9YVhPT1hFT1h7aVc6QUlHR0c3OEVdRz86PDwxMzMpJyQnIR4qLxoeGBMcHAocIwjHnZDCpYpubXpvdnZvcnJkZG9XV\
32976# myTc2leXWhWVWBOTk5LS0srKzA/Ni47LyovNRofIBQkKxEQGwq8oIVcYmJcXl5RUVtWV1dMS1VHPzgrKRSXoqJhY3iljHVbWnGbhG5naWVQW2WIc2BNTW\
32977# BTUl0+P0xFSkk4OTZ6hIR4gYFze3ungHFQUGVjYmGDb1xoXlVdWVA+SFBAQFA9QEE4MjIuLy80Lyo5LRmFhJG6k4d1dIC0mX9vbm5UU2eNd2NJSVtGSi0\
32978# 4RBYyPRQ6LQ+lsbGapaWAf4tsb4Z5eIVYY299aWlDSlN0Y1JvX09RSEBMQz5CPz4/SxinrKyVmZmNjIufeWF2Z12ObFYuJgeXgXY+PimhEuIiAAAMWklE\
32979# QVRYw7yTX2hSURzHPZ57UeIqcvE2/IdOiZz5Z+OqUxRnc/iXDUrHGJhOlpuDMIN8mA+p0xJGU2a9jDlXIRgEjUYPG6PtZU+tFVHQ/hD0EKxeeqgoiIqu+\
32980# 1Mrrce+cO49f+75nO/5/X6X9h808O/lMcvfVjKumjIZT3HsX4C3RRrN2eDYR7enNrjE1tbHucTG4rXJ0F995KouZ7XOxcraxw/laDlh8psD0Y1o28yZ23\
32981# 8DHPGUxkJ/enT52ogNP4SEFygJwDFjDliyvMk0BAjX45bxT79fovgxYOZgsw6oNI4qIXaTg2EQlnIrDQEGD3fhczF0eOrN3GmHIOJ1MxX6qLHrqk5hw0x\
32982# +AGYsjQHjuaXqo98cWB4qFWQky6PzeJJmhUqalbrJl60AxC2NAa9y48Xf9zsUZ3t5Ffq+Xko7SOrZa1YWnZb6hHnGF0vVMdehMPowQUXWjUoPALzs3ksV\
32983# zQ08ri+J0NTdBd9YaPUnemWzhxSPnqWTJP0PWVcbVdTjtuq4J+OJHxTKpIGI8BQROr1Zsntuyy9Atmu1AeDFyczk5NuFjPMAIGwdtSp0tQjubrpDQX5aW\
32984# GtQCty7S5MZn8+zvnIAGNX0yw8Zp8K3F4MKKdM8qg/i2sy19Zm7S4uPB/YBs9PRTt5hAG8vGJUIaRVw6zP5HBoMU9y5tYMYWPJGwka2qLL0OvE7JVt/An\
32985# y055e4r0qbc679n9DlafLrjFKyIiLrAD32ygMK4Mw497+lkl+irT4vx59Oeaq1SeeAL795U0u6+3gkX7Vr//BltApFIrRwO3Qst1CdHHAVPZeowqCVVg2\
32986# LL3IzPgpgcVXj5bI3q4tIVa1aZm1PM/uwhYr6jA84hADi5pnNo7OngXDFQ1szTJWE3IcLb9/QckueYlnozarYIjMw6/v6OujkvgPenb5aZkYuxQN5IlbY\
32987# 3i4AvMAAwOygGcvxMnZm3VdcsVwz5F5wgDc7JOoSCLqvtzq8CrK3km05Zzun9tpPySlAk8GfD2zfYuDpGJ5kJHGHktYUJHAEaVMKuaeFTW2Y0pEYmieIU\
32988# 10Jqa5HOMJDeb3XE8b3l7/NG9UUgPPgCwZYOzvpFJLGWTuxJItmBgDHY0kGUH6/ceOGn0jkmzp7ulVqAnb15LF+Se+dL1cmavpgktBHsGf3TIBRSMfArW\
32989# QhCWMpQLOD4wAGWRAoX7++HyBknd3XO1qmNRrO1jNp74h/3mS6N/EuqmwavWgy2bTGAHSkYoVYGsdBOgVvAkCj2p5uvl7ehq3t04RdEtaEZecmnmkiNuZ\
32990# gpJ1te/dudjpiPyuax6IExmDFGAA5j4DkDmRRAFATQvXMy4UglMkEmLq7Y1DhZrL1IzKvGB02Dl2fmNA163SivmbbVZGdEWQdh6kLt1iprxCnABACKh0Q\
32991# AYXtWAz49e6AWn1V6pZq+eI+tUksVykEeiYTbRmShZvl7m6RLQU5QQAhA0my0pACpBCIBzngAtgV8eRJoF9uVQgEonBYpjGKmV2Doqtn+WK2VNRuZfcLh\
32992# qNgXwiCfK1dgWrgxv0TjAPAtEOAarUoKrcyVWa7uOWcTDrUYu206SUSvs6mkAEIaoLwAkgjNQdBanSLFYMIYw9wyjSI8vkoynSjEuMwija3C6xsvlgsR9\
32993# lhk1qur3llMBgsiOMQT1OAPLXt/vJyAUGOU732jlOz4kE+WhM/3MMcVqHtw2zm7jgik/Uz7YASC+5eASJpqpB+sFQ2IUqEYRyfl/eVmUCHYSt8UQcdrRl\
32994# nRx0NP1piUFxxwVAwMxEqCb8SpMGFDrUNdSk6SAjZIaIvpSA2otbVW12KDtWlCIqIiKg9dIqgQ3TpHet3GIYXnv888/z/zwyacWCzVjtrQ9BKzwVkVZ4X\
32995# MJl00O22MGrdMof1tDKCFdA2MAfSEk0UKKQhSYREdcFKhO2F4tFK/FrU7Y67k8XlYpzpX+P9Fj/pJzbstDZaVrKKCIDeAqDRuYWRDVCTrW+DgaTBmlSDg\
32996# KzYzrzP39m3L1zOlxL5kvVEqcwKbJ6xMMEU0+onreTRBJpkyNbrIShRmqSL+tYWtiFAqEn2ht/h57vHPdalnSuZdNpZDW2UU4V4pxLzt6os6YDkEAEagg\
32997# Uk2UCPAhCLhoIh+I+d9fvqFn89tJLrqDmejzqj+bjcXWowwXTscBb2SCmBXMjUEXFBGejKE2KANUCOFmjI5YjhQsoZVePxYW69HE9Z8jud6rrF4k62AxD\
32998# UaKDR/6YooXM0oNBYMaSRSHMcGQ+EHLgZDVa8+X5TCPed/VKp0FrMqnJIIInwdlEPmOGh//tAxggoHSARYGIE0nVNH+NZoOBsMJmkW24W98VzsWFiI1Rg\
32999# TIE6exhIpl+kXsEaNCXMV9BmmjLDioY1DWJzkmHWwWQc3mjQE1oOrKSvNRx1byxJglDeCfB870ww/ndHYaRhPJvNtKORCJ4ZSFRE35BJy6lKxuNgvd6kz\
33000# DgYdZElM8gAhMF/FwDqQWhDCFEAcBo5VeDJy69cylQUp8al4w5vnlUTxfbGkA8XCkIydpptNBJ28A+MTZ3BVP9FIkTdIkwVZQIjr465ADD1Nh+32KjcZs\
33001# LZ5cCyM7GeYCu8pameDq9IY4I+GG8NpoNfv6a6CEVqjfB1bbImGq4XR0j9Gdeex4+dAsmCp+KRfesCry42u4ybf37h/JufNzYREgfit2+SJo7GYm+MKQN\
33002# CaEynhkE+ChC+2//g84Mdp085vUmhWiqwQaERdft9xTr/5cfShQ+rn+6u7lrdZud0BU0MGkqiQhlmCxOMUGTTRf57By+/DB3OJoqynBTasQRbDfssjlCx\
33003# 8+VOeOnp+107rnzfdv/jvUNXIpGjoydPJkQAcByHJ2uTva+fXd2z48Dlt7//3M5mni+m2zmmwjc8cpPxpLtf7twJP6puX3149+HFQ9/v7vr+8eO967vRa\
33004# C7wt6ayCW0aDsN4RCzm/xfTP4kpaA0eEhDxq0Fwxh2MGi8GEouWmFjBXoTUeGhr20npraVd1Yu0q1URD461tm47FWuFtqNrK6hzuokgzrGjil/gB17MBH\
33005# /393ne530Or8uVvPHg0cQlsRi31HNXn/8Z3nS6PnN0+v7mzee3bVy36/zhx7MBR/3xi4M1b7nJ8SaLr0KW3+WzZ+3XduP9i0cTLx5xghGlw+jck9/7nWv\
33006# uphztO8Ondq3fuP3QvgPDtn878msikc28/fguDzPVqm+8AbkpUP2IY7ceTg6h1wzo1xi6q8UvPdty89iZg18DgZnIhU0njl05Ox2Zvee4F7n9EOpUtWaa\
33007# RSZcZHNsbnm5NRhkxrHjbpgh+zhrIXf8U7AIT04c2ToyMhZwBJaW0mPz6XTKYZN6+fMNIng2iVpdujColJTeoqCaZCOEsURTzTA0Ho2rYAHQweT1oZGRv\
33008# XvvOQLz7bELewKOf/PTvyY5hrGPqHygmVxFknOyXFSrPuYjNi4KUBV8FKHQyHDdjtOj1717926YrQcCS/PtmVWBQGp68pIR7FUk2I9+HsC5FkBRXQZ4iD\
33009# HLWI0RcC8xjnhe6vqHRqkSGL3+fWRDZEfd8Z/Uq8kbKEfKHbgTfYrmvKUWSBpSh1Y1/F0ZIxmzLzTIMmuAIE0kaDgHnpz7dvd0atV76XSkbuf/8cA1V4J\
33010# zDFqrFTslmJPXUD2dgTWg9msYp2bjkunJQpcnGPYwxJSuJ0afnrw2vbpBfWa2PT8TGeL1SrArkoSzEPsAPSvyWkCQeeQjCZLF8q6MXQNu22tEMpGFMQU0\
33011# p+DDB7vbY6tEvqYj1NTKSicXRAS91hYQhF6MJgS/iji7liZGSXiIoqNRCLUoT3hdJGMgnFtz8UL6y90NZ76mD1/DG6GerNe8Ccbr0oTlxVIlyQJfk8jbv\
33012# fqqmCto1TjNWIiDoEYRIuFnOUjGPUn/cGQs/eXOxZ1ixvtZIX0myWQF65PSEphlvMHhoawPiawdwSTDMLxgRAsgXOAJXZY4WpQVxIeAy3V5WJEInGM/A1\
33013# +ZdJMiwAuxFX+PMxEtIicNRESSGBJBtBi3V0gYQYHhdcMUqVJUkaBVXLCCMUlnGI4uNTiax1XCFlAkmcapAZ53rnGzXreFAcPSDAXGw0OEpWYggfwxmZc\
33014# lq4AoIRyWlQrQKyLbcIfjRYvktDBoDZrqYg6nPHzflzU5DAF/AqeUboxPJCQ9V4LOWMW/KFmwKcbU7KKkzHWE/DjhmSpoCcLZ1fQO7uMId1VI+rM4zXr/\
33015# AnoH9FcRhS6kAAAAAElFTkSuQmCC" />   Sébastien Fourey</a></center>")
33016#@gui : url = link{"( IMAGE Team / GREYC Laboratory - CNRS UMR 6072 )","https://www.greyc.fr/?page_id=443&lang=en"}
33017#@gui : note = note{"\nIf you appreciate what we do on <b>G'MIC</b> and want to help us maintaining and \
33018# developing this piece of software, please consider <a href="https://libreart.info/en/projects/gmic"> \
33019# making a donation</a>!\n\n"}
33020#@gui : note = note{<center><a href="https://libreart.info/en/projects/gmic"><img src="data:image/png;base64,iVBORw0K\
33021# GgoAAAANSUhEUgAAALAAAAAwCAMAAACG2QC0AAAC+lBMVEV7e3uQkJAAAAAMDAyIiIhdXl68vr0rKytmZmZsbGy7vb0mJiZxcXF+fn6NjY17e3u1tbV0d\
33022# HRzc3OEhISbm5u8vLy2t7Z4eHh6enp3d3eVlZWUlJSdnZ1vb2+4uLiamprOzs5XV1ezs7NpaWmHh4etra1eXl6Dg4NtbW2np6eDg4O9vb2YmJgrgnFwz7\
33023# xqzbn6/fxlZWVsbGwvNjRtzrtuzrt00b5iy7Z0y7pky7b3/Ptxz7z2/Pr5/PtbyLI9ppFnzLd2zLtyy7lozLhz0L3z+/ny+vhmy7dvb2++6eBszbrB6uH\
33024# a8+3W8ettzrr1+/nd8+/S8Orm9vN20b9SUlLo9/St49gqfGwuWVHj9fG5596G1sZcXFwyMjLw+fdopprk9fLP7+htxrRBkoLu+ffs+PbN7ufG7ORajIIs\
33025# g3Lf9PC76N9+1MNpp5teo5VboZPU8Ouv5Nmr4te12tJ40r9ku6pmsaNZp5hNmInq9/W25tyZ3c+B1cR+tak4jn0thXRdXV1QUFDR7+mz5dup4tam4dWj4\
33026# NSQ2suN2cp60sGZx79frJxlppljpZjY8ezK7OW649qb3dCU281808KFyrxqvq1NnY09k4Ivi3lOTk7E6+PI5+G43tag3tKk1cqK2MhPsp4+kIA4jHwzjH\
33027# vE5d2t2tGp186MxbpWq5pWoZJAmIdFlYYyk383inpWV1fQ6eOx5dqo3dKd3tGw1s+oz8eYy8F20b5pzblvyLaDwbWDv7N5vLCIurBwr6Nrr6FFrplEqJR\
33028# OopJSno81iXkrg3JaY2FDSEfV8eve7+ud2MyizcWSzcBtyLVfyrRww7NzvK50tahZtaJksaJol45EnItdj4VUioAziHdbdXArfW0qeGlTbmlkaGdiYmJg\
33029# YGAmYVU3W1RQVFMoXFJIU1E+Pj7T6+aY1MiOz8KG0MGOzMCNy75owrB7s6hknZFBo5BEoY9DoY5UnI41mYVjgnxfgns9cWcnZ1o+X1k1TUg5QD4sPjsfP\
33030# zgzMzMkKilq0aUXAAAALXRSTlMdvgAO0AmQAv33kAjy38w1MBPu1rJ4bibm5MS8uKqJdV9ZEv20nJORfGVUPh6rD+f7AAAHWUlEQVRo3mJgggJuhsEMBJ\
33031# lgAOpgAQ1ZacZBDFTUtJSQHCwoKsyjbDiYAQenpIg2O9TB7PwinIZHvzg6EQcsqAYciQcXjhpyCIlxgx3MLi7C0XBbj6bA2gUFmJmZuegBSbwgGAJg3Lc\
33032# 37vBocoMczCzHGXP7DB7baOR8M+JAMMzhwZfvCPODHMzP23BjqqkevYE1DBDrcrNtRznVuZkYuEU5Pk81oaODMZ2OHyC8FW/Ix8/EwCxz77KeqaneIAfW\
33033# ZmbWO2IkFIEO5mu4agIFpkAwONxuHVxSYoae7s+E8IixMzCzwB2McPWAu7yjwC00OwdFyMVlR6MHFyvIwRleXiYowAsIQE4fOLcXuhkYuEVtQxVMavRgA\
33034# zkYQE0Z7KYNBAH03o+BOdSsdu1dIRnbMiYlsQQSdoUaiYQq+BAQl9jcIAQujYBTK0X8QpRD8wk99dqf6E901osDjhWlx+QdGHZmdubZB3/+Wi7XFZkwJg\
33035# 788+6fUrQir/V8zLKvs+wzAH4UHqTywkUy4SLF1v/sqReyL+7YHgPSudlnngm/NZIzJXzwxG9aeHLK0Jd/C9+J8PLWAMSK6+9D+FPQBIT7ia7rWlEYP2v\
33036# VqimT1Wo1Depkqqj+qFDOyvtkvlmNUKFQxWC+NO7w1qoLEt5ygyC4Xy0xbSrh79kbptSRUIyV1TQYDIJ2UteuXW+kOc545LrXzs+Z67qe58YP2LYceh4e\
33037# B9OEIk71po134vlGjlh7niwG92vN0YZ4R4JhrebX5LSy4yzbrnuHO6P5dOC68Wyx2++YSYeDMmaSq7iSpvPCKYSgiteyGGeG7df0E4AjndLJEcA5jU7Uj\
33038# OZZQGl4rA6WP6LIrGMbDCunCxwxVcsMqzMv6x14YkhTxjjtpJyOYD8oXZz2mgynds/VfhrFHQaHcH9NkbwwQeQFonsWR12DcauSEyZSOC1Ad05wm+zinF\
33039# +tCJ315B2GP40wFeZpH7+olC4M7Ep7jTYuQJQwUcIkvDTSIuPHqkxWNoc89pwgO+EPqfAfkrG8Qsmksr32LvWaFC4Rshc+N0OvB6wvhQ3vcdG3oRmQ7Rf\
33040# O/XiR9A0s1QgKt8JS8gs3jQkyaYE1JIpM+JEQJXzXBeZtxzftfp8o7gx4Ru9hJ6zthUVGpYdai6gkkLoULguxwRW3YiyFhYgawG7FBIVdIUIfjIEY2WCP\
33041# sP93A8DfiHsUjsjGBTjTBRKhcFs8oUthInAEqooHG1gcbh739dVzYd6YCDndNM2/O+F/1NNLaxNRFMDxT3TgrBQu9zKruSJNmRicagpOIpO0JDKpbakmC\
33042# xOIrQXTxKBpUrTGJrHvqpRasK19F+wDrJZCv4BvxBe4905m2kqTxm39LwYuzJzzW8x9+lva720QkUYcZ+/eG5AG4gI8IEm9VxEfSB1xwV48/VBFmpZeO8\
33043# Rz8UyXD+UhaZCyYK8k6mbIe6VWRF/rrSY/kssDkuiFC+W8dLjhqgAvSuYIkpYyfmSyq761+5RklzkKJlfEGNHS0tKPd9Y/fP4i7Bf9oKGIcef0qm6CdYC\
33044# EWPEYrl9GdD9vdlHkW3BNbFsfuqMSps7Ac8Ke9YFohCINBRrQiqbm9kDU4UX5NhwUNsFPoDyiH5bTShnlq38DVjcqwI3fwO7CpQrwWm7YwZlJ9mcqwFbK\
33045# lXFzmxUfzJXBE5Vg7+wq/BMcCw+6y2SW2jkGTIcmjgeL9vTsZDwiJtzXBdFpglOHYCK7G3Rzm3UIPowCbFKmzgFArI2h1mKC1fl+RD45VhUs7oJDgBecJ\
33046# higqCcaUxoybesYsH94rQY4HJzRC6XSggvxTqGOYWQ0Gr0hDuky+AHYCTBtB7thH1OmCsXouIrMuWKCXS3gYOifj1UBj99nqIaj0VkVySC8PNdTiJaK0x\
33047# ry6WPAkVmoBVZk59nuu00c6UZfRkZ0tHU6CSozNcAjzwjyeNdNNyLP52ywuE0YTFQBlz6KqfVtDUGC2hy8pNqr1ke3UgrzblcHM/d2TbCMVkrTd8jWKfu\
33048# HQA0wzDuINZv3Z8EGP9mUkTT/qgTDaLNNUupAgNGKT05UB9P0bi1wrsvpkxnhwXwiBn3Xp7wyIdzbsAO1wLDQrHJCNedQFvbBsHKOoPypEiymtrvMqe7O\
33049# BMBIo1ujjPocw8tQHcw/Qy1wbKwl2WN4ki25NRDpoR6Ppyc0FgMohgxDOKwKIcPzEw4KZMV74iMdQEgNI1kEWB31GMaoOOeShicMfzduTw0I/W4o6TH39\
33050# YHdV/nIneutCj45LXfGIwwPe1864WCIxSY2Hq9zhRJCqBzMwEkHl8t+yU+1i/Idgf8D/GdIYIg7mGvUwbQE4X/ADuZb90B3aICbT4SAw63cqve+39IdEu\
33051# BbCC9wQJtdgfPYfd0hAf5ySIsDJ2XEpRoeDIkgfvSQR54d6GBuBYl1P27qDnrw6BiHjA54JlRQjifm8W/dwQ3u/3p4V1gAOtcsKMrL0fD4p/EgBsfW3eW\
33052# UEmCHz+YLyPIKcRoNYsAjyScPmc4HAHFPJOZJDzOgAAAAAElFTkSuQmCC"/></a>}
33053#@gui : url = link("Go to the donation page","https://libreart.info/en/projects/gmic")
33054#@gui : sep = separator()
33055#@gui : note = note{"\n<b>G'MIC</b> officially collaborates with <b>LILA</b> (<i>"Libre comme l'Art"</i>),
33056#@gui : a French non-profit organization, which promotes Arts and Artists as well as access to technics and
33057#@gui : knowledge for everyone.\n
33058#@gui : <span color="#EE5500"><b>LILA</b> collects donations to help developing <b>G'MIC</b>."}
33059#@gui : note = note{"<a href="https://libreart.info/en/projects/gmic"><img src="data:image/png;base64,iVBORw0KGgoAAAA\
33060# NSUhEUgAAAJYAAAAyCAMAAACJUtIoAAABd1BMVEX///8iHCSKb5H+/f7KyMrz8vTw7fGfiaWag6DNwdA5NDu8rMDm4OiOdJWgiqb8/P2hi6fv7+93c3i7\
33061# ubx/fIAwKzL49/j18/U4Mzr7+/v39ffJvc36+fq0oriUfJvs6O0oIyrb2tzVy9hBPEPy7/Po4+mCf4Pp5evZ0NvHusrDtcbBs8WvnLPu6u/c1d7Ft8iRe\
33062# JiPdpbj4uOwnrWrmLCmkauempuCfX1hW1s6MzPOw9G5qb23pru1pLpybW1DPT3l3+fi2+Tf2OHe1uDAsMO2pbuyoLaXlJTt7O3TydbQxdO3tbeppqichq\
33063# JVUFZKREQsJi7v7PDa0t3DwcKjjqmIhIc0LzYkHiby8fLT0dO/vb6olK6WfZxaVVhMR0vYz9vGxca7q7+yr7KtmrI+OD8nISnr5uywnbSsqqyal5uYlZe\
33064# McpONiYx0b3LY19iqlq+koqWhnqF1cXZpZGhjX2JdWF5RTE4yKysuJycmHh7Pzc6QjZHw7PF1dNCuAAAF40lEQVRYw+2Z+VfaQBDHh26JWKupWpPmABRR\
33065# LgFRaAuoBUSqKNb7vm21rdr7bv/4zmysUgM+fMY+fuj3h52Z3XnJJ7uzZKPwX1fUvuMq8sE/UsOtq+guGJL9jFspFYyQVYrBbZEcfSI6xx0QI8FGFa06y\
33066# kM2OktGbkS5yckBSS1GcyJ3IgzUXBlW870atXGOFXCEuW3SMvyuWj4TEQkvrmWH8oww8ums34uOe9C4f8sYdUtzocIcdfc4ee96KnuoAaX1e8DtLMNqYV\
33067# CbGs+w9HVPiDvOoshtKGrYOby3UqJJsjco2J5jFVMJmTv+IDcGVjFEwCqlxQ9918XyR/WESo7gXM+RldOJbUYpAQAWpwrMHLJyLGVfTmlmrNAENgsCTwv\
33068# v370eFus73C35DddX0rkVWqh2ttM4Wy00LbMLYjnWXceuc0ExYTVGceoxn9KYp3ARS1d5IEmMKZIq0RVFSVUVHGK6hFLKsYR1rzdSYDQngrtBorsKwkKY\
33069# LpDY9gacvLb2Na9PPsPKe7ze/bsmLGFhVGjKMyNN7LuI9X6TB8cjTF2IHbnWMwATrqPmUkCEWYcLFSjH6sEyVxp1wKSxoaxRU2NzjK9mdCylGI8YcDYJ1\
33070# DNBkCmEz/L19hk70+vhRgiNTYhwmvbLcwHr6TMePHrMZJfW40+47KA5RiOh5iALxzS/339whvVvZMaK4BM1Z0HDrcR2HWI41kND9YCVa/YRFpK46gbLGU\
33071# 270iJhiXLiLS7icCgU1MuxJnt7e3+qYNJAL+rNabCH/jFcqpeY8p7ViHUvnojFZdBi6wnXhg/CsUQ83ieVY9232WwPWsGk2zZU+2nQhn7HpVQrM5jSOVD\
33072# 7ItodedBcwaZmP4BpES3DemUjLbKascAZp0Vk6ZJ0c1hqN8daWqkZS25I85L3bgTYjWFN2gy9uwTr68jISNs4xxoMph0uYycyrV8Nx+LDw8Mh0Wos5dMp\
33073# 1hepKlb7CMd6+Zrpob63g4FZgAiRSGNZ+5ATlbcc602nzbZMV7LdroZVi6zG6sXxzfHn2I6w+sEav4Pje2yRsgbqB+slDt+xQ1cn2lfVsfQeqjzxQKZIF\
33074# UUVDTY89Lp1q7HEaeMnS/mMdkuqhiVGM14AVswFZIyiuVyAnz2VuEJhRrUaa5Vmqet01pZ3qmEpGgVMY9kM2pSmFdG4A9GgzI+2ktVYdIlpuvFKB3on1b\
33075# CYFhGw9cxpKmH1+FJofFkP/4wKjAoWY9mXcHSK3/iE8sar1ZYi6JTklSjAhqyigCKiVb1W19YeDs4YLGs2IqyLncio0D8avn6Hr2c9YA0s4+DkabBIRd9\
33076# VD1ivyy+w+gKjY1YJa36RB0UPFdYj/hyewtu+qADuYa4gsxBL38KxD38ikaJuvRLWwyc8GNzFZuf7FuX0lXadGxvug0Jh4ahQyFuJtUp5a2Vzh9q5BGss\
33077# BMBGPn3/QVhDAF5HEw56+q1dREZv6ecrZ3HXDE0eq44VbARwJ29vPjrFgkL8BrDsdCxtO4/FTYyXWqtjuQWAqSfiVHLFwNLvpW8Aa95GGN3n6qCOvepYh\
33078# P54EQ6+tSNWIhVI9PvAeqwPtkr6JF6GNfC1vbV184RBX3/DRiwD1mONP6iI1TFwGdbU92Qy+S3ZSosYdmnWYxmfFp1/6QX/1LgM6/HH+fn5vW9riIUg/Y\
33079# L1WB+psla7yrVDXNOiGSs5OTnZLgP0fKXfE/blxCj5ButL3kcF3sagXArtxZkuE9ZUEvUEV7d9i7/XH36RhvJotx2I6i9ZiTVFSfMXOh9S52sTltRKEtF\
33080# ZMY7QraKk82Mz9imqhVj88LCkV3p3b4nXf1Uvrd3+SxWxZv7OWRURoJPWsOLR/sWb62KZNFMBy6TndoD3aJfXKv+Z5Gc5VmlWqE1RC7D4tDxQzScw2ovd\
33081# rAzrVnONOrIAixdRG5jECNe2eq3/+RzfMamb+n+QNw+GFs1J0zJMkd0Bs97RwKuzMNB0FQlAkuxm8cIlRwGoliQzo1cEsxQaUKGO9Rt9fgZfvm2LPQAAA\
33082# ABJRU5ErkJggg==/></a>"}
33083#@gui : sep = separator()
33084#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/03/13</i>.</small>")
33085fx_support_us :
33086  if $!>0 k[0] fi
33087  gui_print_preview ""
33088  l[]
33089    filename=${-path_tmp}gmic_donations.png
33090    need_update={"Y = date(0); M = date(1); D = date(2); date_current = Y*365 + M*31 + D;
33091                  Y = date(0,'"{/$filename}"'); M = date(1,'"{/$filename}"');
33092                  D = date(2,'"{/$filename}"'); date_file = Y*365 + M*31 + D;
33093                  date_current - date_file>=15"}
33094    if $need_update i https://gmic.eu/img/donations_latest_months.png o. $filename
33095    else i $filename
33096    fi
33097    0 t. "Latest donations to the G'MIC project:",0,0,24,1,1 rows. 0,120% *. 255 channels. -3,0
33098    rv a y,0.5 drgba 200 frame 4,4,200 frame 2,2,0 to_rgba
33099  onfail rm
33100  endl
33101  if w>w#0" || "h>h#0 rr2d. {-2,[w,h]},0 fi
33102  j.. .,{([w#0,h#0]-[w,h])/2},0,0,0.85
33103
33104#@gui About G'MIC : _none_, _none_
33105#@gui : note = note{"<center><a href="https://gmic.eu"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAA\
33106# BXCAMAAACJMYjxAAAAXVBMVEX+/v4cO0s7OU4wPnEiJSju6+qiqrCMlZu3u8BufoHd3+FDY2JRLzljS2bLz9Lp9vrteR34lyZqHE3puI3S7/hbX6GUW4z\
33107# wza97QiOaYUa6mqTdfz25i22+VxzqoGYqrht5AAAWRklEQVR42uyabZfiKBCFFYUCDDg7TqIh0f//M7duBSEx2p09Z18+7NzTPUYaE/J4qyjI7H7rt/5m\
33108# nXe/9VtfS+vdb21U7Me/hNbblPzu/6czEs/Y39LmT/jU36C0+7/p/OvXeaf7S3+7bkQ19uN4uVzGG3/i31X0/3GyEFjXnu+991sCMN36dIGu479tLR9Cl\
33109# +J/ll3PLMBKbJXbipbW+nq9Lgan2U/cl8V/4Tz3r8r+hEKMu/9CDCrDStdLz7RKXDGly+MxTBqZjM4hyJ3GR4Y1/suwOoHVuf8EFmgxK6YFWMyBI0yaGd\
33110# Two+qPH8PwuDAvLX36fiyw/oFKRcdEHYuS9UtXB4GVSP/9HLw15JwjMtGfzx/jELwyLCjp3fUx/AEBUzlgYI8rh6oItBCGl+/TjH975Rj9p5QI+4gaVug\
33111# YWPnQxOrvN5an0yFrv9+rE9m3oz7zD5xlxyviEBpHRiV+KqQgHAzoIeLAxAe+nw2tifq88pKn5P3bUTccZqzC69g0nc0f7f4ZY3l3OLQioQUdTsb/eu0W\
33112# /RSKu8iwsrXuHHWvKsju2Vlgde3774cd2dxRe0s2xjI4psEhtl5ZpvAzdIVVNVgQXDo8jaWJQ9RGCdLtgeZjtAi26BftBqgyrCrg0suqxdvcoEdm1fdgx\
33113# Uiqo1YOuxdjbasc6DmO1j5haeqgkLxe5e9uxQpid/EdpoYPA5FGxwYi+55WtCzDIlaMku6EgFL7gzJ2Hvbu0BKrsKq07KKfib/K150uOQoZzicVWr0E4a\
33114# 3XG2AZMhgGw/LzcollzeLzuiu2Wotj0YcmG8sz6aYBMPM+fSl1UECzVyxjcEnFDS2i7HCYw9KnfQukS1Lod2ILzg1oTfTPW/DjBOtebPVKaUZrFFa3DYl\
33115# WOzLmgKGbikaH0DIr8i+Z/QXVglqiCD6pMxwFAqthWDTjXRWVaqkFLdx1hnUgk3OStTNWh/YV1WmC5cwsN6K+MtZH87QW0wKrAmjOavq3RGJ/vT5uN7sp\
33116# dz7tbWNtVbCWNFSljmml9POtGjaUt10TkN2TIBVrBeN3azlmxbBYFRY72+QmYxcxiAEKo6JWPjY3FiZBS9YXJ/N67zGxenwOQ2hAJG5k1eLKIuNrs7E2d\
33117# fB0VQwpMau3MZgzlY+J/bVLPRJeppXsGzerYLquPYSDaILVSjZol7DoUPP66XQCMMkYMBbRnBVEZA35PLVzgr8Lq8vlBRfauLHQYlQbqgYGUFmpEP0MVp\
33118# QEUBU7NhZ+wwoU+CWHzjryffq+76q1yOxWsgod2swqCCxdqVRYtrDKtFrhJZnNOL+Edf4VnTPGABZ07X9kLqBVA5KboFrV3/uL3jBZW6SJIoql3NJ4lbm\
33119# wZrEEUBlV6JKoe2b3TpjggxptcBYnLT56F4cyfTzDsM2wGN0rLH+qrNDTiMidOK85fAsra52MLcGphwGskL3FRiVjXbIeBdYGVlKmE3+1eTjB+o/rHNsh\
33120# BGEWqEsO8wIlFrCwscqH/E9+3zMxwFrFYc2IXeCJMKgShqDxhGLnQVjKKoOLEpEAY2OtYJ396eScezrrDmMB1eUKWktjQUOGdU+bHmboGC2PrQWq0JqPh\
33121# HUnsEJmRS7q536ssYnZ2YoCjPpeWCEO3eqsRmAFwOLL5jD0cqzmzvJBWO0zK0dRT9eMfFGy690ZnPp0Utlyo0ShFPLjDNYPbuJlRz+D9dFaZ1zK4BuyXg\
33122# LOU2gxYMvKCN4ZC4KHEHNUawrhZRPpJSx0E3EcpvgahSqwlKgmeD5+gUXzIKQCPQPT7zaUz+HIbtWzlDU2Y2qa8TJz1tiH4/EYZrDub/O7NxzvEM/IJlq\
33123# pryPKQH4X/eddziCsWAp371531crIK6wGKvNhsfKEBcZ6AwuqsHQ7M5Yj0lv2SO2xCYrw9oLCgac6e22OxwbOyrQefeIW1fRDhTW+OR0dGBOEuGcjTTmK\
33124# z26snnWrL1m2EVaN8MqFc+2Onh9gsRWbzujF+QhRWKz1ARab3s6NhRT1LSzQcrBWRBRKlXUftQ4Mq6+whhEtoPWjwOr96mwtk5LrE5lqBm0dWHnnQDDGC\
33125# IMtAThJMpAKKDu/2vjygMRgWRlbiuhUunHiE1TfwNJUWSEOdltgcZzg4o4v2Aus4bo7O0ZzGzIrNHmGtVfuNoO1SvEGrKRWIDsnqWUNaLFIZFkogViRlr\
33126# oo3yAS1lznNSyo0mpSwr3nE8rdFFisAmuvFrCe6X2/0ViSs1gEa1meC5+wrFNHNYel6XRUh1PqMyvWsIrDYixeKmj9mnK8td5r7JIYsqGbP3mwKvwEK9F\
33127# 6t+r8AuvYzMVLnuRn/nPcxD6tsMISViuw5KLFWOC5VVodQwhaYLEuu2idc/0cliEub91lmMHq9RtYeTxEbrXXiCmZnCDpQkewUt7Vo9Bx5DQKCmS/WUFV\
33128# TjXFV6Y6iLPU2lnsCGzRnDIsUihSJ4DOxbwP+oWtOGGJDJ8oULrdp9WOzNZpnIfh1aAeyLDu0GO13rHTOgHI2N5tG5zxHknKskzX5jsIiiFxW2JcTUD0c\
33129# NleYJHzfxEWth7qTRrZilZvYYFVmQ15NO0hwzKoeeVHz3iti9KcFNlaamRYU6Glr8yKYZUEz0V9onTlakJYyXpnlbR0ttbkbqQDHLYQv1VZoTOWlZg+Xm\
33130# yEGQIytmr4l0hvhFUVbAW8NlYNQ2EFeja/z3WDMjQRgfjlLatKKx4bsleeDbGl/ED9joUgU2IBliyBuHHIrADrNr6eMeLiUFs8VqWODArFFyhFryGplQF\
33131# LyQ0GFk9Lm2Dd5rSsnf9VWK1z1mQslWHF4/4QcnpHg6ACj/fOmmDlN2TxPLUfhMZDuNwHNhQzY1x3pgUN8NRkrsdlvafsY766WGlBCqBkp9eua9MISp3t\
33132# Al7IboA1Lo01W/KkEF5ZlZ1SNa/gLb/l99NwKWZWrC+sVdo1i601INCYDfYXJlbyoJXh8KF4amKFh/1xt6ZFe/7GXtUSMwIopK8YnERNlT2q4iyi+D2s/\
33133# trfFrCaso8YXmDBS7MELw0Cy4BjzllTnnzCOn/I7/gLDsCq0GIhhXPsZY0XpgUNhdXt8X4DXsdIbYXUGgk6cPI+GpkKwyotGYEl6Yxh+e9h3fopDsfQZC\
33134# Uz+SEWVqbQqrBYBRbhgOGV/H6usM7vH0aDE5gJLAnEsgU/cAAKKn6Qwz4rAkjeg+8/bJT6aCFjJ0X8IjWRUxDGR+YTLAG5ARaDAi2imuJ1jsJMy5njN7A\
33135# wOeYwRH6vzvpsrHOOxgLrxt7KsC5PVgKrosLj6C+egmmfgcXImKKhVsbUYnABG7avOZxU0SZYRc7WFB/zTk92lnErWDUuAcsdi7P2Aqsm+PPnyVAOsrPw\
33136# KJBpAcqAnA5cMjMWWBOqy4b/+BaJKK8mltPinsdaVBYQrLCCpckVAQfkb002FvupxCFZuWZZNNl3sMISFqZGHt9+goUYg/jwa1jnJ6x0Y2sJLZ4KJ00lA\
33137# 5rQeAeqccsGvJ0/tMRx0SusKWKCc2tY6lj0/IuvK8OQahwaL1GYa1tHQX3tLJKL7iGEIaQ/FfGCaOI00cxR2PR4ajGwYCxgkRfOX9CIRu6QNmwqWzCCch\
33138# lRZa3Wi55SW/OQD29gcdZvBJYrsIpCSuUYAHRN71tgIWWJDGUoAuxLWKwCK/AUAx4PcdTY31h9P/KxmExQjVHvNsiAi7B6kVnDkoG70wqW54bUHdewssi\
33139# EfCSzhi2wyLyDFWqCFztzBIqmi+pC6nMYZmkA6dnfNyZ1haRBlN9PpvK7bTLqhdXzKBirX4pSZLa9fOmO/CwWvMIzH0ahlmFYCRFeGixtTdy55mksZ9ew\
33140# JDPu1T4Xpcc/STe7JSdCIArLCDQ97OCFVZaxtnz/x7RPA2kGknEsj7pGFoT56NPLTyKdbgZLDXYLFhTEakiaHw/kJQix1d71h5ACKQTVTZFHMhhZ4Tdep\
33141# Cln7QJJB67r/NLt8E1h0Q/W5x5hCRMABKyi3/NAkvYeO0ehfZth1fDdXIO1g1I7AscMic0uYInqOsti67tsI/LHQ7K46PMTyATWdymvhvz8l0+osHdu9S\
33142# BglTQ1wATXBLJlzf5BWSkskeSsGRbiSURpP8DKq/MSFmp4ySUBlptguZbQNWcFvK6spKDU1eaF1rgLvz4O2tVvKknsYkqQgnCSfF8H4hyimdZkQ3BVXJv\
33143# IUQxQxbVL+DQABsvj9A9FgBWifAtcDk4kVTU+iUqosNwJVjt1UFjfssFyKDAat5GVoid0WMdUXHCfkoIeTLHs98JLYBmfZ4Q5PXoOq2Mx45DBUht6ZBqV\
33144# RZZHDuuVi7oQKnwIANCKHELF5s42bCldo5eciaTGLVZjkk+Uh5XNQ9VYiR/l3gfF2Evsd05fDZYh02hLU/OCvFu1HbFYaFnmGWBhCB0Wznd0TKKUcw2nL\
33145# M/eYTlL8K6haqvi5A3WelF7eQIIUoy+vOkDYSVSVhBgVW3HJa8e9i/k5HcqpyEALLrGL03UQVUja8ubW2E1gQrJv7UG91Umc6mw6hT0yAIm9A4bosANSu\
33146# WfYEVMS84SWabP7w0X1qi/DVaLr8sOkt9ew+ozawo7t8gCF05liCy4cIK1mWCfIiOWlmNC59BhbQYLL5VV6z+wM3EKtz5cUWkxYjgzyVeLLGwOdSPd3kE\
33147# zwNqgdLlyyPkNrK+Uzldde3FQfd4eWl/GyHIjLH+CFTKG02BDWUs396RlsKAO61u8H1q2iAeruLHg4lTGpHUAkpyatn2jhNaHsVJdTEc+udCNtPJ5HkMI\
33148# AsC5nmMyQgsVDJYbYdXeUZaVOnzYLdddqClT9Tz8m374KT5TnvNo2FcDthPAmGMUDxIx3OiruO5xoF1pPc6s/ArL0pDHuF7JidrIbEeW9HFt4KEvSl2H5\
33149# aacpZ5VWMVXyL15REXg7wUtZ3VWHVZgb7A8lzAMKWFC12zVLjSqDTNYVeHdDsdD94Vd338aKQ/F91GbBlYrrjgHPZ7NlKPSUlgZjzzDciOskFuyao0p2n\
33150# 84wvLKqsGCihvk8/OisxCcTeXFogGs4GC1/ihXE9cvoVRhsT/puLI5e4WVF1D6hdIC17tRWI+LSAKrP4zB6qGFWu08rBuzubDD+noJCyk+D7g2pogT3ea\
33151# eF2nsuT8MkbPwaTnL9MGfDwGW6BhLN06X+RDDtgvqSS4ve8ydR1q+yWV7mBFWNx3FZ4ScXGg5C3QGWF8NFhpml+deTWbEFRYiTXhx9quwvxhB0d821LEm\
33152# 3RlUv632UzpF1nInSXNBknONDQNjNrSyb1nBmAtf2vDJzvkOK0Rnsn67Ynp7hQ9YUGEkrEWW2incOXLoUQ9ABsrpnaL3a4jv5CcnGirIs8GC3UZYZC3hw\
33153# hVWAKxngVHYyUpN/pnFymqZad21H6qtHxqdJN+If2cVEtbvdSJNHVx2jAx/acRVRH1vZFQpPvlV6bHXfVihcG26yox4+VnNwIdpm1jdg6WbELcIsNQoGL\
33154# 7pYuAmP8Lq9Sh9OfvQbxR12idYAbC8wbrq1GDd2AX9NyyoxNwAQQZL1wX1jGalRRe0PNsKXoXHabBCREGtxQWwQoWlf3yNLG+KaezU+TewMnG6AevHD4N\
33155# 1y4arSoq8GaSmzDFiO/PGvDE3XNgVTWmXDFZXjpbHqhxqIbJGpP2noTVLp07f+v7ChpY88NnJ/4SFOcNCibNrws4zTqjWJrSt0ZWdd0wU57tEijFNZZGG\
33156# EmplKZ6bUUxTp1NwecwqMZalN2CB1ltY9OWmQr/KF9W/UtnDdd/gi6A4jZuJmPqZUIhkKj08qMpqpakWCqzS0mluy6xOCqjCHQMd17D4Hiq7yy9JoI1vg\
33157# 7/GFYUGtvW5hgEpqWHkwbQWDj2b1mZnhd4pVDu8iP/l0xzM/BoWDpj+tHN2u23DMBSObfBXG32Tvf+r7hxaabu18RJsHXYxAi4tRTSlLyRttU3kc79oS4\
33158# pTf5GTcPxDc9in0xePTzi09V51n/WxLp8tu2Rm4WAU/DWBM7p8yuTrAevuY/xqv/N2quf5AHakvG2p5W3gp0vKU058nbDu7njiBH5FWIqbOrZRUGrlxJO\
33159# aqRF+jeEvEFw+xherypvWMuwnj6n56Jry0VLdUks8lTcBWBEBWPfkq5/Uu3WM8LFCe8YCHYal77HaWMYY9gorl6FCtf38mQoQz9cWjOrdmiwfXP2g6CO0\
33160# XBNohz/BqsBqwno+tBJs1LWwPgWVAClFKyq3wQBxVw1Xc2lYvWRtWKWK3KOqynSX7iEr3wZa2a/vVX1f9CUyq0yvr3ZXKlP3lN21ZuQFSLk7wri7Kt386\
33161# kajKp6zD5eH08B0kuaean5clM2L/Dqw7sP6cje0fA2HEswwdbFgTInY6rZg1kQj6YwwK8ICBgTOtkXagJQ4foKpDhWGRDhmGhimxYgtgaKHhhVbS/+Vyb\
33162# axvZpnQIM0JDuq9sseS3fFyhHrgBEuDG2JyHVkhGr/cWBE1cLLaBbVaVr6OmGhvj8fWgbnByzfcR6LeSXiDSx0GX4Dam7eaXhAWSwdhKCip3s7VXV+FnY\
33163# bhrw+1rGiN2+wQgHJdH1vDlOGYw+sozwMM3jjVYPnMIqFQ5U8d5gU2swJL0DTMcpWqsizW2GzOof1ZdV7YWkyYXF6sSwMBOFvh3va8832LCGsGIypI/yz\
33164# 5zv8ipcdPWAr0hfjV40wFXXV2qImg6QvrhnwfzIPrHZgRA9stdNT4YihbXSg1UxYHbBMjPNoWFHUNqzH32UV6yOw8GCadyIL11b47z1zsWYxhoq5cYO1O\
33165# xYyYcXwMQwTTR3s9aExRgHW621p375Vt9i7de+MLKxxM3GsPZXJTHOD2/nsf8BqtTOGZRCnXgBBJqx+5wb0hCVywMrWDMaTeq9kdVqyzp+1dDUhMRBYzY\
33166# HektEhDJMxXjdyI6phKcaFYXIsUAByjU5N3ybbvWGNyg0Bou9gXRrWBnOk4zbNScEKIh3GhwEjBE4OWOsLLKnlPiwxFMqTu649BIvyMSxG+gFLbXh2gSc\
33167# e1isbrEBEJ9kReKRAVxss3glLtzDPC2F9iwKrCWv/9g3H9jGstc3txVyUYZTSxjGKHnVRHDOylglr9jWXhqU/Rhbv5XW2h57lnVl4JncqvOgIG13PK47i\
33168# EVYXWUcx4q3LRJjh9IBly1BlZEV0Gm6DFdyPkm/hcsAivgDxn9Nw1qxCBNzM6YGTCG/UhfMIB5F2SljxAot91CyurGMc9zYNIWfPc6LY7JwG1vlTaSo9K\
33169# JdZYWywfO4WIFaBRpRoKxbIqMRRQTRcoBpUELTJVedUD2NlpqVYZLuhSejOgfnOPOkpfOY8vRqctNPDSIU+Y/QwtGkWyQ638MT5ruHK7hNakDTgWk8fHF\
33170# ROzOfnmRJwvAWnO9vo4EvHECi0S3ik144+x/gRWjas5jBYQcrl1pI+B0BvswtN5Y35gPkaKdUeWrKUlrMrcToNBSDQJ+CJEe4ihYHlKY6ghA5OhHXlTMR\
33171# jPdnrnG8d9sc3vHsfrWbjGsPiw80Jx/F4Z/1Wprkxd0926Tcd7ej+Fa3vhiw455Ia64dPDNyTfp4cGWE5574/bz5T9yFLDz+HYEe9eECk+h9aXzn1feqT\
33172# halb8huwnzGXXwyVdH9qySJZkPz/XfH/5b/88/IdcfPxH+J0LVIAAAAASUVORK5CYII="/></a></center>"}
33173#@gui : note = note{"<center>is proposed to you by</center>"}
33174#@gui : note = note("<center><a href="https://tschumperle.users.greyc.fr/"><img src="data:image/png;base64,iVBORw0KGgo\
33175# AAAANSUhEUgAAAEAAAABBCAMAAABW61JJAAADAFBMVEX27vz06viVWEaZZlbz5/Tv3urwxMeFXE2icmuBUT7w4u7QzeKaaFqRVUPs5vWKVEKBRyvr4vKR\
33176# WEfSj4d/aGWuaVjkqqaca1+ATjnAfHCVWkh7UD/z6Pbv1d6DVUSGUT2TY1ONWUji1ubv0NiHeoaLd3zEgHW8d2qQYFCOX0+LUDri2uvcnpeQen+md3GKY\
33177# laYYlGXXk3p3u3Z0uTFyN7nr62Aa2uoaFehYE93Tz/tvb7su7yYf4XIhHqgb2WRZVieY1KKXk/u1+Lvys/uwcPPi4KkZ1eiZladXk2ZW0nn2unXxNHpwM\
33178# XqtrXntLPfr7DhpqKtk5yHdXuEcXSoc2uSbWeeZlaIWUjj3u/uzdPTvsrdu8LHrrmjipKQfYZ2ZWalbF9zYF+TaV6gaVurZ1aCYFakYlF9WEyJW0uDWUm\
33179# PUz5yTDyETDTd1efSyNrRt8HOtsG8pK7go52IgpTMlJCbhY+rbF2QXEt7U0RiSTxXQjJ1PiLv5/bx4/Hw2+Xfztvmxs6WpMLlvMHNsbvCp7CalqqkjpuR\
33180# h5iefX+iene3eW23c2VhXGWpbmKLZl2wa1unZFN0U0hrT0U/OzlRPC5aPCsrKyrKzOHCw9nnzdffx9K3u9KvudKnsszAtca+r76tqL2UnLfcqqp6iqiGj\
33181# KWxmKK5mZ7VnZmbipbZmpLUlo68jY1rdY3JioC7f3eveXGfdG6wdmxpY2yIbGqxc2ZRTVNkUk9LQTt6STGFSjBHOS8qJR4gHhkWFxLd2ey5wNjGvM2Jmr\
33182# mvobGCka+mnK6Pk6zZpKNpgaJ+g5mtjJB4eo6UgYu4hYHBh3+ZeHl2anBsV1RwSDM9MytmPig7IxPSz+ShsM3lubvetbl4k7TOq7LFoqnSpqd1hKLHnZ9\
33183# paXmVcm6FaWRMU2F7XVZDRUpeRTZTMBxgMReaqsezrcC8n6mklaKfkaC/l5pbcY+nhoqvhYNMY4Cnfnx3b3xbY3eOc3W1cGFcWGBiVVZbTEliS0O4qbjS\
33184# oKBQaYhBSFRDMiYCeYKqAAAKU0lEQVRYw42WBVhaaxjH4XDOoUREUhBEBQRFBLu7u2N2TqdzttO56czN3Kauu7u77rq7u7fb3X2/g9db3OeBv/Dg83D+P\
33185# /7v+d7vPR8OhmE8Jhx4GSLsSuytFYyHceD9l/+fiPFrwD9/0/FjiHEM3tiYMXaZ9vt/+rVULNxfoHE39oH5MTcex8DhYB03EDCmL+z6+NTPVStWLj+xPA\
33186# oGtH+GGxNAjPPB57/lOPNS/74D+18m7d7d359/3btTCAj/QowVB0LouLGvnbd+8/2Bw7cOHz584Kf9+3+4keSdMAXRvQxE0Amvzb/ss1f7h5tv3Rr5/ff\
33187# Dzc0/zct/kZ2QMAHWJWgpugu17Mt9Tc0HRppHm0dGR9++bX7blP8y4aMOoc5CG4M/XRkv+/LNyEhzU/lUUVrARKKDQ/GNn35smnVxymKK7sX/V8Hdz74f\
33188# fjdKLi1tJBBS85KTe32uSH840OSQKTTXTWv8P7yH3+w/cFBOAApzcxvY2ysLYSqz9428zqQY1qrOX/7wI9FI6w8NbRjYa2MWwuTEf/5jQSZiEIBx/rf9x\
33189# SQMkOof6ubG7sMANS5xl1OqFxkEeO/X/te+JMyv9ncLZbP7zMyYTE6MV/vG9Y8M2m4Pn+c7+HIJav9UcAfC3NhsGzOmkhODCk90P55hgP/4+eusAl9QQV\
33190# hqqFtDQ2gD20bG5Lh4oojd2exoAxIseOq6hwQAqeAWlLiFjiXgeAajCLKkPhM24BY83UMikQYVRAGZrvEPCy3SAoJRAKD2PKLoByzbmkIicec2jb7//m0\
33191# yFD6nKLShoXfbhlg0EGntemwA4IPnJBWpeG75zZu3D5LpUAm7kJDWVB6wY3MQJfJUpwGAmbt3qmbtKSb67fDzOGgLsWXbjSx5Nz3SemMojmc+MgBwMufa\
33192# i8+/HSwOMJo41YOn+S7u2UTBQQuaenvcDMrZ9QYA1vTv/Mo75fU8oHKBwK8vfhaoR6D29fklg7JqPWxACf2k2df2TJw7r7z8puBguE2NN2tu01uLbXVxb\
33193# cKVj/QCjFf2sJJJvgUOxLKpHqIyD3ez2E7XufNul+/d4rIoavlivN4usJ+VsgPsJEVpY5qInkYrkcU8uvhyIj2sZvXqzJ+r2/QCloEm2EEqJKghuoUF3R\
33194# SaI4uPjd+yk7tXpryfuXij/k78QOWbDABhBD5EmwRB4SV9yuDVzO1m25gcNIOS5WmtL8LSr/J2qUh5oe4REA2CNCVFQ/FI9BYwEWqCUSEyIUGmD7Bw1sC\
33195# u2aS8VPdwQACvkqJN94VbGmxC4tEg6xkZCdtgfXvRPg8DhLlH8EEJ797R2HmjVwfY3/nUoNbW1tHrz+lLcFySl0wiFWIl2NraVlTQbOp2MZPDi8yUwRnW\
33196# 1pkbMvQ++2eqVMmkwtQICAIEMrlizjaO57PC8O+YMWhG0ON4/Z1ot+KiimTkD4EloNF5PAtan9Kz/aI/2ycWRdEn7doK9N7HnVwN1gOgFeg0/lBI7P3Hu\
33197# 9x8OGhQUHQgzgDdrVUZhdPJPBML3qFDkyLYPkrlFzsIZvEAYA0bkoCxlsRVQ4BQMc3E5BDE3hx/pUAcZlaDZgSO+/VM99OkQjUEKjA1nUQDvczxav80ZQ\
33198# CUkAGPGVsWdnbM+BuhC5v5woig4YM+VJta0N3BAnrGbfFhuqBjBeDxH9SqZLGANX5i021newJB3Qjx1RpL8sEKdkgs+uDJBiUHHdsHeOcLKYShDfBfBz1\
33199# dQPoF8FhpbOTz+TSybbgN08WrPY7DcUG1sc0ZJ7+Va4qYQlhrNsebm//PTQAPRjkxjUa3oEUMhShdUM/ouDgvdLr2YHX3aQqB7w4AY348rAvArykAE4Uo\
33200# IpN5FlCJjdIlGkWjY70QGPPjl1wqEPPdNyEw5ofxxuYtuhuq1k8sJhInepDJZAtoKORce3Bb8APwk1rAUkmBBhriAABsDsPGeGNYdxXsxfIAAJgqwAjuv\
33201# XWrH7Q/CMJjUUHiBbVG/PC+GCwB3MLAw/8F4BlLan3VmlIsgQfIwKMXXa17sjpa+OdpFLa7QIhwt8EA5uCkyzBnMIBpvL9aIiuXbH3lQNBAjUTR1LkeHg\
33202# IyT6BW1f0SgyIUBA8DAmy3JiVijpknBYbh6S3TGY7gsA1WogWhWFm1RmV19fT8Np8M8fmNpSKBQHCTTBaQ1bvqXLxiAx0roygYIP00FyrZ5oVMh7VuR0d\
33203# HnFNr1ASgqhWf5OS4SoeP3qZBfFNLOkYANYgK6+I80csJFKvjkaAKSvoabvicTVgiBoNh55iezsBNAerI6ly3MTExKTf36LE7h2iQKd80TUTmlfMEctBI\
33204# KPrxDSF1uqMdSEC9IIaKNnkhiB0jMjLdzsrODldVVZXVvTGhW+LqmpSbf+TYHZMKGoSFEIl4ZNO9Sk8UDTrzZrWTFeVuZIvxaVeyRYQsVohgXiuryZOtc\
33205# Fkd1R9vrM++LGW5uubemH/s2DSTCjCQTC1NIRotXAYGOprRtefqs+qsrI4JCy8Npvn3bj7XBqxO4AWE6zy1rt47J8dbwmK5sjDA+yYmk0ACrcAo8vIK/v\
33206# QayTc5WUXaqbpy5eoGl/uZ0dZWVKoV1cmJSnXCJdRftgfyTsQA+fNvHQUAWzpkSoNABnaIC4cDxqQvF6iggEtKlm2KRgIXzYCXfeh4nIoJJ5FIpVJ7b3v\
33207# pdUDInX/k6DsT8FyEsIliCRUx489tAac2LtihRlyuEbeQa8MxpyAIbunZVvMxQE99vSRJai+1T8rPZwHAkTsmh2xtaYBgSYfmMDfXzeYSjLTy8xODUeHu\
33208# EwgjM6Y7nZxCXagFdHd3f5KTKJV6S3YXu+4ePnLrzjQTW1ADEI3G9pEpr2lP3sCuKA5QyBXQ3hgKskgYebJj5konZ2olrn7FihVnahMT7bt357KuDx89C\
33209# gAmFSCCRgNBYQ1mLi8xO8FPEUAEe0xhaem/yTqwdfHi89UPOxnOzlSc9/qoyhMf1kq9O7bmsr59M3/fkWKyCbYQ7AECBJWEfDWSJldYitJ4PB65jEgUiS\
33210# CzGCRwwpTn1acDcZOdnXE52YuWUyvPSj+PWvM1K2ff4a3De1hTsYluI/NtpLtvfk0QK0rp4LwzelsgArtcpOj1QiitWd+cWgkz0gFgbXbb2irqPUm29Um\
33211# Jq/T7fScubEzCCJPMNs8qtXD3zzMCBWAjoqwMBAgQc7c/80QolMpLH0Y5Uo2PO+POZEedX9e6tKfe+t7axKTrw9Vbq2pZL/wqbIdqvnCg204jgFvvJ5dj\
33212# Q6ZULh64uiGuLRBBKJEnVlktWMrAOePWfVL19LJwydps6/fWSpKS3kz49Yvll1gklT+bOTuAZzJNQxAPOsjFAQqxQk4gpPadC0LwAECx6oq8t9IRPxnn3\
33213# fPRWUnWkq51Ue+tkiQmvqr+LP/TrueslO2y7XLgt9wpHhx0cCBiUmBrwAxGhEIEyGrBw+WRduk4+5yEVZL1lavOLHZeJZEmvlp3vn/2qa9zWbOvXCsDgC\
33214# dfOwz6OWgJZZallnS+j+eitqBABHTjvTWVTlTGH/5VIUZ4CClbAAAAAElFTkSuQmCC" />   David Tschumperlé</a>     and    
33215#@gui : <a href="https://foureys.users.greyc.fr/"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAM\
33216# AAACdt4HsAAAC/VBMVEW3xMQAAADUqJoDAwEICALFmozFqIwGBQG1jYA7O0EfICUdHiMPDAoODASth3svLzYFBQQoKS8LCQM1NTwaGx0SDg0bHSEHBgYJ\
33217# DQTRpZfCmIpuVk4XFxh5XlU3N0C+lIemgnaif3OPcGZ2XFM/P0clJSoXHAoUFQfAloi4kINrVE0QFAgJCAece3BZRj9WQz03LCYODw8UFRjNo5WXdmuEa\
33218# F6AY1plTkYpKysuJSEYGBwlHhsYFRELEAWotbWxin2ffXEyMzlPPjhMOzVHODMkJi4yJyILCQi0wcGTnp6CZlxPVVVDQ1VxWE9oUUopKzQsLTJDNC8iIy\
33219# UrIh9nbm6LbWRZWGOGaWBJU15eS0IxMT0gJCkgGhYSEhUUEg8XFguyv7+Ejo7Ep4u7koWphHibeW6ZeG2VdGqJa2JzWlJERE00O0NSQjouMzkhGxkfJgo\
33220# cFgkQEARBQUk3P0diU0ZBREQxNj4uLzoqMDZAMyoyKSY1PyEQERMcIQ8LCwwiGwgYEgYSDwSturqfqqrKoJOvlX2UfmpXW1tJSVFHR1BIT087O0hhTEVZ\
33221# S0UiHxsoIA8nMA6wvLyMlpZ+iIh9YVhPT1hFT1h7aVc6QUlHR0c3OEVdRz86PDwxMzMpJyQnIR4qLxoeGBMcHAocIwjHnZDCpYpubXpvdnZvcnJkZG9XV\
33222# myTc2leXWhWVWBOTk5LS0srKzA/Ni47LyovNRofIBQkKxEQGwq8oIVcYmJcXl5RUVtWV1dMS1VHPzgrKRSXoqJhY3iljHVbWnGbhG5naWVQW2WIc2BNTW\
33223# BTUl0+P0xFSkk4OTZ6hIR4gYFze3ungHFQUGVjYmGDb1xoXlVdWVA+SFBAQFA9QEE4MjIuLy80Lyo5LRmFhJG6k4d1dIC0mX9vbm5UU2eNd2NJSVtGSi0\
33224# 4RBYyPRQ6LQ+lsbGapaWAf4tsb4Z5eIVYY299aWlDSlN0Y1JvX09RSEBMQz5CPz4/SxinrKyVmZmNjIufeWF2Z12ObFYuJgeXgXY+PimhEuIiAAAMWklE\
33225# QVRYw7yTX2hSURzHPZ57UeIqcvE2/IdOiZz5Z+OqUxRnc/iXDUrHGJhOlpuDMIN8mA+p0xJGU2a9jDlXIRgEjUYPG6PtZU+tFVHQ/hD0EKxeeqgoiIqu+\
33226# 1Mrrce+cO49f+75nO/5/X6X9h808O/lMcvfVjKumjIZT3HsX4C3RRrN2eDYR7enNrjE1tbHucTG4rXJ0F995KouZ7XOxcraxw/laDlh8psD0Y1o28yZ23\
33227# 8DHPGUxkJ/enT52ogNP4SEFygJwDFjDliyvMk0BAjX45bxT79fovgxYOZgsw6oNI4qIXaTg2EQlnIrDQEGD3fhczF0eOrN3GmHIOJ1MxX6qLHrqk5hw0x\
33228# +AGYsjQHjuaXqo98cWB4qFWQky6PzeJJmhUqalbrJl60AxC2NAa9y48Xf9zsUZ3t5Ffq+Xko7SOrZa1YWnZb6hHnGF0vVMdehMPowQUXWjUoPALzs3ksV\
33229# zQ08ri+J0NTdBd9YaPUnemWzhxSPnqWTJP0PWVcbVdTjtuq4J+OJHxTKpIGI8BQROr1Zsntuyy9Atmu1AeDFyczk5NuFjPMAIGwdtSp0tQjubrpDQX5aW\
33230# GtQCty7S5MZn8+zvnIAGNX0yw8Zp8K3F4MKKdM8qg/i2sy19Zm7S4uPB/YBs9PRTt5hAG8vGJUIaRVw6zP5HBoMU9y5tYMYWPJGwka2qLL0OvE7JVt/An\
33231# y055e4r0qbc679n9DlafLrjFKyIiLrAD32ygMK4Mw497+lkl+irT4vx59Oeaq1SeeAL795U0u6+3gkX7Vr//BltApFIrRwO3Qst1CdHHAVPZeowqCVVg2\
33232# LL3IzPgpgcVXj5bI3q4tIVa1aZm1PM/uwhYr6jA84hADi5pnNo7OngXDFQ1szTJWE3IcLb9/QckueYlnozarYIjMw6/v6OujkvgPenb5aZkYuxQN5IlbY\
33233# 3i4AvMAAwOygGcvxMnZm3VdcsVwz5F5wgDc7JOoSCLqvtzq8CrK3km05Zzun9tpPySlAk8GfD2zfYuDpGJ5kJHGHktYUJHAEaVMKuaeFTW2Y0pEYmieIU\
33234# 10Jqa5HOMJDeb3XE8b3l7/NG9UUgPPgCwZYOzvpFJLGWTuxJItmBgDHY0kGUH6/ceOGn0jkmzp7ulVqAnb15LF+Se+dL1cmavpgktBHsGf3TIBRSMfArW\
33235# QhCWMpQLOD4wAGWRAoX7++HyBknd3XO1qmNRrO1jNp74h/3mS6N/EuqmwavWgy2bTGAHSkYoVYGsdBOgVvAkCj2p5uvl7ehq3t04RdEtaEZecmnmkiNuZ\
33236# gpJ1te/dudjpiPyuax6IExmDFGAA5j4DkDmRRAFATQvXMy4UglMkEmLq7Y1DhZrL1IzKvGB02Dl2fmNA163SivmbbVZGdEWQdh6kLt1iprxCnABACKh0Q\
33237# AYXtWAz49e6AWn1V6pZq+eI+tUksVykEeiYTbRmShZvl7m6RLQU5QQAhA0my0pACpBCIBzngAtgV8eRJoF9uVQgEonBYpjGKmV2Doqtn+WK2VNRuZfcLh\
33238# qNgXwiCfK1dgWrgxv0TjAPAtEOAarUoKrcyVWa7uOWcTDrUYu206SUSvs6mkAEIaoLwAkgjNQdBanSLFYMIYw9wyjSI8vkoynSjEuMwija3C6xsvlgsR9\
33239# lhk1qur3llMBgsiOMQT1OAPLXt/vJyAUGOU732jlOz4kE+WhM/3MMcVqHtw2zm7jgik/Uz7YASC+5eASJpqpB+sFQ2IUqEYRyfl/eVmUCHYSt8UQcdrRl\
33240# nRx0NP1piUFxxwVAwMxEqCb8SpMGFDrUNdSk6SAjZIaIvpSA2otbVW12KDtWlCIqIiKg9dIqgQ3TpHet3GIYXnv888/z/zwyacWCzVjtrQ9BKzwVkVZ4X\
33241# MJl00O22MGrdMof1tDKCFdA2MAfSEk0UKKQhSYREdcFKhO2F4tFK/FrU7Y67k8XlYpzpX+P9Fj/pJzbstDZaVrKKCIDeAqDRuYWRDVCTrW+DgaTBmlSDg\
33242# KzYzrzP39m3L1zOlxL5kvVEqcwKbJ6xMMEU0+onreTRBJpkyNbrIShRmqSL+tYWtiFAqEn2ht/h57vHPdalnSuZdNpZDW2UU4V4pxLzt6os6YDkEAEagg\
33243# Uk2UCPAhCLhoIh+I+d9fvqFn89tJLrqDmejzqj+bjcXWowwXTscBb2SCmBXMjUEXFBGejKE2KANUCOFmjI5YjhQsoZVePxYW69HE9Z8jud6rrF4k62AxD\
33244# UaKDR/6YooXM0oNBYMaSRSHMcGQ+EHLgZDVa8+X5TCPed/VKp0FrMqnJIIInwdlEPmOGh//tAxggoHSARYGIE0nVNH+NZoOBsMJmkW24W98VzsWFiI1Rg\
33245# TIE6exhIpl+kXsEaNCXMV9BmmjLDioY1DWJzkmHWwWQc3mjQE1oOrKSvNRx1byxJglDeCfB870ww/ndHYaRhPJvNtKORCJ4ZSFRE35BJy6lKxuNgvd6kz\
33246# DgYdZElM8gAhMF/FwDqQWhDCFEAcBo5VeDJy69cylQUp8al4w5vnlUTxfbGkA8XCkIydpptNBJ28A+MTZ3BVP9FIkTdIkwVZQIjr465ADD1Nh+32KjcZs\
33247# LZ5cCyM7GeYCu8pameDq9IY4I+GG8NpoNfv6a6CEVqjfB1bbImGq4XR0j9Gdeex4+dAsmCp+KRfesCry42u4ybf37h/JufNzYREgfit2+SJo7GYm+MKQN\
33248# CaEynhkE+ChC+2//g84Mdp085vUmhWiqwQaERdft9xTr/5cfShQ+rn+6u7lrdZud0BU0MGkqiQhlmCxOMUGTTRf57By+/DB3OJoqynBTasQRbDfssjlCx\
33249# 8+VOeOnp+107rnzfdv/jvUNXIpGjoydPJkQAcByHJ2uTva+fXd2z48Dlt7//3M5mni+m2zmmwjc8cpPxpLtf7twJP6puX3149+HFQ9/v7vr+8eO967vRa\
33250# C7wt6ayCW0aDsN4RCzm/xfTP4kpaA0eEhDxq0Fwxh2MGi8GEouWmFjBXoTUeGhr20npraVd1Yu0q1URD461tm47FWuFtqNrK6hzuokgzrGjil/gB17MBH\
33251# /393ne530Or8uVvPHg0cQlsRi31HNXn/8Z3nS6PnN0+v7mzee3bVy36/zhx7MBR/3xi4M1b7nJ8SaLr0KW3+WzZ+3XduP9i0cTLx5xghGlw+jck9/7nWv\
33252# uphztO8Ondq3fuP3QvgPDtn878msikc28/fguDzPVqm+8AbkpUP2IY7ceTg6h1wzo1xi6q8UvPdty89iZg18DgZnIhU0njl05Ox2Zvee4F7n9EOpUtWaa\
33253# RSZcZHNsbnm5NRhkxrHjbpgh+zhrIXf8U7AIT04c2ToyMhZwBJaW0mPz6XTKYZN6+fMNIng2iVpdujColJTeoqCaZCOEsURTzTA0Ho2rYAHQweT1oZGRv\
33254# XvvOQLz7bELewKOf/PTvyY5hrGPqHygmVxFknOyXFSrPuYjNi4KUBV8FKHQyHDdjtOj1717926YrQcCS/PtmVWBQGp68pIR7FUk2I9+HsC5FkBRXQZ4iD\
33255# HLWI0RcC8xjnhe6vqHRqkSGL3+fWRDZEfd8Z/Uq8kbKEfKHbgTfYrmvKUWSBpSh1Y1/F0ZIxmzLzTIMmuAIE0kaDgHnpz7dvd0atV76XSkbuf/8cA1V4J\
33256# zDFqrFTslmJPXUD2dgTWg9msYp2bjkunJQpcnGPYwxJSuJ0afnrw2vbpBfWa2PT8TGeL1SrArkoSzEPsAPSvyWkCQeeQjCZLF8q6MXQNu22tEMpGFMQU0\
33257# p+DDB7vbY6tEvqYj1NTKSicXRAS91hYQhF6MJgS/iji7liZGSXiIoqNRCLUoT3hdJGMgnFtz8UL6y90NZ76mD1/DG6GerNe8Ccbr0oTlxVIlyQJfk8jbv\
33258# fqqmCto1TjNWIiDoEYRIuFnOUjGPUn/cGQs/eXOxZ1ixvtZIX0myWQF65PSEphlvMHhoawPiawdwSTDMLxgRAsgXOAJXZY4WpQVxIeAy3V5WJEInGM/A1\
33259# +ZdJMiwAuxFX+PMxEtIicNRESSGBJBtBi3V0gYQYHhdcMUqVJUkaBVXLCCMUlnGI4uNTiax1XCFlAkmcapAZ53rnGzXreFAcPSDAXGw0OEpWYggfwxmZc\
33260# lq4AoIRyWlQrQKyLbcIfjRYvktDBoDZrqYg6nPHzflzU5DAF/AqeUboxPJCQ9V4LOWMW/KFmwKcbU7KKkzHWE/DjhmSpoCcLZ1fQO7uMId1VI+rM4zXr/\
33261# AnoH9FcRhS6kAAAAAElFTkSuQmCC" />   Sébastien Fourey</a></center>")
33262#@gui : url = link{"( IMAGE Team / GREYC Laboratory - CNRS UMR 6072 )","https://www.greyc.fr/?page_id=443&lang=en"}
33263#@gui : note = note{"\n
33264#@gui : This plug-in is based on our open-source libraries <b>G'MIC</b> and
33265#@gui : <b>CImg</b> (C++ Template Image Processing Library),
33266#@gui : available at:"}
33267#@gui : note = note(<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA/CAMAAABnwz74AAAC/VBMVEUCA\
33268# wMBAQEDBgcPExQIDQ4KFBcGCwwQGRsPISjxhycoS1YZNT7rgCEkP0g2PTiOVzXidSJ4OxgXGBg3XGkTLDQbEw4eHRxCa3gyGw0aHB/uchSHPxlYVlNwNx\
33269# kdHRtNKhNNeYgvMT8XEAnvn2vhjkjQfUi2hGWMZEo1SkzXbiQjISAmIiAcGRlnjZoTExM/IArWw7TnhWfti0igTjYmJzKlVizpcyeoqqOoRhfIZR8jIiB\
33270# mMxQqJyqFpa9VLxhhhI8mIyno3cC8vJqJiHvkqGzujWbukle5e1CBRyyEn6I7XGN/RSJBPj0iICFnOR1aY2SgVBl5nKciIyFEJhRcLg7DvK7nqH3vr22l\
33271# Z0qsY0TeZT1kOSerSyKTRiHg3NeESSd5VT6PsLuLr7tGRESFiIN0OBM+TlwyGgwODQ1XMhM6QVprSDzSdjloj5suMy6lfWE6VVxxSC2QPROUcFePbVSJj\
33272# ojTx720zdXDazPGurA2U12bgnFybWhjW1YkISAnJCNFM1Tzdx32exgyNzMhHx75liH7nyH4jiFMM1UtMi/5kiDpfS/ZrowrKSf0ih/2za9JRkX8pB+aXj\
33273# 9EQD8rLirteR3ncxz68Obz1sD1wZjUo4DBjWlRNVg6K0D6mSP3hRxsNxe/URb56t7muprtvZjhr4lqa2gyLy70jinwgx/1gBvpx6/uxKbzyKXztITcqYB\
33274# KcH1mT3hmRGpgOVxOM0w+NzV5QiTuciCmUB/8qhzfZhbVWRP65NPgwp3btJn4yYrOoILBkXL7yW7QjmxaNlZOTEpBLknxjDkvKDLQZSDgbR59ao11XYT3\
33275# xH14dXNycG/Wm25RP2fCiGCKblw7V1yhdlVtXVJEMVBTUE+daky5ckkyQkFVRz/5oC3CWiKwyL2Rsbn0xai+o47714byp31kYmGvfl1fXV36uVrmmFR2W\
33276# kayYzSXSCJJMSG1ThfW5+PE1tL72pPmvopxWH9WS3adg3FYT1v7s00+OUX7rDJkj5mrlYVSaG9uaWbbdTHVCtkHAAAAfnRSTlMDAQkhFRoPLij+Vj7+Sf\
33277# 7+/v53aTRHm3pkXf79/LSsqIh4PP7+/v38+/v5zb2ahk7+/v7+/v7+/Pz38uXBr56QU/7+/v7+/v7+/f368N3Yx7enkYtw/v7+/v7+/vzp5uPMu7u3sJu\
33278# XcGZd/v7+9uzo5OLg39zb2NXUy62ggmXEYn5YAAAF/0lEQVRIx+2UVVAbURSGl92NJ00guBeKFau7u7u7u7tmG0KSxiGQYC0ORQu0RVociksp7nV39053\
33279# aR+g02VgOn3r95DcmTvny7n/PTfAf/7zbyD9TTGBCOhM2Yy7DRqYzRo+H8CH6MgGR0k1df64qQHo782WZkiZ+rh+MpFKA9aEKIdbAL+ALJy3OkO/ts2Sf\
33280# KVhobFSM9z+R5OpbGh/jF+gJvYj8II5Q9VqdXExy27OAggAZ2kqrf3CfGIzRgF4wATztbQD3tKkQM350Ea6XC6WC/n+4kh1pJw1Z5RRUlIgM85HkY13BM\
33281# YGCCQxYBNvaUOgptFQuUql4kZFBbhwVfJIVqSqOEUzMFDpo1AwYZwEBr/YgH3rp1cqNTVb5WKBQBCACoQCd4HAn66KSkYNfgrFfTOcesLsly+x+Im77xs\
33282# ZPQ8Q+IsDAtwvowjkqki5MEDlkpzCjFFkDyfgjA9bb+qLg1h3OtZ+KZGClk/u7kJ3d1QhEPP5LREBUVyu+mlNw3ASzgRQR46cPfXmJhAzrC7yb8m5k/OJ\
33283# zxegCjn/Q1lmWYELl8ttnW4G480AzXw0dc/NnW1+Z7r/w1ev7uS0qNAc+OoPdzIzM8uiuC4uV1ai44QDpE0jGFR/1cXWJmL/nNc1NWUP6f4Cf35kTpxNV\
33284# VhmASooHuLc2TsggQ4ebhaoK0gc8aj2tsfjh3Q+Cn2CTWKhR5gE6+C5CYAPw5FMktXbA8CCVD6fOXbwgEIrihiFMrnPgAEftw/CBEErYHwBRIUB3cR6LQ\
33285# 2TVDG/l9cNXiETFQiFFN+FogHbqpJRQWrQE338p0rEhnlY3gzCviAhJhg/3iZCiEKRjguPj49D6w1TgxpxzwAxqNir23LOWNcoiCWMSB87bnAlJpBTKtD\
33286# lwsVY/ZWg3NUA3hzMHcnAhsB+fJ+MoXZ01uL0dKkVvd+kXgWSXq9j00MfqQ1TDQ2HxE0n4hi09cy1sX+NE17BaZPpdIrdI0s7yd1bdy0nFrB2TJhgVaS+\
33287# 4mIY9MR3Fu4l0kgwAGsTDwV7eXk/oURESFgsy74TJ/bNDe1lV1Sk5nLRS0yR2mzGSRAmUEkQzJ5LW+WFCu4PlFAoFMn5RX3Rep8KFhelKOXp0rgaM9xJZ\
33288# juyCSBDj3w0ODgtuoo5cJBEIjnvc36RT6xPxaDk5ORBQxt9s5jzAdwERq5ypMIgDJwMDg7uk+jn12jVzzLU13p5Q25W2PlJkyzjQrJsRpHwh0DbfK0eBG\
33289# AcV4TWNjdnh8TExipirPv375+lUNyKkVqv0cfyxxeQ9cjkn+utR6Y3Nw9sbfTLkoaEWC3zjQnLbphiAmn8BNcAQG2fbcD28yDnjSsHKpV+vrm+S5ICjYY\
33290# MQQWdGjAIZAYBe++gjpPpzN4cjmz5EqVSyZyStGKZpPjZMHtTLQugUwXZfK42oGFh2qO6iSfyRDgc16rKyUv7P22V9OsnecZBPEW8XbokPAGBBjPWHWYT\
33291# 502znTY1vkmEIKjhS0hFZcYt9DYLUAEHEfF4PXpo/dkAstcxyFQ9Glx9Mz7+xmdbHtYBp09oWIi3d1pa2t3vCCJrq+cNwxEwHGkgDAEaWtOabOOrm3qg8\
33292# HjGtY9vR0dHe3tHP0Y8eSgiz2EGOGcACSCRTUNDgpxsRSJbT5FIhAXhmvjuXUb07VqOTOQpQzgznQidhAiPpgKogTzaoOexmTJXV1eOazuQwhEGELYP4E\
33293# ME23qByNrmPS9cuHj10qX8s23k558+c/1tT6DLgNCYhISECxcvXj1z+hTGaRwBPmPelJaihvaChG4JDEpLSt78JtjUHYHW+5KS0vaC03Uf13dHYJr34MH\
33294# 7Syh1dXX5aJSFCEdmDHa9Hp5hHH7P7RzCaQfiNqYbJ3C7dy0cE7Tn3PpuNJAXfi3c4zeBLK/LLeh6GF8rf+Dh2VGA8GaDXbxDD/QE5fWJMk5HZG5dHIUR\
33295# HvXh5d/ceMhvAuTcvC7Va5BGuIWXG2MRdKS3E9g1gQagMyLPLVGGdCzXhYBuAGk59G5fba9FALoL0WKLk6mDg4OprpYOEfgPPj8AH7GtomieWx8AAAAAS\
33296# UVORK5CYII=" />  <a href="https://gmic.eu/">https://gmic.eu</a>     and    
33297#@gui : <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA/CAMAAABnwz74AAADAFBMVEUAAAD8+/v77u/9+/v8/Pz47\
33298# vT25Of04+aYbXL03eD+/fzx2tz89/v99/n98PHDn6ns4OnQtLqshov88PP/8/T51NT46evz4evguLmlYWn56PDgwMK7govXp6u0YG/CjZWUd5Dt1tqTXG\
33299# WIWWLx0eLl0NrAcnnIeYDw4Onlxtf54e3z1+Xs0d/rzNvz5+704ev33ejt2ePdv87vt7ngqqyoconZpqSoUlpwPFndxdHgsbTGorHDm6zMg4XBbHPPaGq\
33300# jOUW0NUTxzN/jv9PSl7PMj46zYWmfTFv+/P7YvcjuwMLOsLzmvrq1i5/WjZe3Z22oRk6UQk359vf77/TuutbkuM/brsPQqLrXuLXMpbXrrbCxbYi3cHaq\
33301# WGOEQ2LDT1e8RU+YLDykLDqPHDD17/Lgs8nWqr70oKi+kqa0gpnKf43AdoWVXnvMc3qSU3DEYWTNXGKSQ1WbSVScP0uAJDj66fLrxt3sxNfw2dbqvtPdu\
33302# svfxcPWoLnKkqq+i57hhZHTfYSpZoK8enqsbnauXGv66ez93N7qzNbrycjJkZ6xeJLTiZHDiIm/gYSxeICjYXudWHfCYG+OUGl6QmB/N1a5T1WvQVCsMj\
33303# 9eHzz0x9/wwNr8zc/VtMPhnaPsmqHYlKHVnp27mZy9fJKYYoSld3yLWXmET3GEXGyZVFqGOU+PNki8g5zkkpvRlJiihI7Xh4yLaHqYb3nbbnFlMU2pP0l\
33304# xLUn44t7rttDKhpXhkZCaeoavZXSjZ22AS2iXX2ezU2GPTlq7HjH61unt1M/irMuMcoGfWm+bT2mlUWa5WF3psc7/q7fXm6yrfIuzhInfe4Sjb4LafHb9\
33305# 8vrm0MvgpMPBiafLn5+ieZW/kZGXcYueZoeQZG2OQ2OiXWKJRFR8Lz+cIjH5zuXhtNP9t7zSnqWUf46kaY17OkX91dj+xMGsjZTth5GFOT5RFjGGEybjx\
33306# sz7wMjks8H5r7HMrarJgqHjoJhvDinu4NrWjn7NgXOhO12uTFzKaYJvHzpyRElCABtwx6DiAAAAJHRSTlMA/pTZ66c8G5RtyJC8rw7PnpSQfSTMUfTllM\
33307# /Ev6+U7Ojg3lN8FIp+AAANFUlEQVRIx5SUaYhSURzFm4pWKlopiqB4+hz35ZW4jFuluTsjjUuZa2pOpqVONlZO5VYThZFKuLRQUUMRyCRtYBZUUmBT0AI\
33308# tQn0oilbaC7pWEEHrvV/el/fjnPP/nztgfhsRQ8KSiCRiwKYePGjU+ElDB/zXaWqyETEYEgaLJRIDAaLNZmtrGzV8yL9TmuY3qUlkFANFDpBI0WhUrY6+\
33309# DV4LnQtOHD5h6L8CsAQGSyyOM3BkHCkQjZbOyZDd8+btZiLPJw4fOvrvgKsBfBebynqWo1IZDBQeFUzI9CdOvHe7e32tBzfuZk4cPvqPGcy/GsB1saXSu\
33310# JjKQDGaUahn2tmv373r7+da6vxMqsoRoo/MZk4c+RtDg9vagIK4VEqNSxlUMZCASsgA4H0/l5vJ8DN8L9dT4xYKwkUb5s2ePHLCLwA2GwlHwOGwZBQFJQ\
33311# YaxN8BHq4lw+ebvEWvscjl1rj9bg6cP/Jg8tThE34GXFWTCRCOTMZRMJgoMUokBSXI6/fcfi/XwjfxTUVwU7Uat1blcAo1hwJ9Z7fk2Ywx44f8UIAld86\
33312# R5iAsNkAkkjCkaCyrf30COChmgAdjQ8FjT/fjWjfncPhxOBx2oyWlEFusDs2cNm4IADTWoNPudOp0bDEZhcJSsDEJonjXX+QWLXxwvF6jN9XvDnMO5/NK\
33313# pbs7XDs8O1R6FlNLOpLhg8MGDJ5vowjmtMvtTl2XLicVU6Xirk1bWtwej7cByFhMIIKiJ4k+qqfRnm84fCn5mE47F4xKEzHFY751CgA0BQhdGjkbAFgEq\
33314# pQq7tKs27qIrkoZjfV6I0RwTSn3iRZEwpQ87KGXU+H7D2OhZ4novEzFAgBt8wNQczOLpdGwdAxxjioW6zZtRQtTRpPRWgcCjOAjk3KvhPOIJMt83uFL1+\
33315# 4jDyQS3cNsvWKZDjJoImJw+LNnsTiIQkGh8GJqbh0AqFIpk9ViMaWNANHnKbsPL5qt1Say2TvJpGNDXu/qyDoqlToAgDpT8DgIwoJJYkkULBYbZx5xcar\
33316# VlNds5qV5fV6eiefhhIV3dstC50pBJp3j0AsdPc8vWSoNC4NAnYnYs805AoQnYzBqUgPw4Djs8XC5X/9PeXjpZBKNRu+SyELBUCmWF74s+OFHDiu/UvGP\
33317# HDCozRbFE3LyWXa2XNecyzFY1FUvNvXASo+nmE4DAM9hNnpVwpY8IpMltKVSKbbxudLfq79kqlTMtIEAACycXdW+dA1b49SwWFKpxr53I5qu6uNxLWZeX\
33318# 5/qcR9QAPeAemuvnwuGrgUfKI7eo8Xo9UolnAWApiY1WbC6/coap12+wM6ya+z2y4tgukpVNVqt5ow53e3pU3E2tMxDZt+UyWSht+C12CjLxgpWS12vBQ\
33319# AbhoKDBAvk7Ro5a06OlWPp2h9shQ1gEbwWqyVj4Z3wqHxHWza0HDmyezYTSVy7ppU8TJQeRqxWh+QcyICIAVNEQZ0QhIdwBDw+3ryaedylLFf7uDyruW5\
33320# WHe2mG2B40aKefB7Zpadd1wIXz2JZvzXtkES/KYDwBAIoJJglmQxqHX9xWdHidnP7eT4QAd3RQleiOUL46CmXS7HrCMIMXgvGafetfEf+YfxriBSwiasW\
33321# CATNAogAEORVLy73wG530cNT+T76Vr58Xz0BRzifPn24ePHQva3MxLVgjIl+DABbJHFgYf5VEkSw3zrgdN6yy+Xs9j063daNLoMqBSyYfb1ldMtr+lEDP\
33322# enzv+ldETn9PBE6Jzvd4Xj6NFM4gkgHAsB8NRlaNXd5+5qlc+V2uY5tX7vpVKRVxUtVn75pvWeAnxTovarkB/+HD2/8EdorbQg5H770lP/0koJ2Mza2AQ\
33323# hQCF26ZWvsm9cs1TjbWRrnplMdIrq5Wu31X1xpQCtfCn2f/G8+fOr1H9rFZO5JZA+G39SemmFaNqH9qiCKau7S7LfrbrCdJ9k5to51ZaNLtNLn8/haRfc\
33324# MPSsLSrT/jf/zvQuHjh+fvU8meSQqm3lW/0s9DQkNHDCiqY2EwxM6d67u7OyEGq8jBEJUuNAwp1vUGllhUHYbOi7cu33o0IUzrl278gjt0f1I2ZyuXtIj\
33325# 93ftGdkAEPGEuEAggCBCM+jk2bNzXjxQuODublgkuigyRJStC2/vWHzhwpmN+gJaqXikv6/ipY28S3n9S/SDsQCgjuLjccGSnau7Vgk6VwkIAqDgOAxGD\
33326# 9NFEZGho+PgofXHlm87cxc53PqkzDnhKFvNaevTw4oejgH5CiDh57Q7t69bN2udRi7XyZ1rNp8/taK17BP6ey9+6bzcQ5OKozgewaKo6AUVPSgoXfaedu\
33327# 1WGpHXrefKUltdE6KsTBO02rLUpkY15yuVUHv6dtlMEXM5Nam9sJRRura5LVa01Wj/1KA/osdPiCDoQX25f104n3M4v985v3OEL+wmsylsNptfsI6sP1l\
33328# 2bjWZfHnDgQcHPt24cbe3vhDBrt2LllH2qAw+lQxCfFzpUdlRe0uj6MSnnQ9OioTCPrYpDL7P4bo72PXV1Xewd28cP/jg8qcbd4rJVOKlQhLBRbLSJeyc\
33329# KteOSLmIzCepSbQIaZevXD5JE8pNsBEOGcPhOl4pDXvnRvWO44/PndhZLTqCvU5ddeEeAGwCr7uYI40jkIvLJG1fx+VyZO1Koaj003FaUmcwup8DAD+Uv\
33330# EnbX333/v0d6x/f9dbVJ2sJ+251fPg4HgAOYXAUirjK+vBhVWWVpb/fwmRKGzQ8HqgeHjcYaO6Co56gJx3zsooBgFX8+Hoi1qElrFq7dtvp06NHTQC1AB\
33331# 71h2cebsQt3Gixbux/WMUh1dgzGo1aq00Hml+/bg4EAtGhoFOrBlOHun5fR8Zms21ZW1FxoQJEMGF5oStTzjDFnL0cDukwh7SOeTQtcwJlnEE/sPe/9vv\
33332# 9tuiQf8hm6yASCQTb0Afb1rMVFR8enf4IIlhUgqEwSXx+OwRBKgjazk3L0u3pYDDvzAQBoLkQQrM/Gh2K+jOdnWrqrY50dN2QreLsow/3nm0GgJI1eA5T\
33333# KlDp5XYdxIW4XK5M1p4J5vNarTMf9bwONDd7SEFPwB90ao6do1LrO50AXbPtQoXt3rNHAHBo+eJKSc7gE0A+HZRu50KQ1KfTJhKxmFprb3c97wp0RbcHA\
33334# kP+fIxXfY5892bCmcgr6+s7nJltbyqmjpqwa/eSqqac0QipDEfXbV8n3rOHIZE22Btqy8sbUb4j8BxonScYzeeTCWwx9g5rUBvLJM8r1J2dV7d4CoBTJR\
33335# QGI5Wiiy1Vy0BPAx36MCJrsJcfq21EHS5Xt+P5c09B7eB2rS5dT1YMJnvPHyvX9N4i9MbGjpq4e8XCjRRGhG6JWMXi/v6N/RZ6BHlyE+WVFreajHA8Hne\
33336# 5XSEY9kAtwv2rsaW1VEJisJdXo1YSropYU0dNLMHgGJamVDabQp48gVTS7m7IwG6pqytrEwnDRiMMO9wudwiADJ+FIixZoSYStwwm9l3tUL+NiWrHjipa\
33337# tNTSn0XYbD1qQuWoQSVHdXp9S2udsIzWZmYbBaEQ7BowwiDFfWU77lA16lvEbQkl4RLx7Vsv9nwBsFCczY6YULbdgOpQvRyVy3UCua6v9XYbb3gAdoMQu\
33338# nNcd7evRVSGPULWqNW31mqUhLWxV69oJwBg4tNTliwyYrL3SA2o3MFX8XU+Pl9q1zXwykf0Pr6D73IP5LrdcYNQVFZaW8sqDJzEmFJDxr5/t5oFAIdWVk\
33339# YkqDksRxy5FJ0eSUXodElO+qQBdED9gANyGd3xeAg2ht+JRPt3lO8AI+stgtf7glb97t3+5FQAWNJPjzQ1NUWs1hTdQgfnEYk0IYj0ZtuxRrPAPGwOhcx\
33340# G2BwO006sXs9i7fD2Ulft03jf9ZG9ClpjIQcYPIPBsVotVrqVHrHSU1kJJLC3DJbdvl33VcBmmz3Dbjg0zAY/VpM1peRiBXUfgapOeoV9XuUXAFgB2gGD\
33341# wwH2OUQq0CNyA2pgG0yDdW2324b1PewQbObDIbiv7Hxp8X2qQqHRaIhE4pdEYs7YBYUBvgizhoITM6FheY/A1KrvsaMCVCDQ64BDYYs5zh9wG0EW2YY+0\
33342# N/J5RqFQtn5YebsaWN+DNyTMSVLlzFk8LC+VWfqkfeMjAhUbDYqTzbuP2YXOLoH+G6Xgxvvaz3fyFJmOifNHTdm1E8qOlWy9CKnCTHLe/QmvUkgkaoQnw\
33343# +paSg/dlMFzPl8h6OrK55Pz5s091cLHQAsXojjZEfCOj3bJ5VJrHQxg8OUPKkBSnc7XIFAV9f8SVOA299o8qIllS9TWUTVBOqxiiG2VlLObN2zh0SqsZH\
33344# 8Uf/2SX9cmAoAXGXlS3ANXnIYFMY1PIWCB9Pyta1bt66ZUfTd7V8AJRsrXxZEweHOnMFv3roGU7Ji1rQf297fAZilOByFcwaPW4MHtjOKpgPbf9HoRUtx\
33345# eDwes3TRLHC4wPZfVbTp6fIZRYVl+T81fdwY4Pb/9Q3i0ArjHjdFFQAAAABJRU5ErkJggg==" />  <a href="http://cimg.eu/">htt\
33346# p://cimg.eu</a></center>)
33347#@gui : note = note{"\n
33348#@gui : If you appreciate <b>G'MIC</b>, you are welcome to send us a nice postcard from your place, at:\n\n
33349#@gui : <small><samp>David Tschumperlé,\n Laboratoire GREYC (CNRS UMR 6072), Equipe Image,\n
33350#@gui : 6 Bd du Maréchal Juin,\n 14050 Caen Cedex / France.</samp></small>"}
33351#@gui : note = note{"Postcards senders automatically enter the <i>Friends Hall of Fame</i> :) !\n\
33352# You may also consider <a href="https://libreart.info/en/projects/gmic">making a donation</a>!"}
33353
33354#@gui Contributors : _none_, _none_
33355#@gui : note = note{"
33356#@gui : We would like to thank all these people who contributed to <b>G'MIC</b> in one way or another.
33357#@gui : A big hug to : \n\n
33358#@gui : <b> -</b> <i>Sylvie Alexandre</i> <small>(packaging, testing & filters)</small>
33359#@gui : <b> -</b> <i>Partha Bagchi</i> <small>(packaging)</small>
33360#@gui : <b> -</b> <i>Daniel P. Berrangé</i> <small>(packaging)</small>
33361#@gui : <b> -</b> <i>Sébastien Bougleux</i> <small>(debugging)</small>
33362#@gui : <b> -</b> <i>Jérome Boulanger</i> <small>(testing & code)</small>
33363#@gui : <b> -</b> <i>Claude Bulin</i> <small>(packaging)</small>
33364#@gui : <b> -</b> <i>Aurélien Ceyden</i> <small>(packaging)</small>
33365#@gui : <b> -</b> <i>François Collard</i> <small>(testing)</small>
33366#@gui : <b> -</b> <i>Patrick David</i> <small>(testing & filters)</small>
33367#@gui : <b> -</b> <i>Maxime Daisy</i> <small>(code & testing)</small>
33368#@gui : <b> -</b> <i>Frédéric Devernay</i> <small>(code)</small>
33369#@gui : <b> -</b> <i>Iain Fergusson</i> <small>(filters)</small>
33370#@gui : <b> -</b> <i>Tobias Fleischer</i> <small>(testing & code)</small>
33371#@gui : <b> -</b> <i>Roberto Ferramosca</i> <small>(packaging)</small>
33372#@gui : <b> -</b> <i>Jérome Ferrari</i> <small>(testing, code & tutorials)</small>
33373#@gui : <b> -</b> <i>Andrea Ferrero</i> <small>(testing, code)</small>
33374#@gui : <b> -</b> <i>Chris Fiedler</i> <small>(gfx)</small>
33375#@gui : <b> -</b> <i>Sébastien Fourey</i> <small>(G'MIC-Qt, ZArt code & G'MIC online)</small>
33376#@gui : <b> -</b> <i>Gentlemanbeggar</i> <small>(filters)</small>
33377#@gui : <b> -</b> <i>David Gowers</i> <small>(testing)</small>
33378#@gui : <b> -</b> <i>Claes Holmerson</i> <small>(tutorials)</small>
33379#@gui : <b> -</b> <i>Arto Huotari</i> <small>(filters)</small>
33380#@gui : <b> -</b> <i>Dan Leinir Turthra Jensen</i> <small>(debugging)</small>
33381#@gui : <b> -</b> <i>Tom Keil</i> <small>(testing, filters & tutorials)</small>
33382#@gui : <b> -</b> <i>Andy Kelday</i> <small>(testing & filters)</small>
33383#@gui : <b> -</b> <i>Alan Kwan</i> (afre) <small>(testing & filters)</small>
33384#@gui : <b> -</b> <i>Angelo Lama</i> <small>(testing & EKD integration)</small>
33385#@gui : <b> -</b> <i>John Lakkas</i> <small>(filters)</small>
33386#@gui : <b> -</b> <i>Stéphane de la Linuxerie</i> <small>(design)</small>
33387#@gui : <b> -</b> <i>Mark</i> <small>(translation)</small>
33388#@gui : <b> -</b> <i>Mahvin</i> <small>(testing & design)</small>
33389#@gui : <b> -</b> <i>MareroQ</i> <small>(translation)</small>
33390#@gui : <b> -</b> <i>Ramon Miranda</i> <small>(translation)</small>
33391#@gui : <b> -</b> <i>Tou Omiya</i> <small>(translation)</small>
33392#@gui : <b> -</b> <i>Mauro Quercia</i> <small>(translation)</small>
33393#@gui : <b> -</b> <i>PhotoComiX</i> <small>(testing, translation & filters)</small>
33394#@gui : <b> -</b> <i>Garry Osgood</i> <small>(documentation & filters)</small>
33395#@gui : <b> -</b> <i>Jehan Pages</i> <small>(testing & code)</small>
33396#@gui : <b> -</b> <i>Andreas Påhlsson</i> <small>(filters)</small>
33397#@gui : <b> -</b> <i>James Prichard</i> <small>(testing & filters)</small>
33398#@gui : <b> -</b> <i>Guilherme Razgriz</i> <small>(translation)</small>
33399#@gui : <b> -</b> <i>Karsten Rodenacker</i> <small>(packaging & code)</small>
33400#@gui : <b> -</b> <i>Marc Roovers</i> <small>(clut data)</small>
33401#@gui : <b> -</b> <i>Dani Sardà</i> <small>(translation)</small>
33402#@gui : <b> -</b> <i>Yuri Shemanin</i> <small>(debugging)</small>
33403#@gui : <b> -</b> <i>Silvio Grosso</i> <small>(debugging)</small>
33404#@gui : <b> -</b> <i>Stepanekos</i> <small>(translation)</small>
33405#@gui : <b> -</b> <i>Thorsten "otto" Stettin</i> <small>(packaging)</small>
33406#@gui : <b> -</b> <i>Lukas Tvrdy</i> <small>(Krita integration)</small>
33407#@gui : <b> -</b> <i>Martin Wolff</i> <small>(testing & filters)</small>
33408#@gui : <b> -</b> <i>Bernd Zeimetz</i> <small>(packaging)</small>
33409#@gui : <b> -</b> <i>Matthias Zepper</i> <small>(testing)</small>
33410#@gui : <b> -</b>"}
33411
33412#@gui Download External Data : gui_download_all_data, gui_no_preview(1)
33413#@gui : note = note{"This filter will download all external data files used by some filters of the <i>G'MIC</i>
33414#@gui : plug-in (<i>Color Grading</i>, <i>Light Leaks</i>, <i>Grain</i>, etc...),
33415#@gui : and will install them as persistent files on your hard drive. After this operation, you won't need a permanent
33416#@gui : internet connection anymore in order to use some of the G'MIC filters."}
33417#@gui : note = note()
33418#@gui : note = note{"<b><span color="#EE5500">Warning:</span></b> A <b>lot of data</b> will be downloaded.
33419#@gui : This can take a long time !"}
33420#@gui : sep = separator()
33421#@gui : Force re-Download from Scratch = _bool(0)
33422#@gui : sep = separator()
33423#@gui : note = note{"<b><span color="#EE5500">Alternative (manual) method:</span></b>\nIf, for any reasons,
33424#@gui : your plug-in is unable to retrieve data from the Internet, you can download all
33425#@gui : those data files manually (as a single .zip file) at this address :"}
33426#@gui : url = link{"https://gmic.eu/gmic_all_data.zip"}
33427#@gui : note = note{"You must then decompress all files contained in this archive at the following location:\n
33428#@gui : - for <b>Unix</b>-like systems : <span color="blue"><samp>$HOME/.cache/gmic/</samp></span>\n
33429#@gui : - for <b>Windows</b> systems : <span color="blue"><samp>%APPDATA/gmic/</samp></span>
33430#@gui : "}
33431#@gui : sep = separator()
33432#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/16/04</i>.</small>")
33433gui_download_all_data :
33434
33435  # Color Presets.
33436  l[] clut foo rm onfail rm endl
33437
33438  # Grain.
33439  _filenames_grain=${-_fx_simulate_grain}
33440  _url_grain=https://gmic.eu/data_film_presets
33441  _prefix_grain=grain_
33442  _ext_grain=cimgz
33443
33444  # Light leaks.
33445  _filenames_lightleak=${-_fx_light_leaks}
33446  _url_lightleak=https://gmic.eu/data_lightleaks
33447  _prefix_lightleak=
33448  _ext_lightleak=cimgz
33449
33450  # Sample images.
33451  _filenames_sample=${-__sample}
33452  _url_sample=https://gmic.eu/img
33453  _prefix_sample=sample_
33454  _ext_sample=png
33455
33456  # Demo thumbnails.
33457  _filenames_demos=gmic_demos
33458  _url_demos=https://gmic.eu/img
33459  _prefix_demos=
33460  _ext_demos=cimgz
33461
33462  # Manage downloads.
33463  _n=0
33464  _N={narg($_filenames_grain,$_filenames_lightleak,$_filenames_logo,$_filenames_sample)}
33465  progress 0
33466  _gui_download_all_data[] grain,$1
33467  _gui_download_all_data[] lightleak,$1
33468  _gui_download_all_data[] logo,$1
33469  _gui_download_all_data[] sample,$1
33470  _gui_download_all_data[] demos,$1
33471  progress 100
33472
33473_gui_download_all_data :
33474   repeat narg(${_filenames_$1})
33475     filename=${_prefix_$1}${arg\ 1+$>,${_filenames_$1}}.${_ext_$1}
33476     e[] "Download "$filename
33477     if $2" || "!isfile(['{/${-path_cache}$filename}']) l[]
33478       ${_url_$1}/$filename o ${-path_cache}$filename rm onfail
33479     endl fi
33480     progress {100*$_n/$_N}
33481     _n+=1
33482   done
33483
33484#@gui Filter Design : _none_, _none_
33485#@gui : note = note{"
33486#@gui : <b>G'MIC</b> is an <b>open</b> image processing framework. Thus, including
33487#@gui : <b>user-defined filters</b> into this plug-in is possible.\n\n
33488#@gui : To do so, you need to create a <span foreground="purple" style="italic">.gmic</span>
33489#@gui : file in your <i>$HOME/</i> folder (or <i>%APPDATA%/user.gmic</i> on Windows).
33490#@gui : It will be read each time the plug-in is launched, or when the <i>Refresh</i>
33491#@gui : button (under the central pane) is pressed. It must be a regular text file, containing the declarations and
33492#@gui : implementations of the filters (written in the <b>G'MIC</b> language) that will be added
33493#@gui : to the list of available ones."}
33494#@gui : note = note{"
33495#@gui : Existing filters are already defined this way.
33496#@gui : Writing a filter from scratch in <b>G'MIC</b> requires some skills, but
33497#@gui : can be generally done in very few lines.\n\n
33498#@gui : <span foreground="purple" underline="single">Example of a valid <i>.gmic</i> entry :</span>"}
33499#@gui : note = note{"<samp>#@gui My effect : my_effect, my_effect</samp>"}
33500#@gui : note = note{"<samp>#@gui : Sigma = float(2,0,10)</samp>"}
33501#@gui : note = note{"<samp>my_effect :\n     +blur $1 n 0,255 xor</samp>"}
33502#@gui : note = note{"
33503#@gui : Look at the reference documentation and the tutorial whose links are given below, to learn more.
33504#@gui : By the way, you are encouraged to share your nice custom filters with us on our forums,
33505#@gui : for inclusion into next releases of <b>G'MIC</b>.
33506#@gui : "}
33507#@gui : sep = separator()
33508#@gui : url = link(0,"[1] G'MIC reference documentation","https://gmic.eu/reference/")
33509#@gui : url = link(0,"[2] G'MIC scripting tutorial","https://gmic.eu/tutorial/index.shtml")
33510#@gui : url = link(0,"[3] G'MIC filter template","https://gmic.eu/template.gmic")
33511
33512#@gui Friends Hall of Fame : _none_, fx_friends
33513#@gui : note = note{"\n<span foreground="purple" underline="single">Supporters:</span>"}
33514#@gui : note = note{" <b>-</b> A big hug goes to these friends who supported the project:"}
33515#@gui : note = note{"<i>
33516#@gui : Christian Stenner,
33517#@gui : Daniel Balle,
33518#@gui : Matthias Fuchs,
33519#@gui : <a href="https://www.augustus5star.fr/">Alban Bourrat</a>,
33520#@gui : <a href="http://viewbug.com/photo/65310796">Elizabeth Hayman</a>,
33521#@gui : Nicolas Künzler,
33522#@gui : Mikael Wargh,
33523#@gui : Giovanni Bianchessi,
33524#@gui : Job van der Zwan,
33525#@gui : <a href="http://espitallier.net/">Laurent Espitallier</a>,
33526#@gui : Mark van der Grijp,
33527#@gui : Patrick Wauters,
33528#@gui : <a href="https://pocketvj.com">Marc-André Gasser</a>,
33529#@gui : <a href="http://www.flickr.com/photos/ssshupe/">Steven Shupe</a>,
33530#@gui : Mika Yrjölä,
33531#@gui : <a href="https://www.silviogrosso.com/">Silvio Grosso</a>,
33532#@gui : Marek Kubica,
33533#@gui : <a href="http://www.flickr.com/newmikey">Mike Bing</a>,
33534#@gui : <a href="http://pixby.com/">Dave Allen</a>,
33535#@gui : Margaret Wong,
33536#@gui : Adrian Bottomley,
33537#@gui : Pamela Young,
33538#@gui : <a href="http://chrisbowness.com/">Chris Bowness</a>,
33539#@gui : Peter Howarth,
33540#@gui : Marlon Montalvo,
33541#@gui : Christian Freiherr von Malchus,
33542#@gui : Nolan Tyrrell,
33543#@gui : Gilles Bouquerel,
33544#@gui : Mihail Balabanov,
33545#@gui : Rolf Niepraschk,
33546#@gui : Volkmar Geske,
33547#@gui : Menno Tjoelker,
33548#@gui : <a href="https://borkarabhijeet05.blogspot.com/p/about-me.html">Abhijeet Borkar</a>,
33549#@gui : <a href="https://www.behance.net/kontaktarl1e0a">Arleta Lesniewska</a>,
33550#@gui : Nicola Giaccobe,
33551#@gui : Helmut Mühleisen,
33552#@gui : Paul Buckley,
33553#@gui : Olivier Lecarme,
33554#@gui : Edward Ingram,
33555#@gui : <a href="http://www.gus-verlag.de/verlag/Artelier/">Stefan Städtler-Ley</a>,
33556#@gui : Michel Pastor,
33557#@gui : Sz.U,
33558#@gui : Sven Kraft,
33559#@gui : Frederik Elwert,
33560#@gui : Jessica Leonard,
33561#@gui : <a href="https://www.viewbug.com/member/KennZaney">Kenneth Simons</a>,
33562#@gui : <a href="https://www.flickr.com/photos/114936163@N05/">Milos Ciuk</a>,
33563#@gui : Manlio Barolo,
33564#@gui : John Lewandowski,
33565#@gui : <a href="http://mediaklan.com/">Didier Lima</a>,
33566#@gui : Žygimantas Tauras,
33567#@gui : Massimo Ferri,
33568#@gui : <a href="https://github.com/sina-ht">Hiroshi Takekawa</a>,
33569#@gui : Freelance writer,
33570#@gui : <a href="https://www.flickr.com/photos/49284009@N04/">Elaine Hutchings</a>,
33571#@gui : András Somogyi,
33572#@gui : <a href="https://www.flickr.com/photos/tonurics">Jason Dora</a>,
33573#@gui : Boris Hajdukovic,
33574#@gui : <a href="https://mappish.com/pages/about-us">Jeff Combs / Mappish</a>,
33575#@gui : <a href="http://flickr.com/photos/btraven">BTraven</a>,
33576#@gui : <a href="https://500px.com/spodeworld">Steven Brener</a>,
33577#@gui : Susanne Gabrielski,
33578#@gui : Andrea Correani,
33579#@gui : Mads Thomsen,
33580#@gui : Djek Eykhout,
33581#@gui : Michael Calabrese,
33582#@gui : Joachim Steiert
33583#@gui : Christian Dubettier,
33584#@gui : J. Casseur,
33585#@gui : <a href="http://www.gnomelibre.fr/">Okki</a>,
33586#@gui : Dariusz Duma,
33587#@gui : <a href="http://www.mahvin.com/">Mahvin</a>,
33588#@gui : Elleen Hennessy,
33589#@gui : BluffStuffPlus,
33590#@gui : <a href="http://www.bertrandchan.eu">Bertrand Chan</a>,
33591#@gui : Mirella Scotto,
33592#@gui : <a href="http://www.photopablo.com">Paul Sauve</a>,
33593#@gui : <a href="https://darktablemaster.de">Lars Mielke</a>,
33594#@gui : Devin Sorell,
33595#@gui : <a href="http://www.quesepuedehacerenlinux.net">Pepe Baeza</a>,
33596#@gui : <a href=" http://www.lesnoy-tanets.com">Andrey Pivovarova</a>,
33597#@gui : <a href="http://doliver.co.uk/">David Oliver</a>,
33598#@gui : <a href="https://ello.co/errore">errore</a>,
33599#@gui : <a href="http://www.anudai.de">Anudai</a>,
33600#@gui : James Stalnaker,
33601#@gui : <a href="https://plus.google.com/u/0/b/117441237982283011318/112547676857320288448/about">Paolo Finetti</a>,
33602#@gui : Luigi Scarselli,
33603#@gui : <a href="https://patdavid.net/">Pat David</a>,
33604#@gui : Juan Jose Rodriguez Vela,
33605#@gui : Thomas Jakob,
33606#@gui : Kim Bartholomew,
33607#@gui : <a href="http://www.captivemoment.com">Sudi</a>,
33608#@gui : Michael Prostka,
33609#@gui : Arkadi Gelfond,
33610#@gui : <a href="https://joeysl.wordpress.com/">Sabine Schäfers</a>,
33611#@gui : <a href="http://www.viewbug.com/member/KennZaney">Bull O'Woods</a>,
33612#@gui : Jost Jakob Schaper,
33613#@gui : Dominik Wefers,
33614#@gui : Frank McLaughlin,
33615#@gui : <a href="https://29a.ch/">Jonas Wagner</a>,
33616#@gui : <a href="www.ixaarii.com">Void lon iXaarii</a>,
33617#@gui : Mark Boadey,
33618#@gui : Laura Haglund,
33619#@gui : Lee Elliott,
33620#@gui : Bernard Desenclos,
33621#@gui : Randy Gordon-Gilmore,
33622#@gui : Eddie Dedrick,
33623#@gui : <a href="http://mindprints.org/">Greg FitzPatrick</a>,
33624#@gui : Zsolt Szabo,
33625#@gui : Daniel Hanna,
33626#@gui : Peter Bengtsson,
33627#@gui : Diego Nassetti,
33628#@gui : William Tweedy,
33629#@gui : Shawnee Horn,
33630#@gui : Stephan Munsch,
33631#@gui : <a href="http://www.mysticali3n-wear.com">MysticAli3n-Wear</a>,
33632#@gui : Mika Mantere,
33633#@gui : Christian Beuschel,
33634#@gui : Tore Busch,
33635#@gui : Douc McGregor.
33636#@gui : Marcel Dahm,
33637#@gui : Susan Voitel,
33638#@gui : <a href="https://www.flickr.com/photos/henkkoning">Henk Koning</a>,
33639#@gui : Arnie Jordan,
33640#@gui : Carol Jennings,
33641#@gui : Sébastien Huart,
33642#@gui : <a href="http://www.jessstryker.com/">Jess Stryker</a>,
33643#@gui : Rui Luis,
33644#@gui : <a href="https://www.flickr.com/photos/sallesrenato/">Renato Salles</a>,
33645#@gui : <a href="http://www.viewbug.com/member/alef0"> Petr Zagalak</a>,
33646#@gui : <a href="http://www.antonio.cat">Antonio Vicién Faure</a>,
33647#@gui : Vincent Bermel,
33648#@gui : Christian Stocco,
33649#@gui : <a href="https://www.flickr.com/photos/136307651@N04/">Richard Benedict</a>,
33650#@gui : Dr. Helmut Jarausch,
33651#@gui : <a href="http://www.michaeljamesbeck.com">Michael Beck</a>,
33652#@gui : <a href="http://rickleone.tumblr.com/">Riccardo Leone</a>,
33653#@gui : Gisela Looram,
33654#@gui : <a href="https://plus.google.com/u/0/+FrankTegtmeyer/posts">Frank Tegtmeyer</a>,
33655#@gui : David Kettrey,
33656#@gui : <a href="https://www.youtube.com/user/kncpt1">Peter Hoge</a>,
33657#@gui : Alexander Heitmann,
33658#@gui : <a href="http://harlequin.webcomics.fr/page/episode-1-page-1">Olivier Larski</a>,
33659#@gui : <a href="http://victorfandrey.blogspot.ca">Victor Fandrey</a>,
33660#@gui : Stefan Peter,
33661#@gui : <a href="https://plus.google.com/u/0/+DimitriosPsychogios">Dimitrios Psychogios</a>,
33662#@gui : <a href="https://plus.google.com/+AnttiLuoma">Antti Luoma</a>,
33663#@gui : <a href="https://twitter.com/jeyoung">Eddy Young Tie Yang</a>,
33664#@gui : Thomas Elfstrom,
33665#@gui : Valentine Boyce,
33666#@gui : George Harnett,
33667#@gui : Darius Manka,
33668#@gui : Chris Knox,
33669#@gui : <a href="http://tomtappingphotoblog.blogspot.fr/">Thomas Tapping</a>,
33670#@gui : Phillip R Ziesemer,
33671#@gui : Jean Francois.
33672#@gui : Franz Ziereis,
33673#@gui : Alessandro Renzi,
33674#@gui : Tsuda Koshi,
33675#@gui : <a href="http://www.boxrec.com">Boxrec Ltd</a>,
33676#@gui : <a href="http://www.wolfgangschweizer.com/">Wolfgang Schweizer</a>,
33677#@gui : <a href="http://www.ramonmiranda.com/">Ramon Miranda</a>,
33678#@gui : Volker Bradley,
33679#@gui : <a href="http://plus.google.com/+MarcoZara">Marco Zara</a>,
33680#@gui : <a href="http://plus.google.com/+MarcoTedaldi">Marco Tedaldi</a>,
33681#@gui : <a href="http://cybertographer.com">Rodney Lee</a>,
33682#@gui : Konstantinos Blatzonis,
33683#@gui : Simon Chanson,
33684#@gui : Herbert Malle,
33685#@gui : <a href="http://www.matthias-zepper.de/">Matthias Zepper</a>,
33686#@gui : Christian Mariucci,
33687#@gui : M. R.,
33688#@gui : Mark Link,
33689#@gui : <a href="http://blog.meetthegimp.org/">Rolf Steinort</a>,
33690#@gui : <a href="https://plus.google.com/112357088505488756823/posts">Daniel Tauro</a>,
33691#@gui : <a href="http://geniisoft.com/">Ben Langhinrichs</a>,
33692#@gui : <a href="http://www.openlabs.it/">Paolo Pedaletti</a>,
33693#@gui : <a href="http://blog.photomontager.com">Ricardo Corin</a>,
33694#@gui : <a href="https://plus.google.com/115953666279509959258">James Prichard</a>,
33695#@gui : <a href="https://plus.google.com/116658221461047313647">Matt Jones</a>,
33696#@gui : <a href="http://www.flickr.com/people/twekkel/">Eddy Vervest</a>,
33697#@gui : <a href="http://www.flaviocdc.net/wiki/">Flavio Casadei Della Chiesa</a>,
33698#@gui : <a href="http://www.artwanted.com/artist.cfm?artid=10918">Lyle Kroll</a>.
33699#@gui : </i>"}
33700#@gui : sep = separator()
33701#@gui : note = note{"\n<span foreground="purple" underline="single">Postcard senders:</span>"}
33702#@gui : note = note{" <b>-</b> We've received <b>46</b> postcards from <b>G'MIC</b> enthusiasts so far.
33703#@gui :               You could be the <b>47rd</b> sender :)"}
33704#@gui : note = note{" <b>-</b> A big hug goes to these postcard senders (recently received first) :"}
33705#@gui : note = note{"<i>
33706#@gui : <a href="https://cimg.eu/img/postcard73.jpg">Benjamin Russell</a> (Portsmouth/USA),
33707#@gui : <a href="https://cimg.eu/img/postcard72.jpg">Andreas Weissenburger</a> (Bochum/Germany),
33708#@gui : <a href="https://cimg.eu/img/postcard70.jpg">Patrick Wanters</a> (USA),
33709#@gui : <a href="https://cimg.eu/img/postcard69.jpg">Josep Febrer</a> (Pregonda/Menorca),
33710#@gui : <a href="https://cimg.eu/img/postcard68.jpg">Richard Gledson</a> (Newcastle upon tyne/England),
33711#@gui : <a href="https://cimg.eu/img/postcard67.jpg">James Jaworski</a> (Winnipeg/Canada),
33712#@gui : <a href="https://cimg.eu/img/postcard66.jpg">Powlux</a> (France),
33713#@gui : <a href="https://cimg.eu/img/postcard65.jpg">Volker Doebel</a> (Haldern/Germany),
33714#@gui : <a href="https://cimg.eu/img/postcard64.jpg">Patrick Wauters</a> (Bilbao/Spain),
33715#@gui : <a href="https://cimg.eu/img/postcard63.jpg">Sebastien Fourey</a> (Konstanz/Germany),
33716#@gui : <a href="https://cimg.eu/img/postcard62.jpg">David Revoy</a> (Toulouse/France),
33717#@gui : <a href="https://cimg.eu/img/postcard61.jpg">Giulio Canevari</a> (Pavia/Italy),
33718#@gui : <a href="https://cimg.eu/img/postcard60.jpg">Bruno Steinbach</a> (Pondicherry/India),
33719#@gui : <a href="https://cimg.eu/img/postcard59.jpg">Steve Gillow</a> (Fort Worth/Texas/USA),
33720#@gui : <a href="https://cimg.eu/img/postcard58.jpg">Peter Neave</a> (Sydney/Australia),
33721#@gui : <a href="https://cimg.eu/img/postcard57.jpg">Andrea [Photoflow]</a> (Italy),
33722#@gui : <a href="https://cimg.eu/img/postcard56.jpg">Garry R. Osgood</a> (New York/USA),
33723#@gui : <a href="https://cimg.eu/img/postcard55.jpg">Justin Pletzfeld</a> (Germany),
33724#@gui : <a href="https://cimg.eu/img/postcard54.jpg">Werner Meier</a> (Germany),
33725#@gui : <a href="https://cimg.eu/img/postcard53.jpg">Patrick Wauters</a> (Roma/Italy),
33726#@gui : <a href="https://cimg.eu/img/postcard52.jpg">Marc Lis</a> (Belgium),
33727#@gui : <a href="https://cimg.eu/img/postcard51.jpg">ZondeR</a> (France),
33728#@gui : <a href="https://cimg.eu/img/postcard50.jpg">Bill C.</a> (USA),
33729#@gui : <a href="https://cimg.eu/img/postcard49.jpg">Michael T.</a> (France),
33730#@gui : <a href="https://cimg.eu/img/postcard48.jpg">Patrick Wauters</a> (Lisboa),
33731#@gui : <a href="https://cimg.eu/img/postcard47.jpg">Akky [Gimpchat]</a> (Australia),
33732#@gui : <a href="https://cimg.eu/img/postcard45.jpg">Michel Thomas</a> (Germany),
33733#@gui : <a href="https://cimg.eu/img/postcard44.jpg">Pierre-Yves</a> (Ile de Batz/France),
33734#@gui : <a href="https://cimg.eu/img/postcard43.jpg">Family Hamacher</a> (Trier/Germany),
33735#@gui : <a href="https://cimg.eu/img/postcard41.jpg">Benoit Gauzere and Francois Lozes</a> (Hokusai/Japan),
33736#@gui : <a href="https://cimg.eu/img/postcard40.jpg">Dr. Rainer Teubner</a> (Seligenstadt/Germany),
33737#@gui : <a href="https://cimg.eu/img/postcard39.jpg">Mauro Mitrino</a> (Mantova/Italy),
33738#@gui : <a href="https://cimg.eu/img/postcard37.jpg">Werner Meier</a> (Mettlach/Germany),
33739#@gui : <a href="https://cimg.eu/img/postcard36.jpg">Arto Huotari</a> (Helsinki/Finland),
33740#@gui : <a href="https://cimg.eu/img/postcard33.jpg">Benoit Gauzere</a> (California/USA),
33741#@gui : <a href="https://cimg.eu/img/postcard30.jpg">Arkadi Gelfond</a> (Foster City - California/USA),
33742#@gui : <a href="https://cimg.eu/img/postcard29.jpg">Corinne Masimann</a> (Neuchatel/Switzerland),
33743#@gui : <a href="https://cimg.eu/img/postcard27.jpg">Mahvin</a> (Portland/USA),
33744#@gui : <a href="https://cimg.eu/img/postcard26.jpg">Vincent Roullier</a> (Caen/France),
33745#@gui : <a href="https://cimg.eu/img/postcard24.jpg">M????</a> (Munich/Germany),
33746#@gui : <a href="https://cimg.eu/img/postcard23.jpg">F. Albior</a> (Jaca/Spain),
33747#@gui : <a href="https://cimg.eu/img/postcard22.jpg">PhotoComIX</a> (Frascati/Italy),
33748#@gui : <a href="https://cimg.eu/img/postcard21.jpg">Guy Poizat</a> (Cabestany/France),
33749#@gui : <a href="https://cimg.eu/img/postcard20.jpg">Institut for Biomathematik und Biometrie</a> (Neuherberg/Germany),
33750#@gui : <a href="https://cimg.eu/img/postcard15.jpg">Jean-Michel Webbe</a> (Guadeloupe/France),
33751#@gui : <a href="https://cimg.eu/img/postcard14.jpg">Jaime</a> (Barcelona/Spain).
33752#@gui : </i>"}
33753#@gui : sep = separator()
33754#@gui : note = note{"\nMay the force be with you!"}
33755fx_friends :
33756  if $! ratio={w/h} else ratio=1 fi
33757  rm _heart80x73 scale3x r 150%,150%,1,1,0,0,0.5,0.5
33758  +*. 70 +*.. 110 +*... 255 *[-4] 255 a c
33759  blur_radial 4 sharpen 300
33760  i.. ${fitratio_wh\ {w},{h},$ratio},1,3
33761  rand.. 0,255 sh.. 1,2 /. 2 rm.
33762  blur_radial.. 20 sharpen.. 50
33763  r. ..,..,1,4,0,0,0.5,0.5 blend alpha
33764  143,80,1,1,0 t. "Greetings to\n  all G\47MIC\n  friends!",2,-2,27,1,1
33765  +dilate. 3 *.. 255 to_rgb.. j... ..,{[w#-3-w#-2,h#-3-h#-2]/2},0,0,1,.
33766  rm[-2,-1]
33767
33768_heart80x73 :
33769  40,73,1,1,0 ellipse 22,22,20,20,0,1,1 polygon 3,7,37,42,72,42,27,1,1 +mirror x a x
33770
33771#@gui Gmicky - Roddy : fx_gmicky, fx_gmicky_preview
33772#@gui : Mascot Image = choice{"Gmicky (by Deevad)","Gmicky (by Mahvin)","Gmicky & Wilber (by Mahvin)",
33773#@gui : "Roddy (by Mahvin)"}
33774#@gui : sep = separator()
33775#@gui : note = note{"<b><i>Gmicky</i></b> is the name of the <b>G'MIC</b> mascot.
33776#@gui : He is a small and cute tiger who knows how to do magic.
33777#@gui : <b><i>Gmicky</i></b> is a tiger, i.e. fast, agile and elegant, just as the <b>G'MIC</b> code is :).
33778#@gui : As many magicians, <b><i>Gmicky</i></b> knows lot of <b>gimmicks</b>,
33779#@gui : and he is a direct and friendly companion of
33780#@gui : the ImageMagick's wizard, or the GraphicMagick's frog."}
33781#@gui : note = note{"<b><i>Roddy</i></b> is another mascot designed specifically for the
33782#@gui : <i>Artistic / Rodilius</i> filter of <b>G'MIC</b>.\n"}
33783#@gui : note = note{"<b><i>Gmicky</i></b> and <b><i>Roddy</i></b> have been both created and drawn by "}
33784#@gui : url = link("Mahvelous Mahvin","http://www.mahvin.com/")
33785#@gui : note = note{"and"}
33786#@gui : url = link{"David Revoy (Deevad)","http://www.davidrevoy.com/"}
33787fx_gmicky :
33788  rm
33789  if $1==0 sp gmicky nm "name(Gmicky)"
33790  elif $1==1 sp gmicky_mahvin nm "name(Gmicky)"
33791  elif $1==2 sp gmicky_wilber nm "name(Gmicky & Wilber)"
33792  else sp roddy nm "name(Roddy)"
33793  fi
33794
33795fx_gmicky_preview :
33796  fx_gmicky $* rr2d $_preview_width,$_preview_height,0,2
33797
33798#@gui Privacy Notice : _none_, _none_
33799#@gui : note = note{"This plugin may download up-to-date filter definitions from the
33800#@gui : <a href="https://gmic.eu">gmic.eu</a> server.\n\n
33801#@gui : It is the case when first launched after a fresh installation, and periodically
33802#@gui : with a frequency which can be set in the settings dialog.
33803#@gui : The user should be aware that the following information may be retrieved
33804#@gui : from the server logs: <i>IP address of the client; date and time of the request;</i>
33805#@gui : as well as a short string, supplied through the HTTP protocol <i>"User Agent"</i> header
33806#@gui : field, which describes the full plugin version as shown in the window title
33807#@gui : (e.g. "<i>G'MIC-Qt for GIMP 2.8 - Linux 64 bits - 2.2.1_pre#180301</i>").\n\n
33808#@gui : Note that this information may solely be used for purely anonymous
33809#@gui : statistical purposes.
33810#@gui : "}
33811#@gui : sep = separator()
33812#@gui : note = note("<small>Author: <i>Sébastien Fourey</i>.      Latest Update: <i>2018/03/01</i>.</small>")
33813
33814#@gui Release Notes : _none_, _none_
33815#@gui : note = note{"
33816#@gui : - <b>2009/01/13</b> : version <i>1.3.0</i> (initial plug-in release).\n
33817#@gui : - <b>2010/09/03</b> : version <i>1.4.0</i>.\n
33818#@gui : - <b>2011/07/07</b> : version <i>1.5.0</i>.\n
33819#@gui : - <b>2014/08/20</b> : version <i>1.6.0</i>.\n
33820#@gui : - <b>2016/03/25</b> : version <i>1.7.0</i>.\n
33821#@gui : - <b>2017/05/29</b> : version <i>2.0.0</i>.\n
33822#@gui : - <b>2017/10/09</b> : version <i>2.1.0</i>.\n
33823#@gui : - <b>2018/02/15</b> : version <i>2.2.0</i>.\n
33824#@gui : - <b>2018/06/21</b> : version <i>2.3.0</i>.\n
33825#@gui : - <b>2018/10/04</b> : version <i>2.4.0</i>.\n
33826#@gui : - <b>2019/03/15</b> : version <i>2.5.0</i>.\n
33827#@gui : - <b>2019/04/29</b> : version <i>2.6.0</i>.\n
33828#@gui : - <b>2019/08/14</b> : version <i>2.7.0</i>.\n
33829#@gui : - <b>2019/12/04</b> : version <i>2.8.0</i>.\n
33830#@gui : - <b>2020/03/28</b> : version <i>2.9.0</i>.\n
33831#@gui : - <span foreground="purple"><b>2021/07/16</b> : version <i>2.9.8</i> (Current stable).</span>\n
33832##@gui : - <b>2021/07/16</b> : version <i>2.9.8</i> (Current pre-release).\n
33833#@gui : "}
33834#@gui : sep = separator()
33835#@gui : url = link{"View changelog to upcoming major version (3.0)","https://discuss.pixls.us/t/on-the-road-to-3-0"}
33836#@gui : url = link{"View latest minor changelog (2.9)","https://discuss.pixls.us/t/release-of-gmic-2-9"}
33837#@gui : url = link{"View latest major changelog (2.0)","https://discuss.pixls.us/t/release-of-gmic-2-0-0"}
33838
33839#@gui What's New? : _none_,fx_whatsnew_preview
33840#@gui : note = note("Here you'll find a list of filter additions and deletions in the plug-in, since your last visit.
33841#@gui : When you have seen what's new, press the <b>Got It!</b> button to reset the list of changes.")
33842#@gui : sep = separator()
33843#@gui : nochange = value(0)_0+
33844#@gui : note = note("\nThere have been no changes since your last visit.\n\n")
33845#@gui : New Filters = text(1,"")_0
33846#@gui : Removed Filters = text(1,"")_0
33847#@gui : Got it! = button(0.5)_0+
33848#@gui : sep = separator()
33849#@gui : note = note("<small>Authors: <i>David Tschumperlé</i>.
33850#@gui :       Latest Update: <i>2021/01/18</i>.</small>")
33851fx_whatsnew_preview : skip "$*"
33852  file=${-path_cache}whatsnew.txt
33853  l[]
33854    parse_gui whatsnew
33855    if isfile(['{/$file}']) it $file date={date([0,1,2],['{/$file}']):/} else 0 date={date([0,1,2]):/} fi
33856    utf82html.. utf82html.
33857    if $4 ot.. $file newfilters,delfilters= is_changes=0
33858    else
33859
33860      # Check for changes.
33861      newfilters,delfilters=
33862      +- is_changes={max(abs(im),abs(iM))!=0} rm.
33863
33864      if !w # First call to the filter
33865        rm. s -,10
33866        repeat $! newfilters.=" - "{$>,t}{`10`} done
33867
33868      elif $is_changes # Estimate changes
33869        eval "
33870          ref(vector256(),str0);
33871          ref(vector256(),str1);
33872          for (s0 = s1 = 0, s0<h#0 && s1<h#1,
33873            e0 = find(#0,_'\n',s0)%h#0;
33874            e1 = find(#1,_'\n',s1)%h#1;
33875            str0 = 0; copy(str0,i[#0,s0],e0 - s0);
33876            str1 = 0; copy(str1,i[#1,s1],e1 - s1);
33877            ref(lowercase(str0) - lowercase(str1),diff);
33878            for (l = 0, l<size(diff) && !diff[l], ++l);
33879            cmp = diff[l];
33880            !cmp?( # Same strings
33881              s0 = e0 + 1;
33882              s1 = e1 + 1;
33883            ):cmp<0?( # Added filter
33884              run('(\'\"',str0,'\"\') html2utf8. newfilters.=\" - \"{t}{`10`} rm.');
33885              s0 = e0 + 1;
33886            ):( # Deleted filter
33887              run('(\'\"',str1,'\"\') html2utf8. delfilters.=\" - \"{t}{`10`} rm.');
33888              s1 = e1 + 1;
33889            );
33890          )"
33891      fi
33892    fi
33893    rm
33894  endl
33895  u "{0}"_{$is_changes?0:2}\
33896    "{"{``$newfilters}"}"_{['$newfilters']!=0?2:0}\
33897    "{"{``$delfilters}"}"_{['$delfilters']!=0?2:0}\
33898    "{0}_2"
33899
33900#@gui ____<b>Arrays & Tiles</b>
33901#-------------------------------
33902
33903#@gui Array [Faded] : fx_array_fade, fx_array_fade_preview(1)
33904#@gui : X-Tiles = int(2,1,10)
33905#@gui : Y-Tiles = int(2,1,10)
33906#@gui : X-Offset (%) = float(0,0,100)
33907#@gui : Y-Offset (%) = float(0,0,100)
33908#@gui : Fade Start (%) = float(80,1,100)
33909#@gui : Fade End (%) = float(90,1,100)
33910#@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes")
33911#@gui : Size = _choice("Shrink", "Expand", "Repeat [Memory Consuming!]")
33912#@gui : sep = separator()
33913#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
33914fx_array_fade :
33915  if $7&1 mirror x fi if $7>1 mirror y fi
33916  array_fade $1,$2,$5,$6,$8
33917  shift -$3%,-$4%,0,0,2
33918
33919fx_array_fade_preview :
33920  fx_array_fade $1,$2,$3,$4,$5,$6,$7,0
33921
33922#@gui Array [Mirrored] : fx_array_mirror, fx_array_mirror_preview(1)
33923#@gui : Iterations = int(1,1,10)
33924#@gui : X-Offset (%) = float(0,0,100)
33925#@gui : Y-Offset (%) = float(0,0,100)
33926#@gui : Array Mode = choice(2,"X-Axis","Y-Axis","XY-Axes","2XY-Axes")
33927#@gui : Initialization = choice("Original","Mirror X","Mirror Y","Rotate 90 deg.","Rotate 180 deg.","Rotate 270 deg.")
33928#@gui : Expand Size = _bool(false)
33929#@gui : Crop (%) = int(0,0,100)
33930#@gui : sep = separator()
33931#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
33932fx_array_mirror : skip ${7=0}
33933  if $5==1 mirror x
33934  elif $5==2 mirror y
33935  elif $5==3 rotate 90
33936  elif $5==4 rotate 180
33937  elif $5==5 rotate 270
33938  fi
33939  if $7
33940    if $4==0 columns 0,{100-$7}%
33941    elif $4==1 rows 0,{100-$7}%
33942    elif $4==2 z 0,0,{100-$7}%,{100-$7}%
33943    elif $4==3 z {$7/2}%,{$7/2}%,{100-$7/2}%,{100-$7/2}%
33944    fi
33945  fi
33946  shift -$2%,-$3%,0,0,2
33947  array_mirror $1,$4,$6
33948
33949fx_array_mirror_preview :
33950  fx_array_mirror $1,$2,$3,$4,$5,0,$7
33951
33952#@gui Array [Random] : array_random, array_random(1)
33953#@gui : Source X-Tiles = int(5,1,20)
33954#@gui : Source Y-Tiles = int(5,1,20)
33955#@gui : Destination X-Tiles = int(7,1,20)
33956#@gui : Destination Y-Tiles = int(7,1,20)
33957#@gui : sep = separator()
33958#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
33959
33960#@gui Array [Random Colors] : fx_array_color, fx_array_color(1)
33961#@gui : X-Tiles = int(5,1,20)
33962#@gui : Y-Tiles = int(5,1,20)
33963#@gui : Opacity = float(0.5,0,1)
33964#@gui : sep = separator()
33965#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
33966fx_array_color :
33967  repeat $! l.
33968    $1,$2,1,3 rand. 0,255 to_colormode. {-2,s} ri. .. *. $3 *.. {1-$3} +[-2,-1]
33969  endl mv. 0 done
33970
33971#@gui Array [Regular] : fx_array, fx_array_preview(1)
33972#@gui : X-Tiles = int(2,1,10)
33973#@gui : Y-Tiles = int(2,1,10)
33974#@gui : X-Offset (%) = float(0,0,100)
33975#@gui : Y-Offset (%) = float(0,0,100)
33976#@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes")
33977#@gui : Size = _choice("Shrink", "Expand", "Repeat [Memory Consuming!]")
33978#@gui : sep = separator()
33979#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
33980fx_array :
33981  shift -$3%,-$4%,0,0,2
33982  if $5&1 mirror x fi if $5>1 mirror y fi
33983  array $1,$2,$6
33984
33985fx_array_preview :
33986  fx_array $1,$2,$3,$4,$5,0
33987
33988#@gui Ascii Art : fx_asciiart, fx_asciiart_preview(0)+
33989#@gui : Charset = choice(5,"Custom","Binary Digits","Digits","Lowercase Letters","Uppercase Letters",
33990#@gui : "Ascii","Card Suits","Math Symbols")
33991#@gui : Custom Dictionary = text{" .oO0"}
33992#@gui : Analysis Scale = int(16,8,103)
33993#@gui : Analysis Smoothness = float(15,0,100)
33994#@gui : Synthesis Scale = int(16,8,103)
33995#@gui : Result Type = choice(2,"White on Black","Black on White","Colored on Black","Colored on Transparent")
33996#@gui : sep = separator()
33997#@gui : Gamma = float(0,-3,3)
33998#@gui : Smoothness = float(0.2,0,5)
33999#@gui : Colors = choice("Full Colors","2 Colors","3 Colors","4 Colors","8 Colors","12 Colors","16 Colors",
34000#@gui :                 "Grayscale","2 Grays","3 Grays","4 Grays","8 Grays","12 Grays","16 Grays")
34001#@gui : sep = separator()
34002#@gui : Output Ascii File = _bool(0)
34003#@gui : Output Folder = _folder()
34004#@gui : Output Filename = _text("gmic_asciiart.txt")
34005#@gui : sep = separator()
34006#@gui : url = link("Click here for a detailed description of this filter.",\
34007# "http://www.gimpchat.com/viewtopic.php?f=28&t=10047")
34008#@gui : sep = separator()
34009#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/27/03</i>.</small>")
34010fx_asciiart : skip "${10=},${11=},${12=}"
34011  repeat $! l[$>] to_rgb
34012    apply_gamma {10^$7} b $8% n 0,255
34013    if $1==0 dict="$2"
34014    elif $1==1
34015      dict=" 01"
34016    elif $1==2
34017      dict=" 0123456789"
34018    elif $1==3
34019      dict=" abcdefghijklmnopqrstuvwxyz"
34020    elif $1==4
34021      dict=" ABCDEFGHIJKLMNOPQRSTUVWXYZ"
34022    elif $1==5
34023      dict=" !\042#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\133\\\135^_\140abcdefghijklmnopqrstu"\
34024           "vwxyz\173|\174~"
34025    elif $1==6
34026      dict=" \16\17\20\21"
34027    elif $1==7
34028      dict=" \200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231"
34029    fi
34030    if $6==1 negate fi
34031    if $-3 +img2ascii $dict,$3,$4%,$5,"$-2"/"$-1"
34032    else +img2ascii $dict,$3,$4%,$5
34033    fi
34034    wh=${}
34035    if $6==0 k. n 0,255
34036    elif $6==1 k. negate n 0,255
34037    elif $6==2" || "$6==3
34038      r[0] $wh,1,100%,1
34039      if $9>=7 luminance[0] fi
34040      if $9%7 quantize[0] {arg($9%7,2,3,4,8,12,16)-1},1,0 fi
34041      r[0] [1],[1],1,100% *[0] [1]
34042      if $6==2 rm[1]
34043      else *[1] 255 a c
34044      fi
34045    fi
34046  endl done
34047
34048fx_asciiart_preview :
34049  repeat $! l[$>]
34050    w={w} h={h}
34051    fx_asciiart $1,"$2",${3-9},0,foo,foo
34052    r $w,$h,1,100%,0,0,0.5,0.5
34053  endl done
34054
34055#@gui Chessboard : fx_chessboard, fx_chessboard_preview(0)
34056#@gui : First Size = int(64,1,512)
34057#@gui : Second Size = int(64,1,512)
34058#@gui : First Offset = int(0,0,512)
34059#@gui : Second Offset = int(0,0,512)
34060#@gui : Angle = float(0,0,180)
34061#@gui : Opacity = float(0.5,0,1)
34062#@gui : First Color = color(0,0,0,255)
34063#@gui : Second Color = color(255,255,255,255)
34064#@gui : sep = separator()
34065#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
34066#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
34067#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
34068#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
34069#@gui : sep = separator()
34070#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
34071fx_chessboard :
34072  to_rgba chessboard ${1-14}
34073
34074fx_chessboard_preview :
34075  gui_split_preview "fx_chessboard $*",${-3--1}
34076
34077#@gui Dices : fx_dices, fx_dices(0)
34078#@gui : Resolution = float(2,1,10)
34079#@gui : Size = int(24,8,64)
34080#@gui : Color Model = choice(1,"Black Dices","White Dices","Dices with Colored Numbers","Dices with Colored Sides")
34081#@gui : sep = separator()
34082#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/06</i>.</small>")
34083fx_dices :
34084
34085    # Create dice patterns.
34086    repeat 6 {2*$2},{2*$2} _dice$> done
34087    if $3%2 negate[-6--1] fi
34088    frame_round[-6--1] 10,10,0,0,128,128,128,0
34089    r2dy[-6--1] $2 a[-6--1] x
34090
34091    repeat $!-1 l[$>,-1]
34092
34093      # Prepare input images.
34094      +luminance[0] rv[1,2] r[0,1] {100*$1/$2}%,{100*$1/$2}%,1,100%,2 quantize[1] 6,0
34095
34096      # Convert input image to dices pattern.
34097      *.. $2 channels.. 0,1 r.. {$2*100}%,{$2*100}%
34098      $2,$2,1,2,'if(c,y,x)' r. ...,...,1,2,0,2 +[-3,-1] +warp. ..,0,0 rm...
34099
34100      if $3<2 rm[0] mv. 0
34101      else r[0] [2],[2],1,100% rv[0,-1] blend[0,-1] multiply
34102      fi
34103
34104    endl done rm.
34105
34106_dice0 : ellipse. 50%,50%,5.2%,5.2%,0,1,255
34107_dice1 : ellipse. 25%,25%,5.2%,5.2%,0,1,255 ellipse. 75%,75%,5.2%,5.2%,0,1,255
34108_dice2 : _dice1 _dice0
34109_dice3 : _dice1 ellipse. 25%,75%,5.2%,5.2%,0,1,255 ellipse. 75%,25%,5.2%,5.2%,0,1,255
34110_dice4 : _dice3 _dice0
34111_dice5 : _dice3 ellipse. 25%,50%,5.2%,5.2%,0,1,255 ellipse. 75%,50%,5.2%,5.2%,0,1,255
34112
34113#@gui Drawn Montage : fx_drawn_montage, fx_drawn_montage_preview(1) : *
34114#@gui : Layer = choice("1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th","11th","12th",
34115#@gui : "13th","14th","15th","16th")
34116#@gui : Associated Color = color(0,0,0)
34117#@gui : Zoom = float(-10,0,10)
34118#@gui : X-Centering (%) = float(50,0,100)
34119#@gui : Y-Centering (%) = float(50,0,100)
34120#@gui : Angle = choice("0 deg.","90 deg.","180 deg.","270 deg.")
34121#@gui : Pargs = value(-1)
34122#@gui : Args0 = value(0:0:0:0:50:50:0)
34123#@gui : Args1 = value(0:0:0:0:50:50:0)
34124#@gui : Args2 = value(0:0:0:0:50:50:0)
34125#@gui : Args3 = value(0:0:0:0:50:50:0)
34126#@gui : Args4 = value(0:0:0:0:50:50:0)
34127#@gui : Args5 = value(0:0:0:0:50:50:0)
34128#@gui : Args6 = value(0:0:0:0:50:50:0)
34129#@gui : Args7 = value(0:0:0:0:50:50:0)
34130#@gui : Args8 = value(0:0:0:0:50:50:0)
34131#@gui : Args9 = value(0:0:0:0:50:50:0)
34132#@gui : Args10 = value(0:0:0:0:50:50:0)
34133#@gui : Args11 = value(0:0:0:0:50:50:0)
34134#@gui : Args12 = value(0:0:0:0:50:50:0)
34135#@gui : Args13 = value(0:0:0:0:50:50:0)
34136#@gui : Args14 = value(0:0:0:0:50:50:0)
34137#@gui : Args15 = value(0:0:0:0:50:50:0)
34138#@gui : sep = separator()
34139#@gui : note = note{"<small><b>Note:</b>
34140#@gui : This filter requires a top layer containing the desired montage layout defined as free-form shapes
34141#@gui : of different colors. You can then assign each layer to a layout color to create the montage.
34142#@gui : </small>"}
34143#@gui : sep = separator()
34144#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/29</i>.</small>")
34145_fx_drawn_montage :
34146  nb_layers={min(16,$!-1)}
34147
34148  if !$nb_layers
34149    error="A top layout layer is missing for this filter to make it work properly!"
34150    if $-1 gui_warning_preview $error return
34151    else error $error
34152    fi
34153  fi
34154
34155  # Manage parameter changes.
34156  pargs,args0,args1,args2,args3,args4,args5,args6,args7,args8,args9,args10,args11,args12,args13,args14,args15=${9-25}
34157  if $1!=$pargs # Get back saved parameters for selected layer
34158    ('${args$1}') replace. {':'},{','} arg_R,arg_G,arg_B,arg_zoom,arg_xcenter,arg_ycenter,arg_angle=${u\ {t}} rm.
34159  else # Set new parameters for selected #L
34160    arg_R,arg_G,arg_B,arg_zoom,arg_xcenter,arg_ycenter,arg_angle=${2-8}
34161  fi
34162  args$1=$arg_R:$arg_G:$arg_B:$arg_zoom:$arg_xcenter:$arg_ycenter:$arg_angle
34163  status=\{$1\}\{$arg_R,$arg_G,$arg_B\}\{$arg_zoom\}\{$arg_xcenter\}\{$arg_ycenter\}\{$arg_angle\}\{$1\}\{$args0\}\
34164         \{$args1\}\{$args2\}\{$args3\}\{$args4\}\{$args5\}\{$args6\}\{$args7\}\{$args8\}\{$args9\}\{$args10\}\
34165         \{$args11\}\{$args12\}\{$args13\}\{$args14\}\{$args15\}
34166
34167  # Read parameters for all layers.
34168  c= repeat $nb_layers
34169    ('${args$>}') replace. {':'},{','} R$>,G$>,B$>,zoom$>,xcenter$>,ycenter$>,angle$>=${u\ {t}}
34170    cols=$cols$c${R$>},${G$>},${B$>} c=, rm. rotate[{1+$>}] {90*${angle$>}}
34171  done
34172
34173  # Get bounding boxes for each associated color in layout layer, as well as labeled layout layer.
34174  to_rgb[0] to_rgba[^0] $nb_layers,1,1,4,[inf,inf,-inf,-inf] [0],[0]
34175  f[0] "
34176    begin(colors = [ "$cols" ]);
34177    repeat ("$nb_layers",l,
34178      I==colors[3*l,3]?(
34179        if (x<i(#-2,l,0,0,0), i(#-2,l,0,0,0) = x);
34180        if (y<i(#-2,l,0,0,1), i(#-2,l,0,0,1) = y);
34181        if (x>i(#-2,l,0,0,2), i(#-2,l,0,0,2) = x);
34182        if (y>i(#-2,l,0,0,3), i(#-2,l,0,0,3) = y);
34183        i(#-1,x,y) = l + 1;
34184      );
34185    ); I"
34186
34187  thumbnail=0 boundaries=0
34188  if $-1
34189
34190    # Render thumbnail of current layer, for preview.
34191    l={1+$1}
34192    if $l<=$nb_layers
34193      if {$l,w>h} +r2dx[$l] {(0$_preview_width?0$_preview_width:400)/6}
34194      else +r2dy[$l] {(0$_preview_height?0$_preview_height:400)/6} fi
34195      frame. 1,1,0,0,0,255 drgba. to. "#"$l,0,-2,13 to_rgba.
34196      mv. -3
34197      thumbnail=1
34198    fi
34199
34200    # Render region boundaries of layout layer, for preview.
34201    +f. "const boundaries = 1; ref(crop(x - 2,y - 2,5,5),V); if (min(V)==max(V),0,i)"
34202    (0,0,0,$cols) r. 3,{$nb_layers+1},1,1,-1 1,100%,1,1,y?255:0 a[-2,-1] x permute. yzcx map.. . rm.
34203
34204    repeat $nb_layers
34205      xmin,ymin,xmax,ymax={-3,I[$>]}
34206      if !isinf($xmin)" && "!isinf($ymin)" && "!isinf($xmax)" && "!isinf($ymax)
34207        xc$>,yc$>={0,0.5*([$xmin,$ymin]+[$xmax,$ymax]-6)*100/[w,h]}
34208      else xc$>,yc$>=-1024
34209      fi
34210    done
34211
34212    mv. -3
34213    boundaries=1
34214  fi
34215
34216  # Render montage.
34217  if iM>0
34218
34219    # Resize layers to fit proposed layout.
34220    repeat $nb_layers
34221      l={$>+1} xmin,ymin,xmax,ymax={-2,I[$>]}
34222      if !isinf($xmin)" && "!isinf($ymin)" && "!isinf($xmax)" && "!isinf($ymax)
34223        dx={$xmax-$xmin+1}
34224        dy={$ymax-$ymin+1}
34225        nw,nh={$l,round(min(w/$dx,h/$dy)*[$dx,$dy]/(1+3*(${zoom$>}/10)^2))}
34226        r[$l] $nw,$nh,1,100%,0,0,{(100-${xcenter$>})%},{(100-${ycenter$>})%}  # Centered crop
34227        r[$l] $dx,$dy,1,100%,6                                                # Resize
34228      else r[$l] 1,1 # Save a bit of memory!
34229      fi
34230    done
34231
34232    # Draw image pixels over layout layer.
34233    to_rgba[0]
34234    f[0] "
34235      l = i#-1;
34236      l1 = l - 1;
34237      l<=0?I:(
34238        xmin = i(#-2,l1,0,0,0);
34239        ymin = i(#-2,l1,0,0,1);
34240        rx = x - xmin;
34241        ry = y - ymin;
34242        crop(#l,rx,ry,0,0,1,1,1,4);
34243      )"
34244  fi
34245  rm[-2,-1]
34246
34247  # Manage preview.
34248  if $boundaries blend[0,-1] alpha fi # Add shape boundaries.
34249  if $thumbnail # Add current layer thumbnail over preview
34250    drgba[0] rr2d[0] ${-gui_preview_wh}
34251    j[0] .,3,3,0,0,0.75
34252    rm.
34253  fi
34254  if $-1 repeat $nb_layers
34255    to "#"{1+$>},${xc$>}%,${yc$>}%,13,1,0.75
34256  done fi
34257
34258
34259  k[0] c 0,255
34260  u $status
34261
34262fx_drawn_montage :
34263  _fx_drawn_montage $*,0
34264
34265fx_drawn_montage_preview :
34266  _fx_drawn_montage $*,1
34267
34268#@gui Extract Objects : fx_extract_objects, fx_extract_objects_preview(1)
34269#@gui : Background Point (%) = point(0,0)
34270#@gui : sep = separator()
34271#@gui : Color Tolerance = int(20,0,256)
34272#@gui : Opacity Threshold (%) = int(50,0,100)
34273#@gui : Minimal Area = float(0.3,0,5)
34274#@gui : Connectivity = choice("Low","High")
34275#@gui : Output As = _choice(0,"Crop","Segmentation")
34276#@gui : sep = separator()
34277#@gui : Preview Guides = bool(1)
34278#@gui : sep = separator()
34279#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/23/02</i>.</small>")
34280#@gui : url = link("Filter explained here","http://gimpchat.com/viewtopic.php?f=28&t=7905")
34281fx_extract_objects :
34282  if $5 min_area=$5% else min_area=6 fi
34283  repeat $! l[$<] to_rgba
34284    nm=${-gui_layer_name}
34285    w={w} h={h}
34286    x={$1%*(w-1)}
34287    y={$2%*(h-1)}
34288    color={I($x,$y)}
34289    if $7==0 # Output: Crop.
34290      +replace_color $3,0,$color,0,0,0,0 autocrop_components. $4%,$min_area,$6,2
34291      repeat w
34292        +z[0] {1,i($>,0)},{1,i($>,1)},{1,i($>,3)},{1,i($>,4)}
34293        nm. pos({1,i($>,0)},{1,i($>,1)}),name($nm" "[$>])
34294      done rm[0,1]
34295    elif $7==1 # Output: Segmentation.
34296      replace_color $3,0,$color,0,0,0,0
34297      +autocrop_components[0] $4%,$min_area,$6,2
34298      autocrop_components[0] $4%,$min_area,$6,1
34299      repeat w nm[$>] pos({i($>,0)},{i($>,1)}),name($nm" "[$>]) done rm.
34300    fi
34301    $w,$h,1,4 fc. $color nm. name($nm" [background]")
34302  endl done
34303
34304fx_extract_objects_preview :
34305  x0,y0=${1,2}
34306  if $5 min_area=$5% else min_area=5 fi
34307  repeat $! l[$>] to_rgba
34308    x={$x0%*(w-1)}
34309    y={$y0%*(h-1)}
34310    color={I($x,$y)}
34311    +replace_color $3,0,$color,0,0,0,0
34312    autocrop_components. $4%,$min_area,$6,2
34313    repeat w
34314      xycoords={1,i($>,0)},{1,i($>,1)},{1,i($>,3)},{1,i($>,4)}
34315      rectangle[0] $xycoords,0.3,0,0,255,255
34316      rectangle[0] $xycoords,1,0xFFFFFFFF,0,0,0,255
34317    done
34318    drgba[0]
34319    to[0] {w}" objects",2,2,13,2,0.3,255,255,255,255
34320    k[0]
34321    if $8
34322      line 0,$y0%,100%,$y0%,0.5,0xF0F0F0F0,255 line 0,$y0%,100%,$y0%,0.5,0x0F0F0F0F,0
34323      line $x0%,0,$x0%,100%,0.5,0xF0F0F0F0,255 line $x0%,0,$x0%,100%,0.5,0x0F0F0F0F,0
34324    fi
34325    circle $x,$y,3,1,0,255,0 circle $x,$y,3,1,0xFFFFFFFF,0
34326  endl done
34327
34328#@gui Grid [Cartesian] : fx_imagegrid, fx_imagegrid(0)
34329#@gui : X-Size = int(10,2,512)
34330#@gui : Y-Size = int(10,2,512)
34331#@gui : sep = separator()
34332#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
34333fx_imagegrid :
34334  imagegrid $1,$2
34335
34336#@gui Grid [Hexagonal] : fx_imagegrid_hexagonal, fx_imagegrid_hexagonal(1)
34337#@gui : Resolution = int(32,1,128)
34338#@gui : Outline = float(0.1,0,0.5)
34339#@gui : Anti-Aliasing = bool(1)
34340#@gui : sep = separator()
34341#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/12/01</i>.</small>")
34342fx_imagegrid_hexagonal :
34343  repeat $! l[$>]
34344    if $3 r 200%,200%,1,100% fi
34345    imagegrid_hexagonal $1,$2
34346    if $3 r 50%,50%,1,100%,2 fi
34347  endl done
34348
34349#@gui Grid [Triangular] : fx_imagegrid_triangular, fx_imagegrid_triangular(0)
34350#@gui : Pattern Width = int(10,8,128)
34351#@gui : Pattern Height = int(18,8,128)
34352#@gui : Pattern Type = choice(0,"Horizontal","Vertical","Crossed","Cube","Decreasing","Increasing")
34353#@gui : Outline Color = color(255,255,255,128)
34354#@gui : sep = separator()
34355#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/08/07</i>.</small>")
34356fx_imagegrid_triangular :
34357  repeat $! l[$>] split_opacity l[0] to_rgb
34358    imagegrid_triangular ${1-3},{$7/255},${4-6}
34359  endl a c endl done
34360
34361#@gui Make Seamless [Diffusion] : fx_make_seamless, fx_make_seamless_preview(1)
34362#@gui : Equalize Light = float(0,0,100)
34363#@gui : sep = separator()
34364#@gui : Preview Original = bool(0)
34365#@gui : Tiled Preview = choice(3,"None","2x1","1x2","2x2","3x3","4x4")
34366#@gui : sep = separator()
34367#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
34368#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
34369#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
34370#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
34371#@gui : sep = separator()
34372#@gui : note = note{"<small><b>Note:</b> This filter helps in converting your input pattern as a <b>seamless</b>
34373#@gui : (a.k.a periodic) texture.</small>"}
34374#@gui : sep = separator()
34375#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/24/02</i>.</small>")
34376fx_make_seamless :
34377  repeat $! l[$>]
34378    if $1 +b {20.5-$1/50}% -[0] [1] fc. ${average_colors.} + fi
34379  endl done
34380  periodize_poisson c 0,255
34381
34382fx_make_seamless_preview :
34383  u={arg($3,2,1,2,3,4)} v={arg($3,1,2,2,3,4)}
34384  gui_split_preview "if !$2 fx_make_seamless $* fi if $3 array "$u","$v" fi",${-3--1}
34385
34386#@gui Make Seamless [Patch-Based] : fx_frame_seamless, fx_frame_seamless_preview(0)
34387#@gui : Frame Size = int(32,0,256)
34388#@gui : Patch Size = int(9,3,64)
34389#@gui : Blend Size = int(0,0,64)
34390#@gui : Frame Type = choice(1,"Inner","Outer")
34391#@gui : Equalize Light = float(100,0,100)
34392#@gui : sep = separator()
34393#@gui : Preview Original = bool(0)
34394#@gui : Tiled Preview = choice(3,"None","2x1","1x2","2x2","3x3","4x4")
34395#@gui : sep = separator()
34396#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
34397#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
34398#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
34399#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
34400#@gui : sep = separator()
34401#@gui : note = note{"<small><b>Note:</b> This filter helps in converting your input pattern as a <b>seamless</b>
34402#@gui : (a.k.a periodic) texture.</small>"}
34403#@gui : sep = separator()
34404#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/15/12</i>.</small>")
34405fx_frame_seamless :
34406  repeat $! l[$>]
34407    if $5 +b {20.5-$5/50}% -[0] [1] fc. ${average_colors.} + fi
34408  endl done
34409  frame_seamless ${1-4} c 0,255
34410
34411fx_frame_seamless_preview :
34412  u={arg($7,2,1,2,3,4)} v={arg($7,1,2,2,3,4)}
34413  gui_split_preview "if !$6 fx_frame_seamless $* fi if $7 array "$u","$v" fi",${-3--1}
34414
34415#@gui Ministeck : fx_ministeck, fx_ministeck_preview(1)
34416#@gui : Number of Colors = int(8,2,24)
34417#@gui : Resolution (px) = int(64,16,256)
34418#@gui : Piece Size (px) = int(8,1,64)
34419#@gui : Piece Complexity = int(2,1,10)
34420#@gui : Relief Amplitude = float(100,0,256)
34421#@gui : Relief Size = float(0.3,0,1)
34422#@gui : Add 1px Outline = bool(0)
34423#@gui : sep = separator()
34424#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/14/01</i>.</small>")
34425fx_ministeck :
34426  repeat $! l[$>]
34427    if w>h r2dx {min($2,w)} else r2dy {min($2,h)} fi
34428    split_opacity l[0]
34429      +colormap. $1 index.. .
34430      [0],[0],1,1 rand[2] 0,1 dilate[2] $4 +[0,2]
34431      r[0] $3""00%,$3""00%
34432      +g[0] xy,1 !=[-2,-1] 0 +f[0] 'i(x+1,y+1)-i(x,y)' !=[-3--1] 0 -|[-3--1]
34433      z[0,-1] 0,0,{w-2},{h-2}
34434      if $7 . fi
34435      +shift. 1,1 *.. -1 +[-2,-1] b. {$6*$3/5} n. -$5,$5
34436      map[0] [1] rm[1] +[0,-1]
34437      if $7 ==[1] 0 * fi
34438    endl r. [0],[0],1,100% a c
34439  endl done
34440  c 0,255
34441
34442fx_ministeck_preview :
34443  repeat $! l[$>]
34444    w={w} h={h}
34445    fx_ministeck $*
34446    r $w,$h,1,100%,0,0,0.5,0.5
34447  endl done
34448
34449#@gui Montage : fx_montage, fx_montage_preview(1) : *
34450#@gui : Montage Type = choice("Auto","Custom Layout","Horizontal","Vertical","Horizontal Array","Vertical Array")
34451#@gui : Custom Layout = text{"V(H(0,1),H(2,V(3,4)))"}
34452#@gui : Merging Mode = choice(1,"Aligned","Scaled")
34453#@gui : Centering / Scale = float(0.5,0,1)
34454#@gui : Padding (px) = int(0,0,128)
34455#@gui : sep = separator()
34456#@gui : Frame (px) = int(0,0,128)
34457#@gui : Frame Color = color(0,0,0,255)
34458#@gui : sep = separator()
34459#@gui : Angle = float(0,0,360)
34460#@gui : Angle Variations = float(0,0,180)
34461#@gui : sep = separator()
34462#@gui : Cycle Layers = int(0,-255,255)
34463#@gui : Revert Layer Order = bool()
34464#@gui : Output As = _choice("Single Layer","Multiple Layers")
34465#@gui : sep = separator()
34466#@gui : note = note{"<small><b>Instructions:</b>\n
34467#@gui : - Don't forget to set the <i>Input layers...</i> option on the left if you have multiple input layers
34468#@gui : for your montage.\n
34469#@gui : - The <i>Custom layout</i> parameter is only active when <i>Montage type</i> is set to <i>Custom layout</i>.
34470#@gui : This is basically a string containing expressions such as:\n
34471#@gui : \n     . <i>H(a,b)</i> or <i>V(a,b)</i> stand respectively for an horizontal and vertical merge of two
34472#@gui : blocks <i>a</i> and <i>b</i>.
34473#@gui : \n     . <i>R(a)</i>, stands for a 90-deg. rotated version of a block <i>a</i>. Use <i>RR(a)</i> and
34474#@gui : <i>RRR(a)</i> for resp. 180-deg and 270-deg. rotations.
34475#@gui : \n     . <i>M(a)</i>, stands for a X-mirrored version of a block <i>a</i>. Use <i>MRR(a)</i> for a Y-mirrored
34476#@gui : version of <i>a</i>.\n\n
34477#@gui : - A block <i>a</i> can be a layer index or a nested montage expression itself.\n
34478#@gui : - Layer indices start from <i>0 (top layer)</i> and are treated periodically.
34479#@gui : </small>"}
34480#@gui : url = link("Click here for a tutorial","https://patdavid.net/2014/05/gmic-montage.html")
34481#@gui : url = link("+ video tutorial","http://www.youtube.com/watch?v=iM42vx22gwg")
34482#@gui : sep = separator()
34483#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/22/12</i>.</small>")
34484fx_montage : skip "${2=A}"
34485  if !$! return fi
34486  code0=X code1="$2" code2=H code3=V code4=A code5=B
34487  if $3==1" && "$4<0.5 r {max(10,$4*200)}%,{max(10,$4*200)}%,1,100%,2 fi
34488  to_rgba if $14 rv fi if $13%$! mv[{$13%$!}--1] 0 fi
34489  if $11" || "$12 repeat $! rotate[$>] {$11+u(-$12,$12)},1,0 done fi
34490  montage ${code$1},{if($3==0,$4,2+max(0,$4-0.5))},$15,\
34491    "if $""7%2 mirror x fi if $""8%2 mirror y fi "\
34492    "rotate {90*$""6} "\
34493    "if $5||$6 "\
34494    "r {max(1,$""4-2*($5+$6))},{max(1,$""5-2*($5+$6))},1,100%,2 "\
34495    "frame $6,$6,${7-10} "\
34496    "r {w+2*$5},{h+2*$5},1,100%,0,0,0.5,0.5 "\
34497    "else r $""4,$""5,1,100%,2 fi "
34498  if $15 gui_autocrop_layers fi
34499  gui_set_layer_name "[Montage]"
34500
34501fx_montage_preview : skip "${2=A}"
34502  if !$! return fi
34503  w={w} h={h}
34504  if $3==1" && "$4<0.5 r {max(10,$4*200)}%,{max(10,$4*200)}%,1,100%,2 fi
34505  drgba
34506  code0=X code1="$2" code2=H code3=V code4=A code5=B
34507  to_rgba if $14 rv fi if $13%$! mv[{$13%$!}--1] 0 fi
34508  if $11" || "$12 repeat $! rotate[$>] {$11+u(-$12,$12)},1,0 done fi
34509  montage ${code$1},{if($3==0,$4,2+max(0,$4-0.5))},0,\
34510    "if $""7%2 mirror x fi if $""8%2 mirror y fi
34511     rotate {90*$""6}
34512     if $5||$6
34513       r {max(1,$""4-2*($5+$6))},{max(1,$""5-2*($5+$6))},1,100%,2 fs={min(53,max(w,h)/3)}
34514       frame $6,$6,${7-10}
34515       r {w+2*$5},{h+2*$5},1,100%,0,0,0.5,0.5
34516       0 t. \\\#$""1,0,0,$fs,1,255 expand_xy. 3,0 [-1]x3 a[-4--2] c
34517       dilate. {3+2*$fs/20} a.. .,c j[0] [1],{5+$5+$6},{$5+$6},0,0,1,[2],255 k[0]
34518     else
34519       r $""4,$""5,1,100%,2 fs={min(53,max(w,h)/3)}
34520       0 t. \\\#$""1,0,0,$fs,1,255 expand_xy. 3,0 [-1]x3 a[-4--2] c
34521       dilate. {3+2*$fs/20} a.. .,c j[0] [1],5,0,0,0,1,[2],255 k[0]
34522     fi "
34523  nw={w} nh={h}
34524  resize_ratio2d $w,{$h-16},2,2
34525  drgba
34526  i[0] 100%,15,1,3,240 t[0] "Estimated size : "{round(100*$nw/$w)}%" x "{round(100*$nh/$h)}%,2,0,16 r[0] 100%,16,1,3,0
34527  a y
34528
34529#@gui Puzzle : fx_puzzle, fx_puzzle_preview(1)
34530#@gui : note = note("<small><b>Pattern parameters:</b></small>")
34531#@gui : X-Tiles = int(5,2,32)
34532#@gui : Y-Tiles = int(5,2,32)
34533#@gui : Curvature = float(0.5,0,1.5)
34534#@gui : Connectors Centering = float(0,0,1)
34535#@gui : Connectors Variability = float(0,0,2)
34536#@gui : sep = separator()
34537#@gui : note = note("<small><b>Blending parameters:</b></small>")
34538#@gui : Relief Smoothness = float(0.3,0,3)
34539#@gui : Relief Contrast = float(100,0,255)
34540#@gui : Outline Smoothness = float(0.2,0,3)
34541#@gui : Outline Contrast = float(255,0,255)
34542#@gui : sep = separator()
34543#@gui : note = note("<small><b>Recomposition parameters:</b></small>")
34544#@gui : Scale = float(100,0,150)
34545#@gui : Scale Variations = float(0,0,100)
34546#@gui : Angle = float(0,-180,180)
34547#@gui : Angle Variations = float(0,0,180)
34548#@gui : Shuffle Pieces = bool(0)
34549#@gui : Additional Outline = bool(0)
34550#@gui : Output Each Piece on a Different Layer = _bool(0)
34551#@gui : sep = separator()
34552#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/06/01</i>.</small>")
34553fx_puzzle :
34554  repeat $! l[$<]
34555    w={w} h={h} to_rgb
34556    puzzle $w,$h,$1,$2,$3,$4,$5
34557    +b. $6%,0 g. xy +[-2,-1] n. -$7,$7 +[0,-1]  # Relief.
34558    +b. $8%,0 n. 0,1 *. -1 +. 1 n. {(255-$9)/255},1 *[0,-1] c 0,255  # Outline.
34559
34560    if $10!=100||$11||$12||$13||$14||$15||$16
34561
34562      # Decompose puzzle into set of pieces.
34563      +-. 1 label_fg. 0
34564      +area_fg. 0,0 <. 50% -|... . ==. 0 *[-2,-1]
34565      distance.. 0 *.. -1 watershed. .. rm.. label. 0,0
34566
34567      repeat iM+1
34568        +==[1] $>
34569        coords=${autocrop_coords.\ 0}
34570        +z[0] $coords z.. $coords rv[-2,-1] *.. . *. 255 a[-2,-1] c
34571        x$>={arg(1,$coords)+round(w/2)} y$>={arg(2,$coords)+round(h/2)}
34572      done
34573      rm[0,1]
34574
34575      # Recompose puzzle.
34576      if $14 sort_list +,u fi
34577
34578      if $16 # One piece by layer.
34579        repeat $! l[$<]
34580          r2dy {max(0.1,$10+$11*u(-1,1))}% rotate {$12+$13*u(-1,1)}
34581          if $15 expand_xy 1,0 fi
34582          cx={round(w/2)} cy={round(h/2)}
34583          sh 100% if $15 dilate. 3 fi
34584          i[0] $w,$h,1,4
34585          j[0] ..,{${x$<}-$cx},{${y$<}-$cy},0,0,1,.,255 rm[-2,-1]
34586        endl done
34587
34588      else # All pieces on the same layer.
34589        i[0] $w,$h,1,{s}
34590        repeat $!-1
34591          r2dy. {max(0.1,$10+$11*u(-1,1))}% rotate. {$12+$13*u(-1,1)}
34592          if $15 expand_xy. 1,0 fi
34593          cx={round(w/2)} cy={round(h/2)}
34594          sh. 100% if $15 dilate. 3 fi
34595          j[0] ..,{${x$<}-$cx},{${y$<}-$cy},0,0,1,.,255 rm[-2,-1]
34596        done
34597      fi
34598
34599    else rm.
34600    fi
34601  endl done
34602
34603fx_puzzle_preview :
34604  fx_puzzle ${1-15},0
34605
34606#@gui Taquin : fx_taquin, fx_taquin(1)
34607#@gui : X-Tiles = int(7,1,20)
34608#@gui : Y-Tiles = int(7,1,20)
34609#@gui : Remove Tile = choice("None","First","Last","Random")
34610#@gui : sep = separator()
34611#@gui : Relief = float(50,0,255)
34612#@gui : Border Thickness (%) = float(5,0,100)
34613#@gui : Border Outline = int(0,0,16)
34614#@gui : Ouline Color = color(0,0,0,255)
34615#@gui : sep = separator()
34616#@gui : Random Seed = int(0,0,65535)
34617#@gui : sep = separator()
34618#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/13/01</i>.</small>")
34619fx_taquin :
34620  to_a repeat $! l[$>] srand $11 taquin $1,$2,$3,$4,$5%,$6,${7-10} endl done
34621
34622#@gui Tileable Rotation : fx_rotate_tileable, fx_rotate_tileable_preview(1)
34623#@gui : Angle = float(45,0,360)
34624#@gui : Maximum Size Factor = int(8,0,20)
34625#@gui : Array Mode = choice(0,"None","x-axis","y-axis","xy-axes","2xy-axes")
34626#@gui : sep = separator()
34627#@gui : note = note("<small><b>Note:</b> This filter implements the tileable rotation technique described by
34628#@gui : <b>Peter Yu</b>, at:</small>")
34629#@gui : url = link("[Peter Yu] Create rotated tileable patterns",
34630#@gui : "http://www.peteryu.ca/tutorials/gimp/rotate_tileable_patterns")
34631#@gui : sep = separator()
34632#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/26/05</i>.</small>")
34633fx_rotate_tileable :
34634  if $3 array_mirror 1,{$3-1},1 fi
34635  rotate_tileable $1,{if($3==0,$2,$2/2)}
34636
34637fx_rotate_tileable_preview :
34638  l fx_rotate_tileable $*
34639  onfail gui_warning_preview "Invalid image size" endl
34640
34641#@gui Tiled Rotation : fx_rotate_tiles, fx_rotate_tiles(1)
34642#@gui : X-Tiles = int(5,1,80)
34643#@gui : Y-Tiles = int(5,1,80)
34644#@gui : Angle = float(15,0,360)
34645#@gui : X-Shadow = float(3,-20,20)
34646#@gui : Y-Shadow = float(3,-20,20)
34647#@gui : Smoothness = float(1.8,0,5)
34648#@gui : sep = separator()
34649#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
34650fx_rotate_tiles :
34651  to_rgba rotate_tiles $3,$1,$2 drop_shadow $4%,$5%,$6%
34652
34653#@gui Tiled Normalization : fx_normalize_tiles, fx_normalize_tiles(1)
34654#@gui : X-Tiles = int(25,1,80)
34655#@gui : Y-Tiles = int(25,1,80)
34656#@gui : Minimal Value = float(0,0,255)
34657#@gui : Maximal Value = float(255,0,255)
34658#@gui : sep = separator()
34659#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
34660#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
34661#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
34662#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
34663#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
34664#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
34665#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
34666#@gui : sep = separator()
34667#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
34668_fx_normalize_tiles :
34669  repeat $! l. split_tiles $1,$2 n $3,$4 append_tiles $1,$2 endl mv. 0 done
34670
34671fx_normalize_tiles :
34672  ac "_fx_normalize_tiles ${1-4}",$-1
34673
34674#@gui Tiled Random Shifts : fx_shift_tiles, fx_shift_tiles(1)
34675#@gui : X-Tiles = int(10,1,30)
34676#@gui : Y-Tiles = int(10,1,30)
34677#@gui : Amplitude = float(10,0,100)
34678#@gui : Opacity = float(1,0,1)
34679#@gui : sep = separator()
34680#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
34681fx_shift_tiles :
34682  to_rgba shift_tiles $1,$2,$3
34683  if $4<1 repeat $! s. c *. $4 a[-4--1] c mv. 0 done fi
34684
34685#@gui Tiled Parameterization : fx_parameterize_tiles, fx_parameterize_tiles(1)
34686#@gui : X-Tiles = int(10,1,30)
34687#@gui : Y-Tiles = int(10,1,30)
34688#@gui : Fitting Function = choice("Linear","Quadratic")
34689#@gui : sep = separator()
34690#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
34691fx_parameterize_tiles :
34692  if $3
34693    quadratize_tiles $1,$2
34694  else
34695    linearize_tiles $1,$2
34696  fi
34697  c 0,255
34698
34699#@gui Tiled Isolation : fx_isolate_tiles, fx_isolate_tiles(0)
34700#@gui : X-Size = float(10,0,100)
34701#@gui : Y-Size = float(10,0,100)
34702#@gui : X-Border = float(5,0,100)
34703#@gui : Y-Border = float(5,0,100)
34704#@gui : Keep Tiles Square = bool(1)
34705#@gui : Keep Borders Square = bool(1)
34706#@gui : sep = separator()
34707#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/13/04</i>.</small>")
34708fx_isolate_tiles :
34709  repeat $! l[$>] to_rgba
34710    if $5 sx={round(min(w,h)*max($1,$2)/100)} sy=$sx else sx={round(w*$1/100)} sy={round(h*$2/100)} fi
34711    if $6 bx={max($3,$4)} by=$bx else bx=$3 by=$4 fi
34712    s x,-$sx
34713    repeat $! l[$>] s y,-$sy r 100%,{100+$by}%,1,100%,0,0,0.5,0.5 a y endl done
34714    r {100+$bx}%,100%,1,100%,0,0,0.5,0.5 a x
34715  endl done
34716
34717
34718#@gui ____<b>Artistic</b>
34719#-------------------------
34720
34721#@gui Bokeh : fx_bokeh, fx_bokeh_preview(1)
34722#@gui : Number of Scales = int(3,1,10)
34723#@gui : Shape = choice(8,"Triangle","Square","Diamond","Pentagon","Hexagon","Octogon","Decagon","Star","Circular")
34724#@gui : Random Seed = int(0,0,65535)
34725#@gui : sep = separator()
34726#@gui : note = note{"<small><b>Starting parameters:</b></small>"}
34727#@gui : Density = int(30,1,256)
34728#@gui : Radius (%) = float(8,0,50)
34729#@gui : Outline (%) = float(4,0,100)
34730#@gui : Inner Shade = float(0.3,0,1)
34731#@gui : Smoothness = float(0.2,0,8)
34732#@gui : Color = color(210,210,80,160)
34733#@gui : Color Dispersion = float(0.7,0,1)
34734#@gui : sep = separator()
34735#@gui : note = note{"<small><b>Ending parameters:</b></small>"}
34736#@gui : Density = int(30,1,256)
34737#@gui : Radius (%) = float(20,0,50)
34738#@gui : Outline (%) = float(20,0,100)
34739#@gui : Inner Shade = float(1,0,1)
34740#@gui : Smoothness = float(2,0,8)
34741#@gui : Color = color(170,130,20,110)
34742#@gui : Color Dispersion = float(0.15,0,1)
34743#@gui : sep = separator()
34744#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
34745#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
34746#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
34747#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
34748#@gui : sep = separator()
34749#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/02/07</i>.</small>")
34750fx_bokeh :
34751  _shape=$2
34752  srand $3
34753  repeat $! l[$<] nm=${-gui_layer_name} pos=${-gui_layer_pos}
34754    100%,100%,1,3
34755    (${4-13};${14-23}) if $1>2 r. 100%,$1,1,1,3 fi
34756    repeat $1
34757      +rows. $> _fx_bokeh... {^} rm.
34758    done
34759    rm. nm. name($nm),opacity(100),mode(screen),pos($pos)
34760    rv
34761    if 0$_output_mode==0 gui_merge_layers
34762    elif 0$_output_mode<2 rm.
34763    fi
34764  endl done
34765
34766_fx_bokeh0 : shape_star $1,3        # Triangle.
34767_fx_bokeh1 : $1,$1,1,1,1             # Square.
34768_fx_bokeh2 : shape_diamond $1       # Diamond.
34769_fx_bokeh3 : shape_polygon $1,5,45  # Pentagon.
34770_fx_bokeh4 : shape_polygon $1,6,45  # Hexagon.
34771_fx_bokeh5 : shape_polygon $1,8,45  # Octogon.
34772_fx_bokeh6 : shape_polygon $1,10,45 # Decagon.
34773_fx_bokeh7 : shape_star $1,5        # Star.
34774_fx_bokeh8 : shape_circle $1        # Circle.
34775
34776fx_bokeh_preview :
34777  gui_split_preview "_output_mode=0 fx_bokeh $*",${-3--1}
34778
34779# [internal] Draw one step of bokeh with specified geometry on last image.
34780# $1=density, $2=size(in %), $3=outline(in %), $4=shade (in [0,1]), $5=smoothness (in %),
34781# $6,$7,$8,$9=RGBA (in [0,255]), $10 = color dispersion (in [0,1])
34782_fx_bokeh :
34783  radius1={r=max(w,h)*$2%;r+1-(r%2)}
34784  radius2={r=$radius1-($radius1*$3%);r+1-(r%2)}
34785
34786  random3d $1 *3d. {-2,w},{-2,h},0
34787  _fx_bokeh$_shape $radius1
34788  if $radius2>=1 _fx_bokeh$_shape $radius2 ri. ..,0,0,0.5,0.5 *. {max(0,min(1,1-$4))} -[-2,-1] fi
34789  sigma={-3,$5%*w}
34790  r. {w+5*$sigma},{h+5*$sigma},1,1,0,0,0.5,0.5
34791  b. $sigma,0 n. 0,255
34792  sprites3d[1] [2],1 rm[2]
34793  l.
34794    s3d r.. 3,{-2,h/3},1,1,-1 s.. x
34795    d={$10*255}
34796    rand[-4] {$6-$d},{$6+$d}
34797    rand... {$7-$d},{$7+$d}
34798    rand.. {$8-$d},{$8+$d}
34799    a[-4--2] x c.. 0,255 y a y
34800  endl
34801  j3d[0] [1],0,0,0,{$9/255},1,0,0 rm[1]
34802
34803#@gui Brushify : fx_brushify, fx_brushify_preview(0)
34804#@gui : note = note("<small><b>Brush parameters:</b></small>")
34805#@gui : Shape = choice(7,"Bottom layer","Top layer","Rectangle","Diamond","Pentagon","Hexagon","Octogon",
34806#@gui : "Ellipse","Gaussian","Star","Heart")
34807#@gui : Ratio = float(0.25,0,1)
34808#@gui : Number of Sizes = int(4,1,16)
34809#@gui : Maximal Size = int(64,1,128)
34810#@gui : Minimal Size (%)= float(25,0,100)
34811#@gui : Number of Orientations = int(12,1,24)
34812#@gui : Fuzzyness = float(0,0,10)
34813#@gui : Smoothness = float(2,0,10)
34814#@gui : Light Type = choice(4,"None","Flat","Darken","Lighten","Full")
34815#@gui : Light Strength = float(0.2,0,1)
34816#@gui : Opacity = float(0.5,0,1)
34817#@gui : sep = separator()
34818#@gui : note = note("<small><b>Painting parameters:</b></small>")
34819#@gui : Density (%) = float(30,0,100)
34820#@gui : Contour Coherence = float(1,0,1)
34821#@gui : Orientation Coherence = float(1,0,1)
34822#@gui : Gradient Smoothness = float(1,0,10)
34823#@gui : Structure Smoothness = float(5,0,10)
34824#@gui : Primary Angle = float(0,-180,180)
34825#@gui : Angle Dispersion = float(0.2,0,1)
34826#@gui : sep = separator()
34827#@gui : Preview Brush = bool(1)
34828#@gui : sep = separator()
34829#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/22/04</i>.</small>")
34830fx_brushify :
34831  _fx_brushify $*
34832  s0=0--3 s1=1--2 s2=0--2
34833  l[${s{$1==0?0:$1==1?1:2}},-1] brushify[^-1] .,$3,{$5%},$6,$9,$10,$11,$12%,$13,$14,$15,$16,$17,$18 rm. endl
34834
34835_fx_brushify : # Insert brush at the end of the list
34836  N={0.9*$4}
34837  if $1==0 +autocrop. i.. 100%,100%,1,3,1 blend[-2,-1] alpha rr2d. $N,$N,0,3
34838  elif $1==1 +autocrop[0] i.. 100%,100%,1,3,1 blend[-2,-1] alpha rr2d. $N,$N,0,3
34839  elif $1==2 $4,$4 rectangle. 10%,10%,90%,90%,1,1
34840  elif $1==3 shape_diamond. $N
34841  elif $1==4 shape_polygon $N,5
34842  elif $1==5 shape_polygon $N,6
34843  elif $1==6 shape_polygon $N,8
34844  elif $1==7 shape_circle. $N
34845  elif $1==8 $4,$4 gaussian. 30%,30%,0
34846  elif $1==9 shape_star $N
34847  elif $1==10 shape_heart $N
34848  fi
34849  norm. r. 100%,{max(0.01,100*$2)}%,1,1,2 r. $4,$4,1,1,0,0,0.5,0.5
34850  spread. $7 b. $8% n. 0,1
34851
34852fx_brushify_preview :
34853  if $1<2" && "$!<2
34854    gui_error_preview "When a custom brush (bottom or top layer) is specified, at least two layers are required
34855                       for this filter to work.In this case, don't forget to set the 'Input layers' option!"
34856    return
34857  fi
34858  fx_brushify $*
34859  if $19
34860    _fx_brushify $*
34861    if $1==0 rm.. elif $1==1 rm[0] fi
34862    rr2d. {0,max(1,w/5)},{0,max(1,h/5)},0,2 n. 0,255
34863    frame. 3,3,0 frame. 1,1,255 frame. 1,1,0
34864    to_rgb. to. "Brush",4,2,13,2,1,255,255,0 to_a.
34865    j[^-1] .,2,2 rm.
34866  else
34867    if $1==0 rm. elif $1==1 rm[0] fi
34868  fi
34869
34870#@gui Cartoon : cartoon, fx_cartoon_preview(0)
34871#@gui : Smoothness = float(3,0,10)
34872#@gui : Sharpening = float(200,0,400)
34873#@gui : Edge Threshold = float(20,1,30)
34874#@gui : Edge Thickness = float(0.25,0,1)
34875#@gui : Color Strength = float(1.5,0,3)
34876#@gui : Color Quantization = int(8,2,256)
34877#@gui : sep = separator()
34878#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
34879#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
34880#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
34881#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
34882#@gui : sep = separator()
34883#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
34884fx_cartoon_preview :
34885  gui_split_preview "cartoon $*",${-3--1}
34886
34887#@gui Circle Abstraction : fx_circle_abstraction, fx_circle_abstraction_preview(1)
34888#@gui : Number of Colors = int(8,2,16)
34889#@gui : Density = int(5,1,100)
34890#@gui : Opacity = float(0.8,0,1)
34891#@gui : Smoothness = float(0,0,4)
34892#@gui : Filled Circles = bool(1)
34893#@gui : Fill Transparent Holes = bool(1)
34894#@gui : Normalize Colors = bool(1)
34895#@gui : sep = separator()
34896#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
34897#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
34898#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
34899#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
34900#@gui : sep = separator()
34901#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/16/06</i>.</small>")
34902fx_circle_abstraction :
34903  repeat $! l[$>]
34904    b $4%
34905    +colormap $1 index[0] [1],0,0
34906    [0],[0],1,4,0
34907
34908    repeat $1
34909      rprogress {$>*100/$1}
34910      +==[0] $>
34911      skeleton3d. 2,2,0,1,0
34912      if {@7} # Process only non empty objects.
34913        s3d. l[-6--1]
34914          r[2] 3,{2,h/3},1,1,-1
34915          1,{2,h/2*$2%},1,1,1 r. 1,{2,h/2},1,1,4 r. 3,200% *[2,-1] y
34916        endl
34917        a[-6--1] y col3d. {1,I($>)}
34918
34919        [0],[0],1,4,0
34920        j3d. ..,0,0,0,1,{1+$5},0,0
34921        sh. 3 col3d... 255 j3d. ...,0,0,0,$3,{1+$5},0,0 rm.
34922        rm..
34923        blend[2,-1] alpha
34924      fi
34925    done
34926    k[2]
34927    if $6 +channels 3 <. 1 inpaint[0] [1],0,1 rm. channels. 0,2 fi
34928    if $7 n 0,255 fi
34929    rprogress 100
34930  endl done
34931
34932fx_circle_abstraction_preview :
34933  gui_split_preview "fx_circle_abstraction $*",${-3--1}
34934
34935#@gui Cubism : fx_cubism, fx_cubism_preview(1)
34936#@gui : Iterations = int(2,0,10)
34937#@gui : Density = float(50,0,200)
34938#@gui : Thickness = float(10,0,50)
34939#@gui : Angle = float(90,0,360)
34940#@gui : Opacity = float(0.7,0.01,1)
34941#@gui : Smoothness = float(0,0,5)
34942#@gui : sep = separator()
34943#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
34944#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
34945#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
34946#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
34947#@gui : sep = separator()
34948#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/05/06</i>.</small>")
34949fx_cubism :
34950  repeat $1 cubism ${2--1} done
34951
34952fx_cubism_preview :
34953  gui_split_preview "fx_cubism $*",${-3--1}
34954
34955#@gui Cutout : fx_cutout, fx_cutout_preview(1)
34956#@gui : Number of Levels = int(4,2,32)
34957#@gui : Edge Simplicity = float(0.5,0,3)
34958#@gui : Edge Fidelity = int(4,0,10)
34959#@gui : Normalize = bool(1)
34960#@gui : sep = separator()
34961#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
34962#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
34963#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
34964#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
34965#@gui : sep = separator()
34966#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Garagecoder</i>
34967#@gui :       Latest Update: <i>2014/03/06</i>.</small>")
34968fx_cutout :
34969  repeat $! l[$>] split_opacity l[0]
34970    median {10-$3}
34971    quantize $1
34972    +luminance. round. area. 0,1 med={ic} rm.
34973    inpaint_holes {$med*$2%},0,1
34974    if $4 n 0,255 fi
34975  endl a c endl done
34976
34977fx_cutout_preview :
34978  gui_split_preview "fx_cutout $*",${-3--1}
34979
34980#@gui Doodle : fx_doodle, gui_no_preview(0)
34981#@gui : Precision (%) = float(30,0,100)
34982#@gui : Smoothness = float(2,0,10)
34983#@gui : Coherence = float(2,0,10)
34984#@gui : Contour Threshold = float(1.5,0,10)
34985#@gui : Spacing = int(2,0,20)
34986#@gui : Minimal Stroke Length = float(70,0,255)
34987#@gui : Preview Progression While Running = _bool(1)
34988#@gui : sep = separator()
34989#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/07/08</i>.</small>")
34990fx_doodle : skip "$*"
34991   repeat $! l[$>]
34992    b $2 n 0,255 structuretensors b $3
34993    100%,100%,1,1,100
34994    if $7 w. ${"fitscreen ."},"[G'MIC] Doodle" fi
34995
34996    _stopflag=0
34997    eval "
34998      const dt = 0.5;
34999      nb_strokes = nb_consecutive_fails = 0;
35000
35001      while (nb_consecutive_fails<500*$1%,
35002        run('+store. canvas');
35003
35004        P0 = u([w,h] - 1);
35005        length = 0;
35006
35007        repeat (2,dir,
35008          P = P0;
35009          oiP = iP = round(P);
35010          C = I(#0,P); E = eig([ C[0], C[1], C[1], C[2] ]); oV = V = (dir?1:-1)*E[4,2];
35011
35012          while (E[0]>$4^2 && (i(iP)>$5 || oiP==iP),
35013            i(iP) = 0;
35014            oiP = iP;
35015            P+=dt*V;
35016            iP = round(P);
35017            C = I(#0,P,1); E = eig([ C[0], C[1], C[1], C[2] ]); V = E[4,2];
35018            dot(oV,V)<0?(V*=-1);
35019            oV = V;
35020            iP!=oiP?++length;
35021          );
35022        );
35023        length<$6?(run('rm. $canvas'); ++nb_consecutive_fails):(
35024          !(++nb_strokes%15)?run('distance. 0');
35025          nb_consecutive_fails = 0;
35026          $7 && !(nb_strokes%30)?(
35027            nb_consecutive_fails = run('if !{*} u inf else +neq. 0 r. {*,w,h},1,1,2 w. rm. u 0 fi')
35028          );
35029        )
35030      )"
35031    k. != 0 * 255
35032  endl done
35033
35034#@gui Diffusion Tensors : fx_diffusiontensors, fx_diffusiontensors_preview(0)
35035#@gui : Resolution (%) = float(10,0,20)
35036#@gui : Size = float(5,0,16)
35037#@gui : Color Mode = choice(3,"Monochrome","Grayscale","Orientation","Color")
35038#@gui : Outline = int(1,0,16)
35039#@gui : sep = separator()
35040#@gui : Sharpness = float(0.15,0,1)
35041#@gui : Anisotropy = float(1,0,1)
35042#@gui : Gradient Smoothness = float(0,0,10)
35043#@gui : Tensor Smoothness = float(3,0,10)
35044#@gui : sep = separator()
35045#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35046#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35047#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35048#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35049#@gui : sep = separator()
35050#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/19/10</i>.</small>")
35051fx_diffusiontensors :
35052  repeat $! l[$>]
35053    wh={[w,h]}
35054    +diffusiontensors ${5-8}
35055    r2dx. {max(1,w*$1%)}
35056    dt. {round(125/max(1,$1))},$2,{$3<3?$3:0},$4
35057    if $3==3
35058      remove_opacity.. r.. .,.,1,100%,3 blend[0] [1],shapeaverage0 dilate. {1+2*$4} a c
35059    else k. fi
35060    r. $wh,1,100%,2
35061  endl done
35062
35063fx_diffusiontensors_preview :
35064  gui_split_preview "fx_diffusiontensors $*",${-3--1}
35065
35066#@gui Ellipsionism : fx_ellipsionism, fx_ellipsionism_preview(0)
35067#@gui : Primary Radius = float(20,1,100)
35068#@gui : Secondary Radius = float(10,1,100)
35069#@gui : Smoothness = float(0.5,0,10)
35070#@gui : Opacity = float(0.7,0,1)
35071#@gui : Outline = float(3,1,3)
35072#@gui : Density = float(0.5,0.1,2)
35073#@gui : sep = separator()
35074#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35075#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35076#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35077#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35078#@gui : sep = separator()
35079#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35080fx_ellipsionism :
35081  ellipsionism ${^0}
35082
35083fx_ellipsionism_preview :
35084  gui_split_preview "fx_ellipsionism $*",${-3--1}
35085
35086#@gui Felt Pen : fx_feltpen, fx_feltpen_preview(0)
35087#@gui : Amplitude = float(300,0,4000)
35088#@gui : Density = float(50,0,100)
35089#@gui : Smoothness = float(1,0,10)
35090#@gui : Opacity = float(0.1,0,1)
35091#@gui : Edge = float(20,0,100)
35092#@gui : Thickness = int(5,2,32)
35093#@gui : sep = separator()
35094#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35095#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35096#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35097#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35098#@gui : sep = separator()
35099#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/25/10</i>.</small>")
35100fx_feltpen :
35101  repeat $! l[$>] +fx_hardsketchbw ${1-5},0,0 blend hardlight erode_oct $6 endl done
35102
35103fx_feltpen_preview :
35104  gui_split_preview "fx_feltpen $*",${-3--1}
35105
35106#@gui Fractalize : fractalize, fractalize(1)
35107#@gui : Detail Level = float(0.8,0,1)
35108#@gui : sep = separator()
35109#@gui : note = note("<small><b>Note:</b> This filter uses lot of random values to generate its result,
35110#@gui : so running it twice will give you different results !</small>")
35111#@gui : sep = separator()
35112#@gui : url = link("Click here for a detailed description of this filter.",\
35113# "http://www.gimpchat.com/viewtopic.php?f=28&t=10036")
35114#@gui : sep = separator()
35115#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/25/04</i>.</small>")
35116
35117#@gui Ghost : fx_ghost, fx_ghost_preview(0)
35118#@gui : note = note("<small><b>Ghost Effect:</b></small>")
35119#@gui : Amplitude = float(200,0,1000)
35120#@gui : Smoothness = float(2,0,10)
35121#@gui : Coherence = float(2,0,10)
35122#@gui : Gamma = float(1,-3,3)
35123#@gui : sep = separator()
35124#@gui : note = note("<small><b>Normalization:</b></small>")
35125#@gui : Amplitude = float(3,0,10)
35126#@gui : Radius = float(16,1,64)
35127#@gui : Invert = bool(0)
35128#@gui : sep = separator()
35129#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35130#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35131#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35132#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35133#@gui : sep = separator()
35134#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/01/30</i>.</small>")
35135fx_ghost :
35136  repeat $! l[$>]
35137    diffusiontensors. 1,1,$2,$3 eigen. compose_channels.. + sqrt.. n.. 1,100 pow.. $4 n.. 0,$1
35138    100%,100%
35139    eval.. "*
35140      ca = i(x,y,0,0);
35141      sa = i(x,y,0,1);
35142      N = i(#-3,x,y);
35143      x0 = x + N*ca;
35144      y0 = y + N*sa;
35145      x1 = x - N*ca;
35146      y1 = y - N*sa;
35147      polygon(#-1,2,x0,y0,x1,y1,-1,1);
35148      I"
35149    k. normalize_local $5,$6
35150  endl done n 0,255 if $7 negate fi
35151
35152fx_ghost_preview :
35153  gui_split_preview "fx_ghost $*",${-3--1}
35154
35155#@gui Hard Sketch : fx_hardsketchbw, fx_hardsketchbw_preview(0)
35156#@gui : Amplitude = float(300,0,4000)
35157#@gui : Density = float(50,0,100)
35158#@gui : Smoothness = float(1,0,10)
35159#@gui : Opacity = float(0.1,0,1)
35160#@gui : Edge = float(20,0,100)
35161#@gui : Fast Approximation = bool(0)
35162#@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white",
35163#@gui : "White on transparent black","Color on white")
35164#@gui : sep = separator()
35165#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35166#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35167#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35168#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35169#@gui : sep = separator()
35170#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35171fx_hardsketchbw :
35172  b $3
35173  if $7==4 repeat $! l[$>] +hardsketchbw $1,$2,$4,$5,$6 blend hardlight endl done return fi
35174  hardsketchbw $1,$2,$4,$5,$6
35175  if $7&1 negate fi
35176  if $7==2 r 100%,100%,1,4 repeat $! sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. done
35177  elif $7==3 r 100%,100%,1,4 repeat $! sh[$>] 3 *. 2 c. 0,255 rm. done
35178  fi
35179
35180fx_hardsketchbw_preview :
35181  gui_split_preview "fx_hardsketchbw $*",${-3--1}
35182
35183#@gui Highlight Bloom : fx_highlight_bloom, fx_highlight_bloom_preview(0)
35184#@gui : Details Strength (%) = float(90,0,400)
35185#@gui : Details Scale = float(60,0,255)
35186#@gui : Smoothness = float(60,0,255)
35187#@gui : Highlight (%) = int(30,0,100)
35188#@gui : Contrast (%) = float(20,0,100)
35189#@gui : sep = separator()
35190#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35191#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35192#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35193#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35194#@gui : sep = separator()
35195#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/24/10</i>.</small>")
35196#@gui : sep = separator()
35197#@gui : note = note("This effect has been inspired by:")
35198#@gui : url = link("This tutorial by Sebastien Guyader and Patrick David",
35199#@gui : "https://pixls.us/articles/highlight-bloom-and-photoillustration-look/")
35200fx_highlight_bloom :
35201  repeat $! l[$>] split_opacity l[0]
35202    +smooth $2,0.3,0.8,1,2
35203    -.. .
35204    amp=$3 do smooth. {min(50,$amp)},0.1,1,1,2 amp-=50 while $amp>0
35205    +retinex. 16,lab,0,15 j.. .,0,0,0,0,{$4%} rm.
35206    ac. "normalize_local {$5%},5",lab_l
35207    *.. {$1%}
35208    + c 0,255
35209  endl a c endl done
35210
35211fx_highlight_bloom_preview :
35212  gui_split_preview "fx_highlight_bloom $*",${-3--1}
35213
35214#@gui Hope Poster : fx_poster_hope, fx_poster_hope_preview(0)+
35215#@gui : Gamma = float(0,-3,3)
35216#@gui : Smoothness = float(3,0,20)
35217#@gui : sep = separator()
35218#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35219#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35220#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35221#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35222#@gui : sep = separator()
35223#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/07/11</i>.</small>")
35224fx_poster_hope :
35225  repeat $! l[$>] split_opacity l[0]
35226    apply_gamma {10^$1} poster_hope $2
35227  endl a c endl done
35228
35229fx_poster_hope_preview :
35230  gui_split_preview "fx_poster_hope $*",${-3--1}
35231
35232#@gui Hough Sketch : fx_houghsketchbw, fx_houghsketchbw_preview(0)
35233#@gui : Smoothness = float(1.25,0,10)
35234#@gui : Density = float(10,0,70)
35235#@gui : Radius = int(5,0,30)
35236#@gui : Threshold = float(80,0,100)
35237#@gui : Opacity = float(0.1,0,1)
35238#@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white",
35239#@gui : "White on transparent black","Color on white")
35240#@gui : sep = separator()
35241#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35242#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35243#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35244#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35245#@gui : sep = separator()
35246#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/05</i>.</small>")
35247fx_houghsketchbw :
35248  b $1 n 0,255
35249  if $6==4 repeat $! l[$>] +houghsketchbw ${2-5} blend hardlight endl done return fi
35250  houghsketchbw ${2-5}
35251  if $6&1 negate fi
35252  if $6==2 r 100%,100%,1,4 repeat $! sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. done
35253  elif $6==3 r 100%,100%,1,4 repeat $! sh[$>] 3 *. 2 c. 0,255 rm. done
35254  fi
35255
35256fx_houghsketchbw_preview :
35257  gui_split_preview "fx_houghsketchbw $*",${-3--1}
35258
35259#@gui Kuwahara : fx_kuwahara, fx_kuwahara_preview(0)
35260#@gui : Iterations = int(2,1,20)
35261#@gui : Radius = int(5,1,30)
35262#@gui : sep = separator()
35263#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
35264#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
35265#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
35266#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
35267#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
35268#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
35269#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
35270#@gui : Value Action = choice("None","Cut","Normalize")
35271#@gui : sep = separator()
35272#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35273#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35274#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35275#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35276#@gui : sep = separator()
35277#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/31/05</i>.</small>")
35278fx_kuwahara :
35279  ac "repeat $1 kuwahara $2 done",$3,$4
35280
35281fx_kuwahara_preview :
35282  gui_split_preview "fx_kuwahara $*",${-3--1}
35283
35284#@gui Linify : fx_linify, fx_linify_preview(0)
35285#@gui : Density = float(40,0,100)
35286#@gui : Spreading = float(2,0,10)
35287#@gui : Resolution (%) = float(40,0,100)
35288#@gui : Line Opacity = float(10,0,30)
35289#@gui : Line Precision = int(24,1,128)
35290#@gui : Color Mode = choice(0,"Subtractive","Additive")
35291#@gui : sep = separator()
35292#@gui : Preview Progression While Running = _bool(1)
35293#@gui : sep = separator()
35294#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35295#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35296#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35297#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35298#@gui : sep = separator()
35299#@gui : note = note{"<b>Note:</b>\n\n
35300#@gui : - This filter is our own implementation of the nice algorithm proposed on the webpage
35301#@gui : <a href="http://linify.me">http://linify.me</a>.\n
35302#@gui : - This is a quite resource-demanding filter, so please be patient when running it.\n
35303#@gui : - It actually renders better when applied on small images (&lt;1024).
35304#@gui : "}
35305#@gui : sep = separator()
35306#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/11/21</i>.</small>")
35307fx_linify :
35308  repeat $! l[$>]
35309    if $7 _debug=1 w ${-fitscreen\ {[w,h]}},0,"[Preview] G'MIC: Linify" fi
35310    linify $1,$2,$3%,$4,$5,$6
35311  endl done
35312
35313fx_linify_preview :
35314  gui_split_preview "fx_linify ${1-6},0",${-3--1}
35315
35316#@gui Lylejk's Painting : fx_lylejk_painting, fx_lylejk_painting_preview(0)
35317#@gui : Iterations = int(10,1,20)
35318#@gui : Abstraction = int(2,1,20)
35319#@gui : Radius = int(4,1,30)
35320#@gui : Canvas = float(10,0,100)
35321#@gui : sep = separator()
35322#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35323#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35324#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35325#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35326#@gui : sep = separator()
35327#@gui : note = note("<small>Authors: <i>Lyle Kroll</i> and <i>David Tschumperlé</i>.
35328#@gui :       Latest Update: <i>2015/23/02</i>.</small>")
35329#@gui : url = link("Filter Explained here","http://www.gimpchat.com/viewtopic.php?f=10&t=2624")
35330fx_lylejk_painting :
35331  repeat $! l[$>]
35332    nm={0,n}
35333    +l repeat $1 b 0.75 unsharp 0.75,10.49 c 0,255 mv. 0 done endl
35334    smooth. 300,0.26,1,0,7
35335    . rv[-3--1]
35336    blend[-2,-1] lighten,0.5
35337    blend[-2,-1] grainmerge,1
35338    fx_kuwahara. $2,$3,0,0
35339    texturize_canvas. $4,4
35340    nm $nm
35341  endl done
35342
35343fx_lylejk_painting_preview :
35344  gui_split_preview "fx_lylejk_painting $*",${-3--1}
35345
35346#@gui Painting : fx_painting, fx_painting_preview(0)+
35347#@gui : Abstraction = int(5,1,10)
35348#@gui : Details Scale = float(2.5,0,5)
35349#@gui : Color = float(1.5,0,4)
35350#@gui : Smoothness = float(50,0,1000)
35351#@gui : Sharpen Shades = bool(1)
35352#@gui : sep = separator()
35353#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35354#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35355#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35356#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35357#@gui : sep = separator()
35358#@gui : note = note("<small>Authors: <i>Lyle Kroll</i>, <i>Angelo Lama</i> and <i>David Tschumperlé</i>.
35359#@gui : \nLatest Update: <i>2011/28/02</i>.</small>")
35360fx_painting : skip ${4=0},${5=0}
35361  repeat $! l[$>]
35362    to_colormode {max(3,s)} split_opacity rv
35363    repeat $1 fx_normalize_local. 10,6,5,20,1,11 done
35364    fx_smooth_anisotropic. {100*$2},0.2,1,$2,{2*$2},0.8,90,2,0,1,1,2,1,16
35365    fx_mix_lab. 1,0,0,$3,0,0.5,$3,0,0.5,0,2,0
35366    if $5 fx_segment_watershed. 10,1,0 fi
35367    smooth. $4,0,1,1,1
35368    rv a c
35369  endl done
35370
35371fx_painting_preview :
35372  gui_split_preview "fx_painting $*",${-3--1}
35373
35374#@gui Pen Drawing : fx_pen_drawing, fx_pen_drawing_preview(0)+
35375#@gui : Amplitude = float(10,0,30)
35376#@gui : sep = separator()
35377#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35378#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35379#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35380#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35381#@gui : sep = separator()
35382#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35383fx_pen_drawing :
35384  drawing $1
35385
35386fx_pen_drawing_preview :
35387  gui_split_preview "fx_pen_drawing $*",${-3--1}
35388
35389#@gui Polygonize [Delaunay] : fx_polygonize_delaunay, fx_polygonize_delaunay_preview(0)
35390#@gui : Density (%) = float(40,0,100)
35391#@gui : Edges = float(5,0,100)
35392#@gui : Boundaries (%) = float(75,0,100)
35393#@gui : Smoothness = float(0.5,0,8)
35394#@gui : Filling = choice(3,"Black","White","Random","Average","Linear")
35395#@gui : Outline (%) = float(50,0,100)
35396#@gui : Outline Color = color(0,0,0,255)
35397#@gui : Anti-Aliasing = bool(1)
35398#@gui : sep = separator()
35399#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35400#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35401#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35402#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35403#@gui : sep = separator()
35404#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/06/05</i>.</small>")
35405fx_polygonize_delaunay :
35406  repeat $! l[$>] to_rgba
35407    wh={[w,h]} if $11 r 150%,150%,1,100%,3 fi
35408
35409    # Compute Delaunay triangulation.
35410    mM={[im,iM]} b $4 n $mM
35411    +to_rgb. gradient_norm. ge. $2
35412    f. "!x || !y || x==w-1 || y==h-1?(u^0.25<$3%):(u^0.25<$1%?i:0)"
35413    {is+1},1,1,2 f.. ">begin(p = 0); i?(I[#-1,++p] = [x,y];p):0"
35414    delaunay..
35415
35416    # Render filling.
35417    if $5==4 # Linear
35418      +f[0] 0 f... "*i?(
35419        p = I(#1); P0 = I[#2,p[0]]; P1 = I[#2,p[1]]; P2 = I[#2,p[2]];
35420        W = solve([P0[0],P1[0],P2[0],P0[1],P1[1],P2[1],1,1,1],[x,y,1]);
35421        I(#-1) = cut(W[0]*I(#0,P0) + W[1]*I(#0,P1) + W[2]*I(#0,P2),0,255);
35422      );I"
35423    elif $5==3 # Average
35424      +f[0] 0 f... "*i?(
35425        p = I(#1); P0 = I[#2,p[0]]; P1 = I[#2,p[1]]; P2 = I[#2,p[2]];
35426        I(#-1) = (I(#0,P0) + I(#0,P1) + I(#0,P2))/3;
35427      );I"
35428    elif $5==2 # Random
35429      +norm[1] label_fg. 0 {iM+1},1,1,3 rand. 0,255 to_rgba. point. 0 map.. . rm.
35430    else # Black or white
35431      +norm[1] !=. 0 *. 255 channels. -3,0
35432      if $5 sh. 0,2 f. 255 rm. fi
35433    fi
35434    rm[0,2]
35435
35436    # Render outlines.
35437    if $6
35438      norm[0] f[0] "i!=j(1) || i!=j(0,1)" # thinning[0] 1
35439      +fc. ${7-10} j.. .,0,0,0,0,{$6%},... rm.
35440    fi
35441
35442    k.
35443    if $11 r $wh,1,100%,2 fi
35444
35445  endl done
35446
35447fx_polygonize_delaunay_preview :
35448  gui_split_preview "fx_polygonize_delaunay $*",${-3--1}
35449
35450#@gui Polygonize [Energy] : fx_polygonize, fx_polygonize_preview(0)
35451#@gui : Amplitude = int(300,0,2000)
35452#@gui : Smoothness = float(10,0,100)
35453#@gui : Minimal Area = float(10,0,100)
35454#@gui : X-Resolution = float(10,1,256)
35455#@gui : Y-Resolution = float(10,1,256)
35456#@gui : Outline Color = color(0,0,0,255)
35457#@gui : sep = separator()
35458#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35459#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35460#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35461#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35462#@gui : sep = separator()
35463#@gui : url = link("Click here for a detailed description of this filter.",\
35464# "http://www.gimpchat.com/viewtopic.php?f=28&t=9174")
35465#@gui : sep = separator()
35466#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/02/12</i>.</small>")
35467fx_polygonize :
35468  polygonize $1,$2,{$3^2},$4,$5
35469  if $9 repeat $! l[$>]  # has outline.
35470    +norm g. xy,1 !=[-2,-1] 0 -|[-2,-1] r. 100%,100%,1,4
35471    replace_color. 0,0,1,1,1,1,$6,$7,$8,$9
35472    blend alpha
35473  endl done fi
35474
35475fx_polygonize_preview :
35476  gui_split_preview "fx_polygonize $*",${-3--1}
35477
35478#@gui Poster Edges : fx_poster_edges, fx_poster_edges_preview(0)
35479#@gui : Image Smoothness = float(20,0,100)
35480#@gui : Edge Threshold = float(60,0,100)
35481#@gui : Edge Shade = float(5,0,30)
35482#@gui : Edge Thickness = float(0,0,5)
35483#@gui : Edge Antialiasing = float(10,0,100)
35484#@gui : Posterization Level = int(0,0,15)
35485#@gui : Posterization Antialiasing = float(0,0,100)
35486#@gui : sep = separator()
35487#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35488#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35489#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35490#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35491#@gui : sep = separator()
35492#@gui : url = link("Click here for a detailed description of this filter.",\
35493# "http://www.davidrevoy.com/article147/gmic-new-filter-poster-edges")
35494#@gui : sep = separator()
35495#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>David Revoy</i>.
35496#@gui :       Latest Update: <i>2012/30/11</i>.</small>")
35497fx_poster_edges :
35498  if $1 bilateral 10,$1 fi
35499  poster_edges ${2-7}
35500
35501fx_poster_edges_preview :
35502  gui_split_preview "fx_poster_edges $*",${-3--1}
35503
35504#@gui Posterize : fx_posterize, fx_posterize_preview(0)
35505#@gui : Smoothness = float(150,0,800)
35506#@gui : Edges (%) = float(30,0,100)
35507#@gui : Paint = float(1,0,10)
35508#@gui : Colors = int(12,2,32)
35509#@gui : Minimal Area = int(0,0,64)
35510#@gui : Outline (%) = float(0,0,100)
35511#@gui : Normalize Colors = bool(0)
35512#@gui : sep = separator()
35513#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35514#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35515#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35516#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35517#@gui : sep = separator()
35518#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/25/10</i>.</small>")
35519fx_posterize :
35520  repeat $! l[$>] split_opacity l[0]
35521    . amp=$1 do smooth. {min(50,$amp)},{$2%},1,$3,{2*$3} amp-=50 while $amp>0
35522    bilateral.. .,5,10 rm.
35523    srgb2rgb +colormap $4 rgb2srgb
35524    index.. .,0,0
35525    if $5
35526      +area.. 0,0 <. $5 inpaint... .,0,3 rm.
35527    fi
35528    map.. . rm.
35529    if $6
35530      +norm (0,1,0;1,1,1;0,1,0) +dilate.. . rm.. -[-2,-1] !=. 0 100%,100%,1,{0,s} j... .,0,0,0,0,{$6%},.. rm[-2,-1]
35531    fi
35532    if $7 n 0,255 fi
35533  endl a c endl done
35534
35535fx_posterize_preview :
35536  gui_split_preview "fx_posterize $*",${-3--1}
35537
35538#@gui Quadtree Variations : fx_quadtree, fx_quadtree_preview(1)
35539#@gui : Mode = choice("Squares","Sierpinksi Design","Ellipse Painting")
35540#@gui : Precision = int(1024,2,4096)
35541#@gui : Homogeneity = float(0.5,0,2)
35542#@gui : Outline = int(0,0,4)
35543#@gui : sep = separator()
35544#@gui : note = note{"<small><b>For 'Ellipse painting' only:</b></small>"}
35545#@gui : Primary Radius = float(3,0,5)
35546#@gui : Secondary Radius = float(1.5,0,5)
35547#@gui : Anisotropy = float(1,0,4)
35548#@gui : Only Leafs = bool(1)
35549#@gui : sep = separator()
35550#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35551#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35552#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35553#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35554#@gui : sep = separator()
35555#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/15/06</i>.</small>")
35556fx_quadtree :
35557  mode,precision,homogeneity,outline,radius1,radius2,anisotropy,only_leafs=${1-8}
35558  m "_qt : sh 3 u {w>1&&h>1?iv*(w*h)^"$homogeneity":-0.1} k[0]" # Function used to split quads
35559  repeat $! l[$>]
35560    to_rgb
35561
35562    # Decompose image with quadtree.
35563    keep_only_leafs={$mode!=2" || "$only_leafs}
35564    +norm. a c
35565    _qt ({d=max(w,h);[0,0,d-1,d-1]},0,${})
35566    repeat $precision
35567      +columns. 100% n={yM} rm.
35568      x0,y0,x1,y1,level={crop(0,$n,5,1)}
35569      xc={round(($x1+$x0)/2)} yc={round(($y1+$y0)/2)}
35570      xc1,yc1={[$xc,$yc]-1}
35571      level1={$level+1}
35572      B0=$x0,$y0,$xc1,$yc1 +crop[0] $B0,3 _qt. B0.=,$level1,${} rm.
35573      B1=$xc,$y0,$x1,$yc1 +crop[0] $B1,3 _qt. B1.=,$level1,${} rm.
35574      B2=$x0,$yc,$xc1,$y1 +crop[0] $B2,3 _qt. B2.=,$level1,${} rm.
35575      B3=$xc,$yc,$x1,$y1 +crop[0] $B3,3 _qt. B3.=,$level1,${} rm.
35576      if $keep_only_leafs
35577        sh. $n,$n,0,0 f. $B0 rm.
35578        ($B1;$B2;$B3) a[-2,-1] y # Insert new leafs
35579      else
35580        =. -1,5,$n # Mark as not terminal leaf anymore
35581        ($B0;$B1;$B2;$B3) a[-2,-1] y # Insert new leafs
35582      else
35583      fi
35584    done
35585    shift. 2,0,0,0,2 sort. +,y levelmax={i(0,h-1)} shift. -2,0,0,0,2 # Sort by quadtree level order
35586
35587    # Estimate data average in each quad.
35588    channels[0] 0,2 +structuretensors[0] a[0,-1] c
35589    r. {w+6},100%,1,1,0
35590    repeat h
35591      x0,y0,x1,y1={crop(0,$>,4,1)}
35592      +crop[0] $x0,$y0,$x1,$y1,3 r. 1,1,1,100%,2 y. x
35593      j.. .,{-2,w-6},$> rm.
35594    done
35595
35596    # Synthetize image.
35597    permute. cyzx
35598    channels[0] 0,3 f[0] 0
35599
35600    if $mode==0 # Squares
35601      f. ">
35602        rectangle(ind,P0,P1,opacity,color) = (
35603          _P0 = P0;
35604          _P2 = P1;
35605          _P1 = [ _P2[0], _P0[1] ];
35606          _P3 = [ _P0[0], _P2[1] ];
35607          polygon(#ind,4,_P0,_P1,_P2,_P3,opacity,color);
35608        );
35609        begin(colo = [ 0,0,0,255 ]);
35610        P = I;
35611        Xpp = P[0,2];
35612        Xnn = P[2,2];
35613        col = [ P[6,3],255 ];
35614        if ("$outline"<=0,
35615          rectangle(#0,Xpp,Xnn,1,col);
35616        _(else),
35617          rectangle(#0,Xpp,Xnn,1,colo);
35618          rectangle(#0,Xpp + "$outline",Xnn - "$outline",1,col);
35619        );
35620        I"
35621
35622    elif $mode==1 # Sierpinski design
35623      f. ">
35624        P = I;
35625        Xpp = P[0,2];
35626        Xnn = P[2,2];
35627        Xnp = [ Xnn[0],Xpp[1] ];
35628        Xpn = [ Xpp[0],Xnn[1] ];
35629        col1 = [ P[6,3],255 ];
35630        col2 = [ P[6,3],64 ];
35631        T = [ P[9],P[10],P[10],P[11] ];
35632        eig = eig(T);
35633        angle = atan2(eig[5],eig[4])*180/pi;
35634        if ((angle>=0 && angle<90) || angle<-90,
35635          polygon(#0,3,Xpp,Xnp,Xnn,1,col1);
35636          polygon(#0,3,Xpp,Xnn,Xpn,1,col2);
35637        _(else),
35638          polygon(#0,3,Xnp,Xnn,Xpn,1,col1);
35639          polygon(#0,3,Xnp,Xpp,Xpn,1,col2)
35640        );
35641        I"
35642
35643    else # Ellipse painting
35644      f. ">
35645        begin(colo = [ 0,0,0,255 ]);
35646        P = I;
35647        Xpp = P[0,2];
35648        Xnn = P[2,2];
35649        Xcc = (Xpp + Xnn)/2;
35650        R = (Xnn[0] - Xpp[0])/2;
35651        col = [ P[6,3],255 ];
35652        r = "$radius2"*R;
35653        R*="$radius1";
35654        T = [ P[9],P[10],P[10],P[11] ];
35655        eig = eig(T);
35656        anisotropy = (1 + eig[1])/(1 + eig[0]);
35657        r*=anisotropy^"$anisotropy";
35658        angle = atan2(eig[5],eig[4])*180/pi;
35659        if ("$outline">0, ellipse(#0,Xcc,R,r,angle°,1,colo));
35660        ellipse(#0,Xcc,R - "$outline",r - "$outline",angle°,1,col);
35661        I"
35662      if !$outline sh[0] 100% if !im solidify[0] 10% fi fi
35663    fi
35664    k[0]
35665  endl done
35666  um _qt
35667
35668fx_quadtree_preview :
35669  gui_split_preview "fx_quadtree $*",${-3--1}
35670
35671#@gui Rodilius : fx_rodilius, fx_rodilius_preview(1)
35672#@gui : Amplitude = float(10,0,30)
35673#@gui : Thickness = float(10,0,100)
35674#@gui : Sharpness = float(300,0,1000)
35675#@gui : Orientations = int(5,2,36)
35676#@gui : Offset = float(30,0,180)
35677#@gui : Smoothness = int(0,0,5)
35678#@gui : Color Mode = choice(1,"Darker","Lighter")
35679#@gui : sep = separator()
35680#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
35681#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
35682#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
35683#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
35684#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
35685#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
35686#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
35687#@gui : Value Action = choice("None","Cut","Normalize")
35688#@gui : sep = separator()
35689#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35690#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35691#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35692#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35693#@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=RC07VUpzwGc")
35694#@gui : sep = separator()
35695#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Rod/GimpChat</i>.
35696#@gui :       Latest Update: <i>2013/05/03</i>.</small>")
35697fx_rodilius :
35698  ac "rodilius ${1-5,7} repeat $6 smooth 10,0,1,1,1,0.8,45 sharpen 30 done c 0,255",$8,$9
35699
35700fx_rodilius_preview :
35701  gui_split_preview "fx_rodilius $*",${-3--1}
35702
35703#@gui Shapeism : fx_shapeism, fx_shapeism_preview(0)+
35704#@gui : Shape = choice(2,"Squares","Triangles","Circles","Diamond","Hexagon","Octagon","Stars","Custom")
35705#@gui : Branches = int(7,3,16)
35706#@gui : Thickness = float(0.38,0,1)
35707#@gui : Angle = float(0,0,360)
35708#@gui : note = note("<small><b>Note:</b> Parameters <i>Branches</i>, <i>Thickness</i> and <i>Angle</i> are
35709#@gui : used only for <i>Custom</i> shapes.</small>")
35710#@gui : Antialiasing = bool(1)
35711#@gui : sep = separator()
35712#@gui : Scales = int(5,1,16)
35713#@gui : Maximal Size = int(32,1,256)
35714#@gui : Minimal Size = int(8,1,256)
35715#@gui : Allow Angle = choice(3,"0 deg.","180 deg.","90 deg.","Any")
35716#@gui : Spacing = int(1,-5,5)
35717#@gui : Precision = int(5,1,10)
35718#@gui : Edges = float(0.5,0,2)
35719#@gui : Smoothness = float(1,0,10)
35720#@gui : Background = color(0,0,0,255)
35721#@gui : sep = separator()
35722#@gui : url = link("Click here for a detailed description of this filter.",\
35723# "http://gimpchat.com/viewtopic.php?f=28&t=7500&sid=5b483979826903b8f8fc8fdaf1767dae")
35724#@gui : sep = separator()
35725#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/11/06</i>.</small>")
35726fx_shapeism :
35727  repeat $! l[$>]
35728    to_rgb
35729    +gradient_norm b. $13% ^. $12 quantize. $6,0,0
35730    100%,100%,1,2
35731    repeat $6
35732
35733      # Create map of possible locations.
35734      +channels[2] 100% +>[1] $> !=.. 0 -|[-2,-1] a[2,-1] c
35735
35736      # Create shape at given scale.
35737      size={if($6<=1,$7,$7+($8-$7)*$>/($6-1))}
35738      if $size<1 break fi
35739      if $5 {2*$size},{2*$size} _fx_shapeism$1. ${2-4} r2dy. $size
35740      else $size,$size _fx_shapeism$1. ${2-4}
35741      fi
35742      +!=. 0 expand_xy[-2,-1] 1,0 n[-2,-1] 0,1
35743      if $10<1 dilate. 3 fi
35744      . a[-3--1] c
35745
35746      # Pack sprites for given scale
35747      rprogress "pack_sprites[-2,-1] 1,100,$9,$10,$11",{$>*100/$6},{($>+1)*100/$6}
35748      channels. 0,1
35749
35750    done
35751
35752    rprogress 97
35753    rm[1]
35754    channels. 0 +!=. 0 blend[0,-1] shapeaverage0 *[1] 255 a c
35755    i[0] 100%,100%,1,4 fc[0] $14,$15,$16,$17
35756    blend alpha
35757    rprogress 100
35758  endl done
35759
35760fx_shapeism_preview :
35761  gui_print_preview ""
35762  50%,50% _fx_shapeism$1. ${2-4} frame. 1,1,0 >=. 50% n. 0,255 r. 100%,100%,1,4
35763  ri. [0],0,0,0.5,0.5 -|
35764
35765_fx_shapeism0 : # Square
35766  f 255 skip $*
35767
35768_fx_shapeism1 : # Triangle
35769  polygon 3,50%,0,0,100%,100%,100%,1,1 skip $*
35770
35771_fx_shapeism2 : # Circle
35772  shape_circle {w} rm.. skip $*
35773
35774_fx_shapeism3 : # Diamond
35775  shape_diamond {w} rm.. skip $*
35776
35777_fx_shapeism4 : # Hexagon
35778  star3d 3,1 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $*
35779
35780_fx_shapeism5 : # Octogon
35781  star3d 4,1 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $*
35782
35783_fx_shapeism6 : # Star
35784  star3d 5 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $*
35785
35786_fx_shapeism7 : # Custom
35787  star3d $1,$2 *3d. {0,min(w,h)/2} r3d. 0,0,1,$3 j3d[0] .,50%,50%,0,1,2,0 k[0]
35788
35789#@gui Sharp Abstract : fx_sharp_abstract, fx_sharp_abstract_preview(0)
35790#@gui : Spatial Scale = float(4,0,32)
35791#@gui : Value Scale = float(10,0,16)
35792#@gui : Precision = float(0.5,0,2)
35793#@gui : sep = separator()
35794#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
35795#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
35796#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
35797#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
35798#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
35799#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
35800#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
35801#@gui : sep = separator()
35802#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35803#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35804#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35805#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35806#@gui : sep = separator()
35807#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/09</i>.</small>")
35808fx_sharp_abstract :
35809  ac "rolling_guidance ${1-3}",$4
35810
35811fx_sharp_abstract_preview :
35812  gui_split_preview "fx_sharp_abstract $*",${-3--1}
35813
35814#@gui Sketch : fx_sketchbw, fx_sketchbw_preview(0)
35815#@gui : Number of Orientations = int(3,1,16)
35816#@gui : Starting Angle = float(45,0,180)
35817#@gui : Angle Range = float(180,0,180)
35818#@gui : Stroke Length = float(30,0,1000)
35819#@gui : Contour Threshold = float(1.75,0,10)
35820#@gui : Opacity = float(0.02,0,0.3)
35821#@gui : Background Intensity = float(0.5,0,2)
35822#@gui : Density = float(0.75,0,5)
35823#@gui : Sharpness = float(0.1,0,1)
35824#@gui : Anisotropy = float(0.7,0,1)
35825#@gui : Smoothness = float(3,0,10)
35826#@gui : Coherence = float(6,0,10)
35827#@gui : Boost Stroke = bool(0)
35828#@gui : Curved Stroke = bool(1)
35829#@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white",
35830#@gui : "White on transparent black","Color on white")
35831#@gui : sep = separator()
35832#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35833#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35834#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35835#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35836#@gui : sep = separator()
35837#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/05/11</i>.</small>")
35838fx_sketchbw :
35839  if $15==4 repeat $! l[$>] +sketchbw ${1-14} blend hardlight endl done return fi
35840  sketchbw ${1-14}
35841  if $15&1 negate fi
35842  if $15==2 r 100%,100%,1,4 repeat $! sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. done
35843  elif $15==3 r 100%,100%,1,4 repeat $! sh[$>] 3 *. 2 c. 0,255 rm. done
35844  fi
35845
35846fx_sketchbw_preview :
35847  gui_split_preview "fx_sketchbw $*",${-3--1}
35848
35849#@gui Smooth Abstract : fx_smooth_abstract, fx_smooth_abstract_preview(0)
35850#@gui : Smoothness (%) = float(75,0,100)
35851#@gui : Regularization = choice(0,"Isotropic","Delaunay-oriented","Edge-oriented")
35852#@gui : Regularization Iterations = int(20,0,100)
35853#@gui : Geometry = float(1,0,5)
35854#@gui : Details = float(30,0,50)
35855#@gui : sep = separator()
35856#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35857#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35858#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35859#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35860#@gui : sep = separator()
35861#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/06/04</i>.</small>")
35862fx_smooth_abstract :
35863  repeat $! l[$>] split_opacity l[0]
35864    srgb2rgb
35865    mM={[im,iM]} +b $4 n. $mM gradient_norm. <=. {50-$5}
35866    inpaint_pde[0] [1],$1%,$2,$3 rm.
35867    rgb2srgb c 0,255
35868  endl a c endl done
35869
35870fx_smooth_abstract_preview :
35871  gui_split_preview "fx_smooth_abstract $*",${-3--1}
35872
35873#@gui Stylize : fx_stylize,fx_stylize_preview
35874#@gui : Style = choice{0,"Custom Style (Top Layer)","Custom Style (Bottom Layer)",
35875#@gui : "Braque: Landscape near Antwerp",
35876#@gui : "Braque: Le Viaduc &agrave; l'Estaque",
35877#@gui : "Braque: Little Bay at La Ciotat",
35878#@gui : "Braque: The Mandola",
35879#@gui : "Christine Garner: Black Colour Pencil",
35880#@gui : "Christine Garner: Colour Pencil Sepia",
35881#@gui : "Christine Garner: Dark Coloured Pencil",
35882#@gui : "Christine Garner: Pencil",
35883#@gui : "Christine Garner: Sketching Pastel",
35884#@gui : "Christine Garner: Willow Charcoal",
35885#@gui : "Delaunay: Windows Open Simultaneously",
35886#@gui : "Delaunay: Portrait de Metzinger",
35887#@gui : "Hokusai: The Great Wave",
35888#@gui : "Kandinsky: Squares with Concentric Circles",
35889#@gui : "Kandinsky: Yellow-Red-Blue",
35890#@gui : "Klee: Death and Fire",
35891#@gui : "Klee: In the Style of Kairouan",
35892#@gui : "Klee: Oriental Pleasure Garden Anagoria",
35893#@gui : "Klee: Polyphony 2",
35894#@gui : "Klee: Red waistcoat",
35895#@gui : "Klimt: The Kiss",
35896#@gui : "Mondrian: Composition in Red-Yellow-Blue",
35897#@gui : "Mondrian: Evening; Red Tree",
35898#@gui : "Mondrian: Gray Tree",
35899#@gui : "Monet: San Giorgio Maggiore at Dusk",
35900#@gui : "Monet: Water-Lily Pond",
35901#@gui : "Monet: Wheatstacks - End of Summer",
35902#@gui : "Munch: The Scream",
35903#@gui : "Picabia: Udnie",
35904#@gui : "Picasso: Les Demoiselles d'Avignon",
35905#@gui : "Picasso: Seated Woman",
35906#@gui : "Picasso: The Reservoir - Horta de Ebro",
35907#@gui : "Pollock: Convergence",
35908#@gui : "Pollock: Summertime Number 9A",
35909#@gui : "Van Gogh: Almond Blossom",
35910#@gui : "Van Gogh: Irises",
35911#@gui : "Van Gogh: The Starry Night",
35912#@gui : "Van Gogh: Wheat Field with Crows"}
35913#@gui : Scale Style to Fit Target Resolution = choice(5,"No rescaling","10%","20%","30%","50%","75%","100%","150%",
35914#@gui : "200%","250%","300%")
35915#@gui : Style Variations = choice("None","All XY-flips","All 90&deg; rotations","All 45&deg; rotations")
35916#@gui : Preview Progression While Running = bool(1)
35917#@gui : sep = separator(), note = note{"<small><b><span color="blue">Style/Target Parameters:</span></b></small>"}
35918#@gui : Fidelity to Target (Finest) = float(0.5,0,5)
35919#@gui : Fidelity to Target (Coarsest) = float(2,0,5)
35920#@gui : Fidelity Smoothness (Finest) = float(3,0,5)
35921#@gui : Fidelity Smoothness (Coarsest) = float(0.5,0,5)
35922#@gui : Fidelity Chromaticity = float(0.1,0,1)
35923#@gui : sep = separator(), note = note{"<small><b><span color="blue">Image Matching Parameters:</span></b></small>"}
35924#@gui : Match Colors With = choice(3,"Nothing","Gamma Balance","Histogram Transfer","PCA transfer")
35925#@gui : Colorspace = choice{3,"sRGB","Linear RGB","YCbCr","YCbCr (Luma/Chroma),"YCbCr (Luma Only)",
35926#@gui : "YCbCr (Chroma Only)","Lab","Lab (Luma/Chroma)","Lab (Luma Only)","Lab (Chroma Only)"}
35927#@gui : Keep Color Channels = choice{"All","Luminance Only (YCbCr)","Luminance Only (Lab)","Chrominances Only (CbCr)",
35928#@gui : "Chrominances Only (ab)"}
35929#@gui : Smoothness = float(0.7,0,5)
35930#@gui : Also Match Gradients = float(1,0,5)
35931#@gui : sep = separator(), note = note{"<small><b><span color="blue">Advanced Parameters:</span></b></small>"}
35932#@gui : Init. Type = choice("Best Match","Identity","Randomized")
35933#@gui : Init. Resolution = choice(1,"8px","16px","32px","64px","128px","256px")
35934#@gui : Init. With High Gradients Only = float(0,0,100)
35935#@gui : Patch Size for Analysis = int(5,2,16)
35936#@gui : Patch Size for Synthesis = int(5,2,16)
35937#@gui : Patch Size for Synthesis (Final) = int(7,2,16)
35938#@gui : Number of Matches (Finest) = int(1,0,10)
35939#@gui : Number of Matches (Coarsest) = int(30,0,200)
35940#@gui : Penalize Patch Repetitions = int(10,0,300)
35941#@gui : Matching Precision (Smaller is Faster) = float(2,0,10)
35942#@gui : Scale Factor = float(1.85,1.1,4)
35943#@gui : Skip Finest Scales = int(0,0,3)
35944#@gui : sep = separator()
35945#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/03/23</i>.</small>")
35946fx_stylize :
35947  init_resolution={arg(1+$16,8,16,32,64,128,256)}
35948
35949  # Define image matching function
35950  colorspace=${"arg 1+$11,all,lrgb,ycbcr,ycbcr,ycbcr_y,ycbcr_cbcr,lab,lab,lab_l,lab_ab"}
35951  match_colors="s c,-3 mv[1] 3 b[-2,-1] 1% negate[-2,-1] n[-2,-1] 0,1"
35952  luma,chroma=
35953  if $11==3" || "$11==7 # Chroma/Luma conversion for YCbCr or Lab
35954    luma=_${"arg 1+($11==7),y,l"}
35955    chroma=_${"arg 1+($11==7),cbcr,ab"}
35956  fi
35957  if $10==1
35958    match_colors=$match_colors" ac[0,1] \"balance_gamma ,\","$colorspace$luma
35959    if narg($chroma) match_colors=$match_colors" ac[0,1] \"balance_gamma ,\","$colorspace$chroma fi
35960  elif $10==2
35961    match_colors=$match_colors" transfer_histogram[0] [1],256,"$colorspace$luma
35962    if narg($chroma) match_colors=$match_colors" transfer_histogram[0] [1],256,"$colorspace$chroma fi
35963  elif $10==3
35964    match_colors=$match_colors" transfer_pca[0] [1],"$colorspace$luma" c[0] 0,255"
35965    if narg($chroma) match_colors=$match_colors" transfer_pca[0] [1],"$colorspace$chroma" c[0] 0,255" fi
35966  fi
35967  if $12==1 match_colors=$match_colors" rgb2ycbcr[0,1] sh[0,1] 1,2 f[-2,-1] 128 rm[-2,-1] ycbcr2rgb[0,1]"
35968  elif $12==2 match_colors=$match_colors" srgb2lab[0,1] sh[0,1] 1,2 f[-2,-1] 0 rm[-2,-1] lab2srgb[0,1]"
35969  elif $12==3 match_colors=$match_colors" rgb2ycbcr[0,1] sh[0,1] 0 f[-2,-1] 128 rm[-2,-1] ycbcr2rgb[0,1]"
35970  elif $12==4 match_colors=$match_colors" srgb2lab[0,1] sh[0,1] 0 f[-2,-1] 50 rm[-2,-1] lab2srgb[0,1]"
35971  fi
35972  match_colors=$match_colors\
35973              " b[0,1] xy,$13 repeat 2 l[$>] s c n 0,255 a c endl done *[-2,-1] {$14*255} a[0,-2] c a[1,-1] c"
35974
35975  patch_penalization=$23
35976
35977  # Insert style image at the end of the list.
35978  if $1<2 # Custom style (top or bottom layer)
35979    if $!<2 error "At least two layers are required in this mode." fi
35980    ind_first={$1==0?1:0} ind_style={$1==0?0:-1} N={$!-1} sh[$ind_style]
35981  else
35982    ind_first=0 N=$! ind_style= _fx_stylize {$1-2}
35983  fi
35984
35985  # Process layers.
35986  is_window=0
35987  repeat $N l[{$ind_first+$>},-1] nm={0,n}
35988    if $2 +rr2d[1] {0,[w,h]*arg($2,0.1,0.2,0.3,0.5,0.75,1,1.5,2,2.5,3)},1,2 fi
35989    if $3==1 . +mirror. x +mirror[-2,-1] y a[-4--1] z
35990    elif $3==2 +l. r {u={max(w,h)};[u,u]},1,100%,0,3,0.5,0.5 repeat 3 +rotate[0] {90*$>},1,3,50%,50% done a z endl
35991    elif $3==3 +l. r {u={max(w,h)};[u,u]},1,100%,0,3,0.5,0.5 repeat 7 +rotate[0] {45*$>},1,3,50%,50% done a z endl
35992    fi
35993    if $4 wsiz=${"fitscreen "{0,[w,h]}} w[0] $wsiz is_window={*} fi
35994    stylize[0] .,${5-9},$15,$init_resolution,${17-22},$patch_penalization,${24-26},$match_colors
35995    k[0,1] nm[0] $nm
35996    if $is_window" && "!{*} break fi
35997  endl done
35998  rm. # Remove style image
35999  if 0$_output_mode
36000    if narg($ind_style) rm[$ind_style] fi # Do not duplicate style layer if output mode -> new layers
36001    if $is_window" && "!{*} rm fi # In case user closed the window, does not output new layers.
36002  fi
36003
36004fx_stylize_preview :
36005  if $1<2 # Custom style (top or bottom layer)
36006    if $!<2 gui_warning_preview "At least two layers are required when specifying a custom style." return fi
36007    ind_first={$1==0?1:0} ind_style={$1==0?0:-1} N={$!-1} sh[$ind_style]
36008  else
36009    ind_first=0 N=$! ind_style= _fx_stylize {$1-2}
36010  fi
36011  repeat $N l[{$ind_first+$>},-1]
36012    gui_no_preview[0] ,
36013    +rr2d[1] {0,[w,h]*2/3},0,2 frame. 1,1,255 frame. 1,1,0,0,0,255 j[0] .,1%,30 rm. to[0] "Style:",1%,0,24
36014  endl done rm.
36015  if narg($ind_style) rm[$ind_style] fi
36016
36017_fx_stylize : # Download pre-defined style image
36018  if isnum($1)
36019    name=${"arg 1+$1",\
36020           "landscapenearantwerp,leviaducalestaque,littlebayatlaciotat,themandola",\
36021            "blackcolourpencil,colourpencilsepia,darkcolouredpencil,pencil,sketchingpastel,willowcharcoal",\
36022            "windowsopensimultaneously,portraitdemetzinger,greatwave,squareswithconcentriccircles,yellowredblue",\
36023            "deathandfire,inthestyleofkairouan,orientalpleasuregardenanagoria,polyphony2,redwaistcoat,thekiss",\
36024            "compositionredyellowblue,redtree,graytree,sangiorgiomaggioreatdusk,waterlilypond,wheatstacksendofsummer",\
36025            "scream,udnie,lesdemoisellesdavignon,seatedwoman,reservoirhortadeebro,convergence,summertime9a",\
36026            "almondblossom,irises,starrynight,wheatfieldwithcrows"}
36027  else name="$1"
36028  fi
36029  filename=${-path_cache}style_$name.png
36030  if isfile(['{/$filename}']) $filename
36031  else i https://gmic.eu/img/style_$name.png o. $filename
36032  fi
36033
36034#@gui Vector Painting : fx_vector_painting, fx_vector_painting_preview(1)
36035#@gui : Details = float(9,0,10)
36036#@gui : sep = separator()
36037#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36038#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36039#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36040#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36041#@gui : sep = separator()
36042#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.\nLatest Update: <i>2015/25/08</i>.</small>")
36043fx_vector_painting :
36044  repeat $! l[$>]
36045    +luminance b. {10-$1}%,1,1
36046    f. "dmax = -1; nmax = 0;
36047        for (n = 0, ++n<=8,
36048          p = arg(n,-1,0,1,-1,1,-1,0,1);
36049          q = arg(n,-1,-1,-1,0,0,1,1,1);
36050          d = (j(p,q,0,0,0,1) - i)^2;
36051          if (d>dmax,dmax = d; nmax = n,nmax)
36052        )"
36053    blend shapeaverage
36054  endl done
36055
36056fx_vector_painting_preview :
36057  gui_split_preview "fx_vector_painting $*",${-3--1}
36058
36059#@gui Warhol : warhol, warhol(1)
36060#@gui : X-Tiles = int(3,1,10)
36061#@gui : Y-Tiles = int(3,1,10)
36062#@gui : Smoothness = float(2,0,10)
36063#@gui : Color = float(40,0,60)
36064#@gui : sep = separator()
36065#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36066
36067#@gui Whirl Drawing : fx_draw_whirl, fx_draw_whirl_preview(0)
36068#@gui : Amplitude = float(20,0,100)
36069#@gui : sep = separator()
36070#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36071#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36072#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36073#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36074#@gui : sep = separator()
36075#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36076fx_draw_whirl :
36077  repeat $! l[$>] split_opacity draw_whirl[0] $* a c endl done
36078
36079fx_draw_whirl_preview :
36080  gui_split_preview "fx_draw_whirl $*",${-3--1}
36081
36082#@gui ____<b>Black & White</b>
36083#-------------------------------
36084
36085#@gui Black & White : fx_blackandwhite, fx_blackandwhite_preview(1)+
36086#@gui : Red Level = float(0.299,0,1)
36087#@gui : Red Smoothness = float(0,0,10)
36088#@gui : Green Level = float(0.587,0,1)
36089#@gui : Green Smoothness = float(0,0,10)
36090#@gui : Blue Level = float(0.114,0,1)
36091#@gui : Blue Smoothness = float(0,0,10)
36092#@gui : sep = separator()
36093#@gui : Brightness (%) = float(0,-100,100)
36094#@gui : Contrast (%) = float(0,-100,100)
36095#@gui : Gamma (%) = float(0,-100,100)
36096#@gui : Hue (%) = float(0,-100,100)
36097#@gui : Saturation (%) = float(0,-100,100)
36098#@gui : sep = separator()
36099#@gui : Grain (Shadows) = float(0,0,200)
36100#@gui : Grain (Midtones) = float(0,0,200)
36101#@gui : Grain (Highlights) = float(0,0,200)
36102#@gui : Grain Tone Fading = float(2,0,10)
36103#@gui : Grain Scale = float(0,0,3)
36104#@gui : Grain Type = choice("Gaussian","Uniform","Salt and Pepper","Poisson")
36105#@gui : sep = separator()
36106#@gui : Local Contrast = float(0,0,60)
36107#@gui : Radius = int(16,1,512)
36108#@gui : Contrast Smoothness = float(4,0,10)
36109#@gui : sep = separator()
36110#@gui : Pseudo-Gray Dithering = int(0,0,5)
36111#@gui : Use Maximum Tones = bool(false)
36112#@gui : sep = separator()
36113#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36114#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36115#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36116#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36117#@gui : sep = separator()
36118#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/20/02</i>.</small>")
36119fx_blackandwhite :
36120  repeat $!
36121    l. split_opacity rv to_rgb. s. c      # Isolate opacity
36122    *... $1 b... $2%                      # Red level + smoothness
36123    *.. $3 b.. $4%                        # Green level + smoothness
36124    *. $5 b. $6%                          # Blue level + smoothness
36125    +[-3--1] /. {$1+$3+$5} c. 0,255       # (R,G,B) -> B&W
36126    adjust_colors ${7-11},0,255
36127    if $12||$13||$14
36128      100%,100% [-1]x2                          # Create noise for shadows, midtones and highlights.
36129      noise... 100,$17 b... $16% n... -$12,$12  # Scaled grain on shadows.
36130      noise.. 100,$17 b.. $16% n.. -$13,$13     # Scaled grain on midtones.
36131      noise. 100,$17 b. $16% n. -$14,$14        # Scaled grain on highlights.
36132      +tones[-4] 3 b[-3--1] $15%                # Get smoothed tones.
36133      *[-6,-3] *[-4,-2] *[-2,-1]                # Get noisy tones.
36134      +[-4--1] c. 0,255                         # Compose them with the B&W image.
36135    fi
36136    rv a c endl mv. 0 done                      # Re-compose opacity and loop to next image.
36137  if $18 normalize_local $18,$19,$20,2%,1,0,255 fi
36138  if $22 n 0,255 fi
36139  if $21 to_pseudogray $21,1 fi
36140
36141fx_blackandwhite_preview :
36142  gui_split_preview "fx_blackandwhite $*",${-3--1}
36143
36144#@gui B&W Stencil : fx_stencilbw, fx_stencilbw_preview(0)
36145#@gui : Threshold = float(10,0,30)
36146#@gui : Smoothness = float(10,0,30)
36147#@gui : Hue = float(0,0,360)
36148#@gui : Saturation = float(0,0,1)
36149#@gui : sep = separator()
36150#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36151#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36152#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36153#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36154#@gui : sep = separator()
36155#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36156fx_stencilbw :
36157  stencilbw $1,$2
36158  if $3||$4 repeat $! l[$>] split_opacity
36159    /[0] 255 i[0] 100%,100%,1,1,$4 i[0] 100%,100%,1,1,$3 a[0-2] c hsv2rgb[0]
36160  a c endl done fi
36161
36162fx_stencilbw_preview :
36163  gui_split_preview "fx_stencilbw $*",${-3--1}
36164
36165#@gui Charcoal : fx_charcoal, fx_charcoal_preview(0)
36166#@gui : Granularity = int(65,0,800)
36167#@gui : Lowlights Crossover Point = int(70,0,255)
36168#@gui : Highlights Crossover Point = int(170,0,255)
36169#@gui : Boost Contrast = bool(0)
36170#@gui : Resize Image for Optimum Effect = bool(1)
36171#@gui : Add Chalk Highlights = bool(0)
36172#@gui : Minimal Highlights = int(50,0,255)
36173#@gui : Maximal Highlights = int(70,0,255)
36174#@gui : Background Color = color(255,255,255)
36175#@gui : Foreground Color = color(0,0,0)
36176#@gui : Invert Background / Foreground = bool(0)
36177#@gui : sep = separator()
36178#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36179#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36180#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36181#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36182#@gui : sep = separator()
36183#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/17/03</i>.</small>")
36184#@gui : note = note("<small>Inspired from the Charcoal script by <i>micomicon</i> :</small>")
36185#@gui : url = link("http://registry.gimp.org/node/25078")
36186fx_charcoal :
36187  repeat $! l[$>] split_opacity l[0]
36188    compose_channels max
36189    w={w} h={h}
36190    if $5 r. 150%,150%,1,1,6 fi
36191    if $4 equalize. n. 0,255 fi
36192    sharpen {$1*3} cut 0,255
36193    if $6 +ir $7,$8 fi  # Add highlights layer if required.
36194    ir[0] $2,$3
36195    if !$15 ==[0] 0 fi
36196    -|
36197    +*[0] $10 +*[0] $11 *[0] $9
36198    a[-3--1] c replace_color 0,0,0,0,0,$12,$13,$14
36199    r $w,$h,1,100%,2
36200  endl a c endl done
36201
36202fx_charcoal_preview :
36203  gui_split_preview "fx_charcoal $*",${-3--1}
36204
36205#@gui Colorize [Interactive] : fx_colorize_interactive, fx_colorize_interactive_preview
36206#@gui : Input Type = _choice("B&W Photograph","Lineart")
36207#@gui : Output Type = _choice{"Colorized Image (1 Layer)","Colors Only (1 Layer)","Image + Colors (2 Layers)",
36208#@gui : "Image + Colors (Multi-Layers)"}
36209#@gui : View Resolution = _choice{1,"Small (Faster)","Medium","High (Slower)","Very High (Even Slower)"}
36210#@gui : 1st Additional Palette (.gpl) = _filein()
36211#@gui : 2nd Additional Palette (.gpl) = _filein()
36212#@gui : Image to Grab Color from (.png) = _filein()
36213#@gui : sep = separator()
36214#@gui : note = note{"<small><b>Description:</b>\n
36215#@gui : This filter allows to quickly colorize a B&W image or lineart.
36216#@gui : Click on the <i>Apply</i> or <i>OK</i> buttons below to open the G'MIC interactive window and
36217#@gui : start adding color control points. When you're done, exit the interactive window: your colored
36218#@gui : result will be transferred back to the host software.\n\n
36219#@gui : If you are not satisfied with the result, <i>Undo it (CTRL+Z)</i>, and click on <i>Apply</i> once
36220#@gui : again to modify your control points defined previously.
36221#@gui : To clear all control points, click on the <i>Reset</i> button above.
36222#@gui : </small>"}
36223#@gui : Clear Control Points = button(0.5)
36224#@gui : Last Image Size = value(0,0)
36225#@gui : Control Points = value(-1)
36226#@gui : sep = separator()
36227#@gui : note = note{"<small><b>Interactions:</b>\n
36228#@gui : Use the following actions in the interactive window to manage your colorization :\n\n
36229#@gui : - <b>Left mouse button</b> creates a new color control point (or move an existing one).\n
36230#@gui : - <b>Right mouse button</b> or key <b>X</b> over a control point deletes it.\n
36231#@gui : - <b>Right mouse button</b> or key <b>P</b> anywhere else picks a color from the image.\n
36232#@gui : - <b>Mouse wheel</b>, or keys <b>CTRL+arrows up/down</b> zoom view in/out.\n
36233#@gui : - <b>CTRL+mouse wheel</b>, <b>SHIFT+wheel</b> or arrow keys move image in zoomed view.\n
36234#@gui : - Key <b>SPACE</b> updates the extrapolated color field.\n
36235#@gui : - Key <b>TAB</b> toggles markers view modes.\n
36236#@gui : - Key <b>BACKSPACE</b> deletes the last control point added.\n
36237#@gui : - Key <b>PAGE UP</b> increases image contrast.\n
36238#@gui : - Key <b>PAGE DOWN</b> decreases image contrast.\n
36239#@gui : - Key <b>R</b> enters/exits color replace mode.\n
36240#@gui : - Keys <b>CTRL+D</b> increase window size.\n
36241#@gui : - Keys <b>CTRL+C</b> decrease window size.\n
36242#@gui : - Keys <b>CTRL+R</b> resets window size.\n
36243#@gui : - Keys <b>ESC</b>, <b>Q</b> or <b>ENTER</b> exit the interactive window.
36244#@gui : </small>"}
36245#@gui : sep = separator()
36246#@gui : note = note("<small>You can find more information on how to use this filter here :</small>")
36247#@gui : url = link("David Revoy's G'MIC Colorization Page",
36248#@gui : "http://www.davidrevoy.com/article240/gmic-line-art-colorization")
36249#@gui : sep = separator()
36250#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/12/07</i>.</small>")
36251fx_colorize_interactive : skip "${4=},${5=},${6=}"
36252  N=$! nm={n}
36253  resolution={arg(1+$3,512,1024,2048,0)}
36254  nm "[G"{`39`}"MIC] Colorize"
36255  if [$9][0]==-1" || "[$8]!=[w,h] _gui_control_points= else _gui_control_points=$9 fi
36256  N=$!
36257
36258  arg_palette1=0 l[]
36259    0 nm. "$4" ext={x} rm
36260    if same(['$ext'],'gpl',-1,0)
36261      input_gpl "$4" arg_palette1=1 fi
36262  onfail rm endl
36263  if $arg_palette1 arg_palette1=[{$!-1}] fi
36264
36265  arg_palette2=0 l[]
36266    0 nm. "$5" ext={x} rm
36267    if same(['$ext'],'gpl',-1,0)
36268      input_gpl "$5" arg_palette2=1 fi
36269  onfail rm endl
36270  if $arg_palette2 arg_palette2=[{$!-1}] fi
36271
36272  arg_grabber=0 l[]
36273    0 nm. "$6" ext={x} rm
36274    i "$6" to_rgb arg_grabber=1
36275  onfail rm endl
36276  if $arg_grabber arg_grabber=[{$!-1}] fi
36277
36278  repeat $N
36279    status=${x_colorize[$>]\ $1,$resolution,$2,$arg_palette1,$arg_palette2,$arg_grabber}
36280  done
36281  k[0-{$N-1}]
36282
36283  if $2==1 repeat $! l[$<] # Output : colors only (1 layer).
36284      channels {s-3},{s-1}
36285    endl done
36286  elif $2>=2 repeat $! l[$<] # Output : Lineart + Colors (2 layers).
36287      +channels {s-3},{s-1} channels.. 0,{0,s-4}
36288    endl done
36289    if $2>=3 split_colors[1] 0,256,8 fi # Split colors into layers.
36290  fi
36291  nm $nm
36292  if !narg($status) status=-1 fi
36293  u \{$1\}\{$2\}\{$3\}\{"$4"\}\{"$5"\}\{"$6"\}\{0\}\{{w},{h}\}\{$status\}
36294
36295fx_colorize_interactive_preview : skip "${4=},${5=},${6=}"
36296  if $7  # Clear control points.
36297    gui_print_preview "No preview\n  available",,"(Control points cleared)"
36298    u \{$1\}\{$2\}\{$3\}\{"$4"\}\{"$5"\}\{"$6"\}\{0\}\{{w},{h}\}\{-1\}
36299  else gui_no_preview ,
36300  fi
36301
36302#@gui Colorize [Photographs] : fx_recolorize, fx_recolorize_preview(1)
36303#@gui : Smoothness = int(2,0,6)
36304#@gui : Anisotropy = float(0.2,0,1)
36305#@gui : Output Mode = choice("Merge Brightness / Colors","Split Brightness / Colors")
36306#@gui : sep = separator()
36307#@gui : note = note{"<small><b>Note:</b> This filter needs two layers to work properly.
36308#@gui : The bottom layer must be a B&W image, while the top layer contains color patches that will
36309#@gui : be extrapolated in a smart way (edge-directed) to fill the entire image. At the end,
36310#@gui : you get a completely recolored image.</small>"}
36311#@gui : sep = separator()
36312#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/16/01</i>.</small>")
36313fx_recolorize :
36314  repeat int($!/2)
36315    if $3 s=$>,{$>+1} else s={2*$>},{2*$>+1} fi
36316    l[$s] rv[0,1]
36317      channels[0] 0 to_rgb.. # Convert to pure gray.
36318      to_rgba. split_opacity. !=. 0  # Retrieve mask of color patches.
36319      srgb2rgb[-3,-2] rgb2lab8[-3,-2] channels... 0 channels.. 1,2 # Now, list is [0]=lightness / [1]=chroma / [2]=mask.
36320      +.. 1 *.. . +gradient_norm... *. -1 watershed... . rm. -.. 1 # Get first estimate for the color interpolation.
36321      +diffusiontensors... $2,1,0.5,0.5 ==.. 0 *. .. rm..
36322      smooth.. .,{$1*80},0.8,60 rm.
36323      a[-2,-1] c lab82rgb. rgb2srgb.
36324      if $3 rgb2hsv. s. c i[2] 100%,100%,1,1,1 i[3] 100%,100%,1,2,0 a[0-2] c a[^0] c hsv2rgb rv fi
36325    endl
36326  done
36327
36328fx_recolorize_preview :
36329  fx_recolorize $* a x
36330
36331#@gui Colorize [with Colormap] : fx_bwrecolorize, fx_bwrecolorize_preview
36332#@gui : Brightness (%) = float(0,-100,100)
36333#@gui : Contrast (%) = float(0,-100,100)
36334#@gui : Gamma (%) = float(0,-100,100)
36335#@gui : Normalize Input = bool(0)
36336#@gui : sep = separator()
36337#@gui : Gradient Preset = choice("User-Defined","Black to White","White to Black","Sepia","Solarize")
36338#@gui : Interpolation Type = choice(1,"Nearest","Linear","Cubic","Lanczos")
36339#@gui : Preserve Initial Brightness = bool(0)
36340#@gui : sep = separator()
36341#@gui : note = note("<small><u>User-defined gradient :</u></small>")
36342#@gui : Number of Tones = int(5,2,8)
36343#@gui : 1st Tone = color(0,0,0,255)
36344#@gui : 2nd Tone = color(43,25,55,255)
36345#@gui : 3rd Tone = color(158,137,189,255)
36346#@gui : 4th Tone = color(224,191,228,255)
36347#@gui : 5th Tone = color(255,255,255,255)
36348#@gui : 6th Tone = color(255,255,255,255)
36349#@gui : 7th Tone = color(255,255,255,255)
36350#@gui : 8th Tone = color(255,255,255,255)
36351#@gui : sep = separator()
36352#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36353#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36354#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36355#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36356#@gui : sep = separator()
36357#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36358fx_bwrecolorize :
36359  remove_opacity
36360  if $4 n 0,255 fi
36361  if $5==0   # User-defined gradient
36362    (${9--2}) r. 4,$8,1,1,-1 permute. yzcx
36363  elif $5==1 # Black to white
36364    (0,255^0,255^0,255^255,255)
36365  elif $5==2 # White to black
36366    (255,0^255,0^255,0^255,255)
36367  elif $5==3 # Sepia
36368    (0,44,115,143,196,244^0,20,84,119,184,235^0,5,44,73,144,200^255,255,255,255,255,255)
36369  else       # Solarize
36370    (0,359^1,1^1,1^255,255) r. 256,1,1,4,3 sh. 0,2 hsv2rgb. rm.
36371  fi
36372  if $6==0 r. 256,1,1,4,1
36373  elif $6==1 r. 256,1,1,4,3
36374  elif $6==2 r. 256,1,1,4,5 c. 0,255
36375  else r. 256,1,1,4,6
36376  fi
36377  if $7==1 sh. 0,2 rgb2hsv. sh. 2 f. x/w hsv2rgb.. rm[-2,-1] fi
36378  l[^-1] luminance adjust_colors ${1-3} endl map[^-1] . rm.
36379
36380fx_bwrecolorize_preview :
36381  gui_split_preview "fx_bwrecolorize ${^0}",${-3--1}
36382
36383#@gui Colorize Lineart [Auto-Fill] : fx_autofill_lineart, fx_autofill_lineart_preview(0)
36384#@gui : Contour Threshold (%) = float(90,0,100)
36385#@gui : Contour Normalization = bool(1)
36386#@gui : Minimal Region Area = int(8,0,256)
36387#@gui : Tolerance to Gaps = int(0,0,10)
36388#@gui : Preview Type = choice("Lineart + Colors","Colors Only")
36389#@gui : sep = separator()
36390#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/12/11</i>.</small>")
36391fx_autofill_lineart :
36392  repeat $! l[$>] nm=${-gui_layer_name}
36393
36394    # Format input lineart to expected format.
36395    is_alpha={s==2||s==4}
36396    if $is_alpha sh 100% is_alpha={im<128&&iM>128} rm. fi # Check is alpha-channel contains lineart
36397    if $is_alpha +channels 100% negate.
36398    else +norm fi
36399    n. 0,255
36400    l.
36401
36402      # Start multi-scale filling.
36403      repeat 1+$4
36404        fact={2^-$<}
36405        nw={0,max(1,round(w*$fact))}
36406        nh={0,max(1,round(h*$fact))}
36407        +r[0] $nw,$nh,1,1,2 if $2 normalize_local. , fi +>=. {min(99.5,$1)}%
36408
36409        if $colors
36410          scale2x[colors] r[colors] .,1 *[colors,-1] rv[-2,-1]
36411        fi
36412
36413        label_fg. 0,1 b.. 0.8 watershed. .. rm.. nm. colors
36414      done
36415
36416      # Inpaint regions that are too small.
36417      +area. 0,1 label_maxarea={"P=[xM,yM];i(#-2,P)-1"}
36418      if $3>1
36419        >. {$3*sqrt($3)} *[-2,-1]
36420        b.. 0.8 watershed. ..
36421      else
36422        rm.
36423      fi
36424      rm..
36425
36426      # Colorize regions.
36427      N={iM}
36428      if $N
36429        -. 1
36430        srand 0 {iM+1},1,1,3,">[j(-1) + u(135,225),u(0,0.7),u(0.4,0.9)]"
36431        hsv2rgb. round. point. $label_maxarea,0,0,1,255
36432        map.. . rm.
36433      fi
36434    endl
36435
36436    # Format output layers.
36437    if !$is_alpha gui_set_layer_mode.. multiply fi
36438    gui_set_layer_name. $nm" [colors]"
36439  endl done
36440
36441fx_autofill_lineart_preview :
36442  repeat $! l[$>]
36443    fx_autofill_lineart ${1-4}
36444    if $5 k. else rv blend multiply fi
36445  endl done
36446
36447#@gui Colorize Lineart [Smart Coloring] : fx_colorize_lineart_smart, fx_colorize_lineart_smart_preview(0)
36448#@gui : Colorize Mode = choice("Generate Random-Colors Layer","Extrapolate Color Spots on Transparent Top Layer",
36449#@gui : "Auto-Clean Bottom Color Layer")
36450#@gui : sep = separator()
36451#@gui : note = note{"<small><b>Global geometry parameters:</b></small>"}
36452#@gui : Contour Detection (%) = float(95,0,100)
36453#@gui : Discard Contour Guides = bool(0)
36454#@gui : note = note{"<small>Add strokes with a saturated color having value 255 (e.g. pure red) on your lineart
36455#@gui : allows to guide the colorization algorithm with virtual contours.</small>"}
36456#@gui : Output Region Delimiters = _bool(0)
36457#@gui : sep = separator()
36458#@gui : note = note{"<small><b>For <i>Random colors</i> mode only:</b></small>"}
36459#@gui : Make Hue Depends on Region Size = float(1,0,1)
36460#@gui : Maximal Color Saturation = int(24,0,255)
36461#@gui : Minimal Color Intensity = int(200,0,255)
36462#@gui : sep = separator()
36463#@gui : note = note{"<small><b>For <i>color spots</i> mode only:</b></small>"}
36464#@gui : Color Shading (%) = int(0,0,100)
36465#@gui : sep = separator()
36466#@gui : note = note{"<small><b>Connection parameters:</b></small>"}
36467#@gui : End Point Rate (%) = float(75,0,100)
36468#@gui : End Point Connectivity = int(2,1,5)
36469#@gui : Spline Max Length (px) = float(60,0,256)
36470#@gui : Segment Max Length (px) = float(20,0,256)
36471#@gui : Spline Max Angle (deg) = float(90,0,180)
36472#@gui : Spline Roundness = float(1,0,2)
36473#@gui : Minimal Region Area = float(10,0,100)
36474#@gui : Allow Self Intersections = bool(1)
36475#@gui : sep = separator()
36476#@gui : Preview Type = choice(0,"Colored geometry","Colored regions","Colored lineart")
36477#@gui : sep = separator()
36478#@gui : note = note("<small>Authors: <i>David Tschumperlé</i>, <i>Sébastien Fourey</i> and
36479#@gui : <i>David Revoy</i>.      Latest Update: <i>2018/11/09</i>.</small>")
36480fx_colorize_lineart_smart :
36481  _fx_colorize_lineart_smart $*,-1 round
36482
36483fx_colorize_lineart_smart_preview :
36484  if $1==1" && "$!<2 gui_warning_preview "A top layer with color spots is missing, for this colorization mode." return
36485  elif $1==2" && "$!<2 gui_warning_preview "A bottom color layer is missing, for this colorization mode." return
36486  fi
36487  _fx_colorize_lineart_smart ${1-3},0,${5--1}
36488
36489_fx_colorize_lineart_smart :
36490  if $1==1" && "$!<2 error "A top layer with color strokes is missing, for this colorization mode."
36491  elif $1==2" && "$!<2 error "A bottom color layer is missing, for this colorization mode."
36492  fi
36493  min_color_area={$15^2}
36494
36495  repeat $1?1:$! inds=${arg\ 1+!!$1,$<,0--1} l[$inds]
36496
36497    ind_lineart={$1==1?1:0}
36498    nm=${gui_layer_name[$ind_lineart]} nm[$ind_lineart] lineart
36499
36500    if $!>1 ind_colors={$1==1?0:1} nmc={$ind_colors,n} nm[$ind_colors] colors fi
36501    if $!>=3 rm[2--1] fi # Delete old color and added contour layers if any
36502
36503    # Constrain input lineart to be a binary, single channel image.
36504    [lineart]
36505    is_alpha={s==2||s==4}
36506    if $is_alpha sh. 100% is_alpha={iM-im>64} rm. fi
36507    if $is_alpha channels. 100%
36508    else luminance. negate. fi
36509    >. {255*(1-$2%)}
36510    nm. strokes
36511
36512    # Retrieve closed binary shape.
36513    _keep_keycoords={$-1==0}
36514    [strokes] close_binary. ${9-14},$min_color_area,$16 nm. new_strokes
36515
36516    if $-1==0 # Render estimated geometry
36517      +negate[strokes] *. 255 to_rgb.
36518      +-[new_strokes] [strokes] dilate. 2
36519      100%,100%,1,3,[0,128,255] j... .,0,0,0,0,1,.. rm[-2,-1]
36520      if narg($keycoords) f[keycoords] "ellipse(#-1,(I)[0,2],3,3,0,1,[255,0,0]);I" rm[keycoords] fi
36521      nm. geometry
36522    fi
36523
36524    # Label color regions and inpaint strokes.
36525    if $1==1 # Mode: Color spots-guided colorization
36526      to_rgba[$colors] [colors],[colors]
36527      f[colors] "i(#-1) = A<255?0:norm(R,G+0.3,B+0.6);I"
36528      label_fg. 0 {1+iM},1,1,{colors,s+1}
36529      f.. ">I[#-1,i]+=[ I(#"$colors"),1 ];I"
36530      s. c,{-s+1} /[-2,-1]
36531      +l[new_strokes] * -1 + 1 +b 1% b.. 1 min endl
36532      watershed... .,0 rm. map.. . rm.
36533
36534      if !$8 rm[strokes] # No color shading
36535      else # Shade colors
36536        j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1
36537        eq[new_strokes] 0 label_fg[new_strokes] 0,0
36538        if $min_color_area +area_fg[new_strokes] 0,0 >. $min_color_area *[new_strokes,-1] fi
36539        watershed[new_strokes] [strokes],0 rm[strokes]
36540        srgb2rgb. guided. [new_strokes],{1+$8/5},0 rgb2srgb.
36541      fi
36542      rm[new_strokes]
36543      nm. new_colors
36544
36545    elif $1==2 # Mode: Clean color layer
36546      j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1
36547      eq[new_strokes] 0 label_fg[new_strokes] 0,0
36548      if $min_color_area +area_fg[new_strokes] 0,0 xy_bg={[xM,yM]} >. $min_color_area *[new_strokes,-1] fi
36549      watershed[new_strokes] [strokes],0 rm[strokes]
36550      to_color[colors] sh[colors] 0,2 rgb2hsv8. rm. blend[colors,new_strokes] shapemedian sh[colors] 0,2 hsv82rgb. rm.
36551      nm[colors] new_colors
36552
36553    else # Mode: Random colorization
36554      j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1
36555      eq[new_strokes] 0 label_fg[new_strokes] 0,0
36556      if $min_color_area +area_fg[new_strokes] 0,0 xy_bg={[xM,yM]} >. $min_color_area *[new_strokes,-1] fi
36557      watershed[new_strokes] [strokes],0 rm[strokes]
36558      label[new_strokes] 0,0
36559      +histogram[new_strokes] {new_strokes,[iM+1,0,iM]} equalize. 1024 n. 0,240
36560      ind_bg={xM}
36561      channels. 0,2 srand 0
36562      f. "[i,u(1,max(3,$6))/255,u(min(252,$7),255)/255]"
36563      . sh. 0 rand. 0,360 rm.
36564      hsi2rgb[-2,-1] *.. $5 *. {1-$5} +[-2,-1] round.
36565      point. $ind_bg,0,0,1,255 point. 0,0,0,1,255
36566
36567      map[new_strokes] . rm.
36568      nm[new_strokes] new_colors
36569    fi
36570
36571    # Final rendering
36572    ind_lineart=$lineart
36573    ind_colors=$new_colors
36574
36575    if $4 # Output region delimiters
36576      100%,100%,1,1,"const boundary = 1;
36577                     J(#"$new_colors",1)!=I(#"$new_colors") || J(#"$new_colors",0,1)!=I(#"$new_colors")"
36578      *. 255 channels. -3,0 sh. 0,2 fc. 255,0,0 rm.
36579      gui_set_layer_name. $nm" [region delimiters]"
36580      mv. {$lineart+1}
36581    fi
36582    if !narg($nmc) gui_set_layer_name[$ind_colors] $nm" [colors]" fi
36583    gui_set_layer_name[$ind_lineart] $nm
36584
36585    if $-1==-1 # Rendering : Lineart and color layers
36586      if !$is_alpha gui_set_layer_mode[$ind_lineart] multiply fi
36587      if $3 l[{$1==1?1:0}] # Discard contour guides
36588        if $is_alpha
36589          100%,100%,1,3,255 blend. [0],alpha,1 rgb2hsv. channels. 2 *. 255 negate.
36590          channels.. 0,{0,s-2} s={0,s} luminance[0] to_colormode[0] $s a c
36591        else
36592          s={s} to_rgb rgb2hsv channels 2 * 255 to_colormode $s
36593        fi
36594      endl fi
36595
36596    elif $-1==0 # Preview : Colored geometry
36597      if $is_alpha channels[$ind_lineart] 100% negate[$ind_lineart] fi to_rgb[$ind_lineart]
36598      n[$ind_lineart] 180,255
36599      blend[$ind_lineart,$ind_colors] multiply
36600      +select_color[geometry] 0,255,255,255 ==. 0 j[$ind_lineart] [geometry],0,0,0,0,1,. k[$ind_lineart]
36601    elif $-1==1 # Preview : Colored regions
36602      k[$ind_colors]
36603    else # Preview : Colored lineart
36604      if $is_alpha channels[$ind_lineart] 100% negate[$ind_lineart] fi to_rgb[$ind_lineart]
36605      if $3 l[$ind_lineart] s={s} to_rgb rgb2hsv channels 2 * 255 to_colormode $s endl fi
36606      blend[$ind_lineart,$ind_colors] multiply k[$ind_lineart]
36607    fi
36608
36609  endl done
36610
36611#@gui Colorize Lineart [Propagation] : fx_colorize_lineart, fx_colorize_lineart_preview(1)
36612#@gui : note = note("<b>Layers ordering:</b>")
36613#@gui : Input Layers = choice{0,"Color Spots + Lineart","Lineart + Color Spots",
36614#@gui : "Color Spots + Extrapolated Colors + Lineart","Lineart + Color Spots + Extrapolated Colors"}
36615#@gui : Output Layers = _choice{1,"Single (Merged)","Extrapolated Colors + Lineart",
36616#@gui : "Lineart + Extrapolated Colors","Color Spots + Extrapolated Colors + Lineart",
36617#@gui : "Lineart + Color Spots + Extrapolated Colors"}
36618#@gui : Extrapolate Colors As = choice("One Layer","Two Layers","Three Layers","Four Layers","Five Layers",
36619#@gui : "Six Layers","Seven Layers","Eight Layers","Nine Layers","Ten Layers","One Layer per Single Color",
36620#@gui : "One Layer per Single Region")
36621#@gui : sep = separator()
36622#@gui : Smoothness = float(0.05,0,1)
36623#@gui : sep = separator()
36624#@gui : note = note{"<small><b>Note:</b> You probably need to select <i>All</i> for the <i>Input layers</i> option
36625#@gui : on the left.\n
36626#@gui : <i>Color Spots</i> = your layer with color indications.\n
36627#@gui : <i>Lineart</i> = your layer with line-art (B&W or transparent).\n
36628#@gui : <i>Extrapolated Colors</i> = the G'MIC generated layer with flat colors.\n\n
36629#@gui : <b>Warnings:</b>
36630#@gui : \n  - Do not rely too much on the preview, it is probably not accurate !
36631#@gui : \n  - Activate option <i>Extrapolate color as one layer per single color/region</i> only if you have
36632#@gui : <i>a lot</i> of available memory !
36633#@gui : </small>"}
36634#@gui : sep = separator()
36635#@gui : url = link("Click here for a detailed description of this filter.",\
36636# "http://www.gimpchat.com/viewtopic.php?f=28&t=7567")
36637#@gui : sep = separator()
36638#@gui : note = note("<small>Authors: <i>David Tschumperlé</i>, <i>Timothée Giet</i> and <i>David Revoy</i>.
36639#@gui :       Latest Update: <i>2013/19/06</i>.</small>")
36640fx_colorize_lineart :
36641  if $!<2 return fi
36642  if $1<2 selection=0,1 else selection=0,1,2 fi
36643  l[$selection]
36644
36645    # Format input layers.
36646    if $1==0            # Color strokes + drawing
36647    elif $1==1 rv       # Drawing + color strokes
36648    elif $1==2 rm[1]    # Color strokes + extrapolated colors + drawing
36649    elif $1==3 rm[2] rv # Drawing + color strokes + extrapolated colors.
36650    fi
36651
36652    # Here we have only 'color strokes + drawing' -> process.
36653    +to_rgba[0] split_opacity. +.. 1 !=. 0 *[-2,-1]   # Map of color labels to spread.
36654    +norm[1] n. 0,1 +histogram. 2,0,1
36655    if i(0)>i(1) *.. -1 +.. 1 fi rm.          # Determine color model of the drawing.
36656    b. $4% watershed.. . rm.                         # Priority map.
36657    -. 1
36658    # Here we have 'color strokes + drawing + extrapolated colors'.
36659
36660    # Format output layers.
36661    if $2==0 rm[0] rv blend[0,1] multiply ind=-1
36662    elif $2==1 rm[0] rv ind=0
36663    elif $2==2 rm[0] ind=1
36664    elif $2==3 rv[1,2] ind=1
36665    elif $2==4 rv[0,1] ind=2
36666    fi
36667
36668    # Separate extrapolated colors as multiple layers.
36669    if $3" && "$ind>=0 l[$ind]
36670      +mix_channels (65536,256,1)
36671      if $3==10 do  # Split by colors.
36672        iM={1,iM}
36673        if $iM>=0
36674          +==[1] $iM area={is} replace[1] $iM,-1
36675          +r. 100%,100%,1,3 *. [0]
36676          rv[-2,-1] *. 255 a[-2,-1] c nm. $area
36677        fi
36678      while $iM>=0 else  # Split by disconnected regions.
36679        label.
36680        if $3<10 %. {$3+1} fi
36681        repeat iM+1 +==[1] $< area={is} +r. 100%,100%,1,[0] *. [0] rv[-2,-1] *. 255 a[-2,-1] c nm. $area done
36682      fi
36683      rm[0,1]
36684      sort_list +,n
36685    endl fi
36686
36687  endl
36688
36689fx_colorize_lineart_preview :
36690  fx_colorize_lineart $1,0,$3,$4
36691
36692#@gui Dithering : fx_ditheredbw, fx_ditheredbw_preview(0)
36693#@gui : Brightness (%) = float(0,-100,100)
36694#@gui : Contrast (%) = float(0,-100,100)
36695#@gui : Gamma (%) = float(0,-100,100)
36696#@gui : Hue = float(0,0,360)
36697#@gui : Saturation (%) = float(0,0,100)
36698#@gui : Smoothness = float(0,0,10)
36699#@gui : sep = separator()
36700#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36701#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36702#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36703#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36704#@gui : sep = separator()
36705#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36706fx_ditheredbw :
36707  repeat $! l[$>] split_opacity l[0]
36708    luminance  adjust_colors ${1-3} b $6
36709    ditheredbw
36710    if $4" || "$5 / 255 i[0] 100%,100%,1,2 fc[0] $4,{$5%} a c hsv2rgb fi
36711  endl a c endl done
36712
36713fx_ditheredbw_preview :
36714  gui_split_preview "fx_ditheredbw $*",${-3--1}
36715
36716#@gui Engrave : fx_engrave, fx_engrave_preview(0)
36717#@gui : note = note("<small><b>Black & White foreground:</b></small>")
36718#@gui : Radius = float(0.5,0,2)
36719#@gui : Density = float(50,0,200)
36720#@gui : Edges = float(0,0,10)
36721#@gui : Coherence = float(8,0,40)
36722#@gui : Threshold (%) = float(40,0,100)
36723#@gui : Minimal Area = int(0,-256,256)
36724#@gui : Flat Regions Removal = float(0,0,10)
36725#@gui : sep = separator()
36726#@gui : note = note("<small><b>Color background:</b></small>")
36727#@gui : Add Color Background = bool()
36728#@gui : Quantization = float(10,0,40)
36729#@gui : Shading = int(1,0,5)
36730#@gui : Hue = float(0,-180,180)
36731#@gui : Saturation (%) = float(0,-100,100)
36732#@gui : Lightness (%) = float(0,-100,100)
36733#@gui : sep = separator()
36734#@gui : Anti-Aliasing = choice(1,"Disabled","x1.5","x2","x3")
36735#@gui : sep = separator()
36736#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36737#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36738#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36739#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36740#@gui : sep = separator()
36741#@gui : note = note("<small>Authors: <i>Lyle Kroll</i> and <i>David Tschumperlé</i>.
36742#@gui :       Latest Update: <i>03/13/2015</i>.</small>")
36743fx_engrave :
36744  f={arg(1+$14,1,1.5,2,3)}
36745  r={$f*(0.2+$1)}
36746  repeat $! l[$<]
36747    nm=${-gui_layer_name} pos=${-gui_layer_pos}
36748    if $8 [0] fi  # Keep copy for color background.
36749    l[0] split_opacity l[0]
36750      wh={w},{h}
36751      norm
36752      if $14 r {100*$f}%,{100*$f}%,1,1,3 fi
36753      if $7>0 [0] fi  # Keep copy for flat regions removal.
36754      l[0]
36755        amount={(0.5+$2)^2}
36756        repeat 5 b $r unsharp $r,{1+$2} c 0,255 done
36757        smooth 100,0.1,1,{$f*$3},{$f*$4}
36758        >= {100-$5}%
36759      endl
36760      if $7>0 # Flat region removal.
36761        gradient_norm[1] b[1] $3 <[1] $7 max[0,1]
36762      fi
36763      if $6<0 area_fg 0,0 > {$f*$6*$6}
36764      elif $6>0 == 0 area_fg 0,0 > {$f*$6*$6} == 0
36765      fi
36766      * 255
36767      if $14 r $wh,1,1,2 fi
36768    endl a c endl
36769
36770    # Process color background.
36771    if $!>1
36772      l[1] split_opacity l[0]
36773        f={arg(1+$14,1,1.5,2,3)}
36774        if $14 r {100*$f}%,{100*$f}%,1,100%,3 fi
36775        b {$f*$9} segment_watershed 5
36776        if $14 r $wh,1,100%,2 fi
36777        repeat $10 guided 10,{$10*80} done
36778        rgb2hsv s c +... $11 +.. {$12%} +. $13% a c hsv2rgb
36779      endl a c endl
36780      nm[0] mode(darken),name($nm),pos($pos)
36781      nm[1] name($nm" [colors]"),pos($pos)
36782    fi
36783
36784 endl done
36785
36786fx_engrave_preview :
36787  repeat $! l[$<]
36788    gui_split_preview "nm foo fx_engrave $* gui_merge_layers",${-3--1}
36789  endl done
36790
36791#@gui Freaky B&W : fx_freaky_bw, fx_freaky_bw_preview
36792#@gui : Strength (%) = float(90,0,100)
36793#@gui : Oddness (%) = float(20,0,100)
36794#@gui : Brightness (%) = float(0,-100,100)
36795#@gui : Contrast (%) = float(0,-100,100)
36796#@gui : Gamma (%) = float(0,-100,100)
36797#@gui : sep = separator()
36798#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36799#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36800#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36801#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36802#@gui : sep = separator()
36803#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/30/09</i>.</small>")
36804fx_freaky_bw :
36805  repeat $! l[$>] split_opacity l[0]
36806    to_rgb
36807
36808    # Estimate gradient field of B&W result.
36809    +expand_xy 1,0 channels. 0,4
36810    f. ">if (c!=4,i,
36811          Rx = i(x+1,y,0,0) - i(x,y,0,0);
36812          Ry = i(x,y+1,0,0) - i(x,y,0,0);
36813          Rn = Rx^2 + Ry^2;
36814          Gx = i(x+1,y,0,1) - i(x,y,0,1);
36815          Gy = i(x,y+1,0,1) - i(x,y,0,1);
36816          Gn = Gx^2 + Gy^2;
36817          Bx = i(x+1,y,0,2) - i(x,y,0,2);
36818          By = i(x,y+1,0,2) - i(x,y,0,2);
36819          Bn = Bx^2 + By^2;
36820          n = 1e-5 + max(Rn,Gn,Bn)^"{$2%}";
36821          val = 0;
36822         if (Rn>=Gn && Rn>=Bn,
36823           i(x,y,0,3) = Rx/n; val=Ry/n,
36824         if (Gn>=Rn && Gn>=Bn,
36825           i(x,y,0,3) = Gx/n; val=Gy/n,
36826           i(x,y,0,3) = Bx/n; val=By/n));
36827         val
36828        )"
36829    channels. 3,4
36830    luminance[0] ia={0,ia}
36831
36832    # Estimate laplacian of final image and invert it.
36833    s. c
36834    f.. "i - i(x-1,y,0,0)"
36835    f. "i - i(x,y-1,0,0)"
36836    +[-2,-1]
36837    ilaplacian. 0
36838    shrink_xy. 1 +. $ia n. 0,255
36839
36840    # Merge result with original color image.
36841    j[0] [1],0,0,0,0,{$1%} rm.
36842    adjust_colors ${3-5}
36843  endl a c endl done
36844
36845fx_freaky_bw_preview :
36846  gui_split_preview "fx_freaky_bw $*",${-3--1}
36847
36848#@gui Ink Wash : fx_ink_wash, fx_ink_wash(0)
36849#@gui : note = note("Ink wash controls")
36850#@gui : Size = float(0.14,0,4)
36851#@gui : Amplitude = float(23,0,200)
36852#@gui : sep = separator()
36853#@gui : note = note("Check if you wish visual control on this step")
36854#@gui : Skip All Other Steps = bool(false)
36855#@gui : note = note ("UNcheck to reactivate the other controls")
36856#@gui : sep = separator()
36857#@gui : Smoother Sharpness = float(0.5,0,2)
36858#@gui : Smoother Edge Protection = float(0.54,0,1)
36859#@gui : Smoother Softness = float(2.25,0,10)
36860#@gui : sep = separator()
36861#@gui : Stretch Contrast = choice("None","Automatic","Automatic & Contrast Mask","Manual Controls")
36862#@gui : note = note ("To activate the sliders below chose 'Manual Controls'")
36863#@gui : sep = separator()
36864#@gui : LN Amplitude = float(2,0,60)
36865#@gui : LN Size = float(6,0,64)
36866#@gui : LN Neightborhood-Smoothness = float(5,0,40)
36867#@gui : LN Average-Smoothness = float(20,0,40)
36868#@gui : sep = separator()
36869#@gui : note = note("<small>Author: <i>PhotoComiX</i>.
36870#@gui :       Latest Update: <i>2011/05/04</i>.</small>")
36871#@gui : url = link(0,"Forum thread about the filter discussion","http://gimpchat.com/viewtopic.php?f=10&t=914")
36872fx_ink_wash :
36873  repeat $! l[$>] split_opacity l[0]
36874    fx_pencilbw. $1,$2,0,0,0
36875    if $3==1 continue
36876    elif $3==0 fx_smooth_anisotropic. 60,$4,$5,$6,1.1,0.8,30,2,0,1,1,0,1,16
36877    fi
36878    if $7==1 normalize_local. 2,6,5,24,1,0,255
36879    elif $7==2 normalize_local. 2,6,5,24,1,0,255 fx_contrast_swm 2,0,0.512
36880    elif $7==3 fx_normalize_local. $8,$9,$10,$11,1,3,0
36881    fi
36882  endl a c endl done
36883
36884#@gui Pencil : fx_pencilbw, fx_pencilbw_preview(0)
36885#@gui : Size = float(0.3,0,5)
36886#@gui : Amplitude = float(60,0,200)
36887#@gui : Hue = float(0,0,360)
36888#@gui : Saturation = float(0,0,1)
36889#@gui : sep = separator()
36890#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36891#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36892#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36893#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36894#@gui : sep = separator()
36895#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/05/03</i>.</small>")
36896fx_pencilbw :
36897  pencilbw $1,$2
36898  if $3" || "$4 repeat $! l[$>] split_opacity
36899    /[0] 255 i[0] 100%,100%,1,1,$4 i[0] 100%,100%,1,1,$3 a[0-2] c hsv2rgb[0]
36900  a c endl done fi
36901
36902fx_pencilbw_preview :
36903  gui_split_preview "fx_pencilbw $*",${-3--1}
36904
36905#@gui Pencil Portrait : fx_pencil_portraitbw, fx_pencil_portraitbw_preview(0)
36906#@gui : Stroke Length = float(30,0,500)
36907#@gui : Stroke Angle = float(120,0,180)
36908#@gui : Contour Threshold = float(1,0,10)
36909#@gui : Opacity = float(0.5,0,1)
36910#@gui : Color = color(144,79,21)
36911#@gui : sep = separator()
36912#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36913#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36914#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36915#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36916#@gui : sep = separator()
36917#@gui : note = note("<small>Authors: <i>Jamac4k</i> and <i>David Tschumperlé</i>.
36918#@gui :       Latest Update: <i>2015/29/06</i>.</small>")
36919fx_pencil_portraitbw :
36920  repeat $! l[$>] split_opacity l[0]
36921    +b 2%
36922    +blend divide rm.. luminance.
36923    fx_ink_wash.. 0,167,0,0.5,0.54,2.25,0,2,6,5,20
36924    +fx_hardsketchbw. 80,32,1.89,0.21,31.46,0,0
36925    +fx_sketchbw.. 1,$2,180,$1,$3,0.03,0,0.6,0.1,0.6,0.25,1,0,1,0
36926    blend[0,1] darken
36927    blend[0,1] multiply,0.5
36928    blend[0,1] lighten,$4
36929    normalize_local ,
36930    to_rgb +fc ${5-7} blend softlight
36931  endl a c endl done
36932
36933fx_pencil_portraitbw_preview :
36934  gui_split_preview "fx_pencil_portraitbw $*",${-3--1}
36935
36936#@gui Stamp : fx_stamp, fx_stamp_preview(0)
36937#@gui : Auto-Threshold = bool(1)
36938#@gui : Threshold = int(50,0,100)
36939#@gui : Smoothness = float(0,0,10)
36940#@gui : Sharpening = float(0,0,30)
36941#@gui : Grain = float(0,0,100)
36942#@gui : Negative = bool()
36943#@gui : Anti-Aliasing = bool(1)
36944#@gui : sep = separator()
36945#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36946#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36947#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36948#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36949#@gui : sep = separator()
36950#@gui : note = note("<small>Authors: <i>Antaron</i>, <i>Mahvin</i> and <i>David Tschumperlé</i>.
36951#@gui :      Latest Update: <i>2015/16/03</i>.</small>")
36952fx_stamp :
36953  repeat $! l[$>] split_opacity l[0]
36954    wh={w},{h}
36955    norm
36956    if $7 r 150%,150%,1,1,3 fi
36957    noise $5
36958    if $1 otsu 256 else >= $2% fi
36959    b {if($7,1.5,1)*$3},0 sharpen $4 n 0,255
36960    apply_curve 1,0,0,101,33,170,229,255,255
36961    if $7 r $wh,1,1,2 fi
36962    if $6 negate fi
36963  endl a c endl done
36964
36965fx_stamp_preview :
36966  gui_split_preview "fx_stamp $*",${-3--1}
36967
36968
36969#@gui ____<b>Colors</b>
36970#----------------------
36971
36972#@gui Abstraction : fx_color_abstraction, fx_color_abstraction_preview(0)
36973#@gui : Smoothness = float(1,0,10)
36974#@gui : Levels = int(10,2,100)
36975#@gui : Contrast = float(0.2,0.01,1)
36976#@gui : sep = separator()
36977#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36978#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36979#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36980#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36981#@gui : sep = separator()
36982#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/19/10</i>.</small>")
36983fx_color_abstraction :
36984  repeat $! l[$>] split_opacity l[0] to_rgb
36985    b $1 s c quantize $2,1,0 area 0 ^ $3 n 0,255
36986  endl a c endl done
36987
36988fx_color_abstraction_preview :
36989  gui_split_preview "fx_color_abstraction $*",${-3--1}
36990
36991#@gui Apply External CLUT : fx_apply_haldclut, fx_apply_haldclut_preview(1)+
36992#@gui : Specify HaldCLUT As = choice(2,"Top Layer","Bottom Layer","Filename")
36993#@gui : HaldCLUT Filename = filein()
36994#@gui : note = note("<small><b>Note:</b> Do not forget to set the <i>Input layers</i> option if you select
36995#@gui : <i>Top layer</i> or <i>Bottom layer</i>.</small>")
36996#@gui : sep = separator()
36997#@gui : Strength (%) = float(100,0,100)
36998#@gui : Brightness (%) = float(0,-100,100)
36999#@gui : Contrast (%) = float(0,-100,100)
37000#@gui : Gamma (%) = float(0,-100,100)
37001#@gui : Hue (%) = float(0,-100,100)
37002#@gui : Saturation (%) = float(0,-100,100)
37003#@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both")
37004#@gui : sep = separator()
37005#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37006#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37007#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37008#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37009#@gui : sep = separator()
37010#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/02/08</i>.</small>")
37011fx_apply_haldclut : skip "${2=}"
37012  mode=$1
37013  filename="$2"
37014  strength,brightness,contrast,gamma,hue,saturation,normalize=${3-9}
37015
37016  if $mode<2 # CLUT as a layer
37017    if $!<2 gui_warning_preview "Input layer with HaldCLUT is missing" return fi
37018    ind_clut={$mode?$!-1:0}
37019  else # CLUT as a file
37020    l
37021      0 nm. "$2" ext={x} rm.
37022      if lowercase(['$ext'])=='cube' input_cube "$2"
37023      else i "$2"
37024      fi
37025      ind_clut={$!-1}
37026    onfail gui_warning_preview "Specified HaldCLUT filename not found" return
37027    endl
37028  fi
37029  if {$ind_clut,iM>512} /[$ind_clut] 255 fi # Possibly a 16bits HaldCLUT.
37030
37031  if $normalize==1" || "$normalize==3 # Pre-normalization
37032    repeat $! if $>!=$ind_clut l[$>] split_opacity balance_gamma[0] , a c endl fi done
37033  fi
37034  repeat $! if $>!=$ind_clut +map_clut[$>] [$ind_clut] j[$>] .,0,0,0,0,{$strength%} rm. fi done rm[$ind_clut]
37035  adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
37036  if $normalize==2" || "$normalize==3 # Post-normalization
37037     repeat $! l[$>] split_opacity n[0] 0,255 a c endl done
37038  fi
37039
37040fx_apply_haldclut_preview : skip "${2=}"
37041  if $1<2 gui_warning_preview "No preview available in this mode" return fi
37042  gui_split_preview "fx_apply_haldclut $1,\"$2\",${3--2}",${-3--1}
37043
37044#@gui Basic Adjustments : fx_adjust_colors, fx_adjust_colors_preview
37045#@gui : Brightness (%) = float(0,-100,100)
37046#@gui : Contrast (%) = float(0,-100,100)
37047#@gui : Gamma (%) = float(0,-100,100)
37048#@gui : Hue (%) = float(0,-100,100)
37049#@gui : Saturation (%) = float(0,-100,100)
37050#@gui : sep = separator()
37051#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37052#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37053#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37054#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37055#@gui : sep = separator()
37056#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/16/06</i>.</small>")
37057fx_adjust_colors :
37058  adjust_colors ${1-5},0,255
37059
37060fx_adjust_colors_preview :
37061  gui_split_preview "fx_adjust_colors $*",${-3--1}
37062
37063#@gui Boost Chromaticity : fx_boost_chroma, fx_boost_chroma_preview(1)
37064#@gui : Amplitude (%) = float(50,0,100)
37065#@gui : Color Space = choice{"YCbCr (Distinct)","YCbCr (Mixed)","Lab (Distinct)","Lab (Mixed)"}
37066#@gui : sep = separator()
37067#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37068#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37069#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37070#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37071#@gui : sep = separator()
37072#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/19/07</i>.</small>")
37073fx_boost_chroma :
37074  repeat $! l[$>] split_opacity l[0]
37075    +to_rgb
37076    if $2>=2
37077      srgb2rgb rgb2lab.
37078      if $2==2 sh. 1 sh.. 2 equalize[-2,-1] rm[-2,-1]
37079      else sh. 1,2 equalize. rm.
37080      fi
37081      lab2rgb. rgb2srgb
37082    else
37083      rgb2ycbcr.
37084      if $2==0 sh. 1 sh.. 2 equalize[-2,-1] rm[-2,-1]
37085      else sh. 1,2 equalize. rm.
37086      fi
37087      ycbcr2rgb.
37088    fi
37089    j.. .,0,0,0,0,{$1%} rm.
37090  endl a c endl done
37091
37092fx_boost_chroma_preview :
37093  gui_split_preview "fx_boost_chroma $*",${-3--1}
37094
37095#@gui Boost-Fade : fx_boost_fade, fx_boost_fade_preview
37096#@gui : Amplitude = float(5,0,10)
37097#@gui : Chromaticity From = choice("YCbCr","Lab")
37098#@gui : sep = separator()
37099#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37100#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37101#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37102#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37103#@gui : sep = separator()
37104#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/11/26</i>.</small>")
37105fx_boost_fade :
37106  repeat $! l[$>]
37107    100%,100%,1,3 rand. 0,1 b. {10-10*($1/10)^0.5} n. 0,255
37108    to_colormode 0 a z
37109    ac "s z transfer_histogram.. . rm.",${"arg 1+$2,ycbcr_cbcr,lab_ab"}
37110  endl done
37111
37112fx_boost_fade_preview :
37113  gui_split_preview "fx_boost_fade $1,$2",${-3--1}
37114
37115#@gui Channel Processing : fx_channel_processing, fx_channel_processing_preview(1)
37116#@gui : Brightness (%) = float(0,-100,100)
37117#@gui : Contrast (%) = float(0,-100,100)
37118#@gui : Gamma (%) = float(0,-100,100)
37119#@gui : Smoothness = float(0,0,10)
37120#@gui : Value Action = choice("None","Cut","Cut & Normalize","Normalize","Threshold")
37121#@gui : Low Value = float(0,0,100)
37122#@gui : High Value = float(100,0,100)
37123#@gui : Quantization = int(256,1,256)
37124#@gui : Equalization = bool(0)
37125#@gui : Negation = bool(0)
37126#@gui : sep = separator()
37127#@gui : Tones Range = choice("All tones","Shadows","Mid-Tones","Highlights")
37128#@gui : Tones Smoothness = float(2,0,10)
37129#@gui : sep = separator()
37130#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
37131#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
37132#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
37133#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
37134#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
37135#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
37136#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
37137#@gui : sep = separator()
37138#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37139#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37140#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37141#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37142#@gui : sep = separator()
37143#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
37144_fx_channel_processing :
37145  adjust_colors ${1-3} b. $4%
37146  if $5==1 c. $6%,$7%
37147  elif $5==2 c. $6%,$7% n. 0,255
37148  elif $5==3 n. $6%,$7%
37149  elif $5==4 ir. $6%,$7% *. 255
37150  fi
37151  if $8!=256 quantize. $8,1,0 fi
37152  if $9 equalize. fi
37153  if $10 negate. fi
37154
37155fx_channel_processing :
37156  repeat $! l. split_opacity rv to_rgb.
37157    fx_start_mix $11,$12
37158    ac. "_fx_channel_processing $1,$2,$3,$4,$5,$6,$7,$8,$9,$10",$13,1
37159    fx_end_mix $11
37160  if $!!=3 rv a c fi endl mv. 0 done
37161
37162fx_channel_processing_preview :
37163  gui_split_preview "fx_channel_processing $*",${-3--1}
37164
37165#@gui Channels to Layers : fx_channels2layers, fx_channels2layers_preview
37166#@gui : Colorspace = choice("RGB","CMY","HSV")
37167#@gui : sep = separator()
37168#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/15/07</i>.</small>")
37169fx_channels2layers :
37170  repeat $! l[$<] nm=${-gui_layer_name} to_rgb
37171    if $1==0 # RGB
37172      s[0] c
37173      r[0] 100%,100%,1,3,0,0,0,0,0,0 nm[0] name($nm" "[red]),mode(add)
37174      r[1] 100%,100%,1,3,0,0,0,0,0,0.5 nm[1] name($nm" "[green]),mode(add)
37175      r[2] 100%,100%,1,3,0,0,0,0,0,1 nm[2] name($nm" "[blue]),mode(add)
37176    elif $1==1 # CMY
37177      rgb2cmy[0] -[0] 255 s[0] c
37178      r[0] 100%,100%,1,3,0,0,0,0,0,0 nm[0] name($nm" "[cyan]),mode(difference)
37179      r[1] 100%,100%,1,3,0,0,0,0,0,0.5 nm[1] name($nm" "[magenta]),mode(difference)
37180      r[2] 100%,100%,1,3,0,0,0,0,0,1 nm[2] name($nm" "[yellow]),mode(difference)
37181      +[0-2] 255
37182      i[0] 100%,100%,1,3,255 nm[0] name($nm" "[base]),mode(difference)
37183    else # HSV
37184      rgb2hsv[0] s[0] c,-2
37185      r[0] 100%,100%,1,3,0,0 sh[0] 2 f. 1 rm. nm[0] name($nm" "[color]),mode(normal)
37186      r[1] 100%,100%,1,3,0,0,0,0,0,1 nm[1] name($nm" "[value]),mode(value)
37187      hsv2rgb[0,1] rv[0,1]
37188    fi
37189  endl done
37190
37191fx_channels2layers_preview :
37192  repeat $! l[$>]
37193    fx_channels2layers $*
37194    repeat $! l[$>] to "#"{1+$>},1,1,43,7,1,255 endl done
37195    frame 1,1,0 frame 3,3,255 append_tiles ,
37196  endl done
37197
37198#@gui Color Balance : fx_balance_gamma, fx_balance_gamma_preview
37199#@gui : Neutral Color = color(128,128,128)
37200#@gui : Stretch Colors = bool(1)
37201#@gui : sep = separator()
37202#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37203#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37204#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37205#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37206#@gui : sep = separator()
37207#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/01/07</i>.</small>")
37208fx_balance_gamma :
37209  repeat $! l[$>] split_opacity
37210    if $!>1 +!=. 0 *[0,-1] fi
37211    l[0]
37212      balance_gamma ${1-3}
37213      if $4 n 0,255 fi
37214    endl
37215    a c endl
37216  done
37217
37218fx_balance_gamma_preview :
37219  gui_split_preview "fx_balance_gamma $*",${-3--1}
37220
37221#@gui Color Blindness : colorblind, fx_colorblind_preview
37222#@gui : Blindness Type = choice("Protanopia","Protanomaly","Deuteranopia","Deuteranomaly","Tritanopia",
37223#@gui : "Tritanomaly","Achromatopsia","Achromatomaly")
37224#@gui : sep = separator()
37225#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37226#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37227#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37228#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37229#@gui : sep = separator()
37230#@gui : note = note{"<small><b>Note:</b>
37231#@gui : This filter simulates different types of colorblindness vision.
37232#@gui : </small>"}
37233#@gui : sep = separator()
37234#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/04</i>.</small>")
37235fx_colorblind_preview :
37236  gui_split_preview "colorblind $*",${-3--1}
37237
37238#@gui Color Presets : fx_color_presets, fx_color_presets_preview(1)+
37239#@gui : LUTs Pack = choice{19,"Abigail Gonzalez (21)","Alex Jordan (81)","Berat (10)","Cinematic (8)",
37240#@gui : "Cinematic Travel (29)","Creative Pack (33)","Eric Ellerbrock (14)","FilterGrade Cinematic (8)",
37241#@gui : "InAvision (15)","J.T. Semple (14)","Kyler Holland (10)","Lutify.Me (7)","Michael Ezra (2)","Moviz (48)",
37242#@gui : "Ohad Peretz (7)","Olivio Sarikas (19)","ON1 Photography (90)","PictureFX (25)","Pixelmator (45)",
37243#@gui : "PIXLS.US (31)","Purple11 (12)","RocketStock (35)","Shamoon Abbasi (25)","SmallHD Movie Look (7)",
37244#@gui : "Youssef Hossam (5)","Others (69)"}
37245
37246##### Abigail Gonzales
37247#@gui : Preset = choice{1,"All [Collage]","None",
37248#@gui : "Blade Runner","Blue House","Blue Ice","Caribe","Cinema","Cinema 2","Cinema 3","Cinema 4","Cinema 5",
37249#@gui : "Cinema Noir","Cinematic for Flog","Day4Nite","Eterna for Flog","Filmic","Fuji HDR",
37250#@gui : "Golden Gate","Matrix","Monochrome 1","Monochrome 2","Old West","Science Fiction"}_0
37251
37252##### Alex Jordan
37253#@gui : Preset = choice{1,"All [Collage]","None",
37254#@gui : "Action Magenta 01","Action Red 01","Adventure 1453","Aggressive Highlights Recovery 5",
37255#@gui : "Bleech Bypass Green","Bleech Bypass Yellow 01","Blue Dark","Blue Shadows 01","Bright Green 01",
37256#@gui : "Brownish","Colorful 0209","Conflict 01","Contrast with Highlights Protection","Contrasty Afternoon",
37257#@gui : "Contrasty Green","Cross Process CP 130","Cross Process CP 14","Cross Process CP 15",
37258#@gui : "Cross Process CP 16","Cross Process CP 18","Cross Process CP 3","Cross Process CP 4",
37259#@gui : "Cross Process CP 6","Dark Green 02","Dark Green 1","Dark Place 01","Dream 1","Dream 85",
37260#@gui : "Faded Retro 01","Faded Retro 02","Film 0987","Film 9879","Film Highlight Contrast","Flat 30",
37261#@gui : "Green 2025","Green Action","Green Afternoon","Green Conflict","Green Day 01","Green Day 02",
37262#@gui : "Green G09","Green Indoor","Green Light","Harsh Day","Harsh Sunset","Highlights Protection",
37263#@gui : "Indoor Blue","Low Contrast Blue","Low Key 01","Magenta Day","Magenta Day 01","Magenta Dream",
37264#@gui : "Memories","Moonlight 01","Mostly Blue","Muted 01","Night 01","Only Red","Only Red and Blue",
37265#@gui : "Operation Yellow","Orange Dark 4","Orange Dark 7","Orange Dark Look","Orange Underexposed",
37266#@gui : "Protect Highlights 01","Red Afternoon 01","Red Day 01","Red Dream 01","Retro Brown 01",
37267#@gui : "Retro Magenta 01","Retro Yellow 01","Saturated Blue","Smart Contrast","Subtle Blue",
37268#@gui : "Subtle Green","Yellow 55B","Yellow Film 01"}_0
37269
37270##### Berat
37271#@gui : Preset = choice{1,"All [Collage]","None",
37272#@gui : "Brown BM","Cine Blue","Cine BM4k","Golden Time","Green and Orange","Monochrome","Sevsuz","Sunlight Love",
37273#@gui : "Western","Western Lut 2"}_0
37274
37275##### Cinematic
37276#@gui : Preset = choice{1,"All [Collage]","None",
37277#@gui : "Deep","Dimension","Enchanted","Flavin","Frosted","Shine","Ultra Water","Wipe"}_0
37278
37279##### Cinematic Travel
37280#@gui : Preset = choice{1,"All [Collage]","None",
37281#@gui : "Blue Cold Fade","Bright Teal Orange","Bright Warm","Clear Teal Fade","Cold Clear Blue","Cold Clear Blue 1",
37282#@gui : "Deep Blue","Deep Dark Warm","Deep High Contrast","Deep Teal Fade","Deep Warm Fade","Faded Green",
37283#@gui : "Greenish Contrasty","Greenish Fade","Greenish Fade 1","Hard Teal Orange","Neutral Teal Orange",
37284#@gui : "Neutral Warm Fade","Smooth Clear","Smooth Green Orange","Smooth Teal Orange","Teal Fade","Very Warm Greenish",
37285#@gui : "Warm Dark Contrasty","Warm Fade","Warm Fade 1","Warm Neutral","Warm Sunset Red","Warm Teal"}_0
37286
37287##### Creative Pack
37288#@gui : Preset = choice{1,"All [Collage]","None",
37289#@gui : "Anime","Bleach Bypass 1","Bleach Bypass 2","Bleach Bypass 3","Bleach Bypass 4","Candle Light",
37290#@gui : "Color Negative","Crisp Warm","Crip Winter","Drop Blues","Edgy Ember","Fall Colors","Foggy Night",
37291#@gui : "Futuristic Bleak 1","Futuristic Bleak 2","Futuristic Bleak 3","Futuristic Bleak 4","Horror Blue",
37292#@gui : "Late Sunset","Moonlight","Night From Day","Red Blue Yellow","Smokey","Soft Warming","Teal Magenta Gold",
37293#@gui : "Teal Orange","Teal Orange 1","Teal Orange 2","Teal Orange 3","Tension Green 1","Tension Green 2",
37294#@gui : "Tension Green 3","Tension Green 4"}_0
37295
37296##### Eric Ellerbrock
37297#@gui : Preset = choice{1,"All [Collage]","None",
37298#@gui : "Avalanche","Black Star","Helios","Hydracore","Hypnosis","Killstreak","Nemesis","Night Blade 4",
37299#@gui : "Paladin","Seringe 4","Serpent","Terra 4","Victory","Yellowstone"}_0
37300
37301##### FilterGrade Cinematic
37302#@gui : Preset = choice{1,"All [Collage]","None",
37303#@gui : "Cine Basic","Cine Bright","Cine Cold","Cine Drama","Cine Teal Orange 1","Cine Teal Orange 2",
37304#@gui : "Cine Vibrant","Cine Warm"}_0
37305
37306##### InAvision
37307#@gui : Preset = choice{1,"All [Collage]","None",
37308#@gui : "7Drk21","BC Darkum","Brown Mobster","Cold Ice","Dark Man X","Film GB-19","Formula B","Gremerta",
37309#@gui : "Hitman","J. Wick 21","London Nights","Louetta","Nightlife","VFB 21","Vintage Mob"}_0
37310
37311##### J.T. Semple
37312#@gui : Preset = choice{1,"All [Collage]","None",
37313#@gui : "Bright Green","Crisp Romance","Crushin","Frosted Beach Picnic","Just Peachy","Late Afternoon Wanderlust",
37314#@gui : "Lush Green Summer","Magenta Coffee","Minimalist Caffeination","Mystic Purple Sunset","Nostalgia Honey",
37315#@gui : "Spring Morning","Toasted Garden","Winter Lighthouse"}_0
37316
37317##### Kyler Holland
37318#@gui : Preset = choice{1,"All [Collage]","None",
37319#@gui : "KH 1","KH 2","KH 3","KH 4","KH 5","KH 6","KH 7","KH 8","KH 9","KH 10"}_0
37320
37321##### Lutify.Me
37322#@gui : Preset = choice{1,"All [Collage]","None",
37323#@gui : "Hackmanite","Herderite","Heulandite","Hiddenite","Hilutite","Howlite","Hypersthene"}_0
37324
37325##### Michael Ezra
37326#@gui : Preset = choice{1,"All [Collage]","None",
37327#@gui : "Deep Skin Tones 2","Deep Skin Tones 3"}_0
37328
37329##### Moviz
37330#@gui : Preset = choice{1,"All [Collage]","None",
37331#@gui : "Moviz 1","Moviz 2","Moviz 3","Moviz 4","Moviz 5","Moviz 6","Moviz 7","Moviz 8","Moviz 9","Moviz 10",
37332#@gui : "Moviz 11","Moviz 12","Moviz 13","Moviz 14","Moviz 15","Moviz 16","Moviz 17","Moviz 18","Moviz 19","Moviz 20",
37333#@gui : "Moviz 21","Moviz 22","Moviz 23","Moviz 24","Moviz 25","Moviz 26","Moviz 27","Moviz 28","Moviz 29","Moviz 30",
37334#@gui : "Moviz 31","Moviz 32","Moviz 33","Moviz 34","Moviz 35","Moviz 36","Moviz 37","Moviz 38","Moviz 39","Moviz 40",
37335#@gui : "Moviz 41","Moviz 42","Moviz 43","Moviz 44","Moviz 45","Moviz 46","Moviz 47","Moviz 48"}_0
37336
37337##### Ohad Peretz
37338#@gui : Preset = choice{1,"All [Collage]","None",
37339#@gui : "Cold Simplicity 2","D and O 1","Retro Summer 3","Subtle Yellow","Teal Moonlight","True Colors 8",
37340#@gui : "Vintage Warmth 1"}_0
37341
37342#### Olivio Sarikas
37343#@gui : Preset = choice{1,"All [Collage]","None",
37344#@gui : "Analog Film 1","Atomic Pink","Beach Aqua Orange","Beach Faded Analog","BW but Yellow","City Dust",
37345#@gui : "Dark Orange Teal","Day to Night King's Blue","DuoTone Blue Red","Faded Pink-ish","Flat Blue Moon",
37346#@gui : "Honey Light","Infrared - Dust Pink","Neutral Pump","Shade King's Ink","Sunset Aqua Orange",
37347#@gui : "Sunset Intense Violet Blue","Sunset Violet Mood","Violet Taste"}_0
37348
37349##### ON1 Photography
37350#@gui : Preset = choice{1,"All [Collage]","None",
37351#@gui : "2-Strip Process","Aqua","Aqua and Orange Dark","Berlin Sky","Blues",
37352#@gui : "Black & White-1","Black & White-2","Black & White-3","Black & White-4","Black & White-5",
37353#@gui : "Black & White-6","Black & White-7","Black & White-8","Black & White-9","Black & White-10","Chrome 01",
37354#@gui : "Cinematic-1","Cinematic-2","Cinematic-3","Cinematic-4","Cinematic-5","Cinematic-6","Cinematic-7",
37355#@gui : "Cinematic-8","Cinematic-9","Cinematic-10","Classic Teal and Orange","Earth Tone Boost","Fade to Green",
37356#@gui : "Film Print 01","Film Print 02","French Comedy","Green Blues","Green Yellow","Landscape-1","Landscape-2",
37357#@gui : "Landscape-3","Landscape-4","Landscape-5","Landscape-6","Landscape-7","Landscape-8","Landscape-9",
37358#@gui : "Landscape-10","Lifestyle & Commercial-1","Lifestyle & Commercial-2","Lifestyle & Commercial-3",
37359#@gui : "Lifestyle & Commercial-4","Lifestyle & Commercial-5","Lifestyle & Commercial-6","Lifestyle & Commercial-7",
37360#@gui : "Lifestyle & Commercial-8","Lifestyle & Commercial-9","Lifestyle & Commercial-10","Moody-1","Moody-2",
37361#@gui : "Moody-3","Moody-4","Moody-5","Moody-6","Moody-7","Moody-8","Moody-9","Moody-10","Nature & Wildlife-1",
37362#@gui : "Nature & Wildlife-2","Nature & Wildlife-3","Nature & Wildlife-4","Nature & Wildlife-5","Nature & Wildlife-6",
37363#@gui : "Nature & Wildlife-7","Nature & Wildlife-8","Nature & Wildlife-9","Nature & Wildlife-10","Oranges","Portrait-1",
37364#@gui : "Portrait-2","Portrait-3","Portrait-4","Portrait-5","Portrait-6","Portrait-7","Portrait-8","Portrait-9",
37365#@gui : "Portrait10","Purple","Reds","Reds Oranges Yellows","Studio Skin Tone Shaper","Vintage Chrome"}_0
37366
37367##### Picture FX
37368#@gui : Preset = choice{1,"All [Collage]","None",
37369#@gui : "AnalogFX - Anno 1870 Color","AnalogFX - Old Style I","AnalogFX - Old Style II","AnalogFX - Old Style III",
37370#@gui : "AnalogFX - Sepia Color","AnalogFX - Soft Sepia I","AnalogFX - Soft Sepia II",
37371#@gui : "PictureFX - Faux Infrared B&W1","PictureFX - Faux Infrared Color P2","PictureFX - Faux Infrared Color P3",
37372#@gui : "PictureFX - Faux Infrared R0a","PictureFX - Faux Infrared R0b","PictureFX - Faux Infrared YP1",
37373#@gui : "GoldFX - Bright Spring Breeze","GoldFX - Bright Summer Heat","GoldFX - Hot Summer Heat",
37374#@gui : "GoldFX - Perfect Sunset 01min","GoldFX - Perfect Sunset 05min","GoldFX - Perfect Sunset 10min",
37375#@gui : "GoldFX - Spring Breeze","GoldFX - Summer Heat",
37376#@gui : "TechnicalFX - Backlight Filter","ZilverFX - B&W Solarization","ZilverFX - InfraRed",
37377#@gui : "ZilverFX - Vintage B&W"}_0
37378
37379##### Pixelmator
37380#@gui : Preset = choice{1,"All [Collage]","None",
37381#@gui : "Black & White 01","Black & White 02","Black & White 03","Black & White 04","Black & White 05",\
37382# "Black & White 06",
37383#@gui : "Cinematic 01","Cinematic 02","Cinematic 03","Cinematic 04","Cinematic 05","Cinematic 06","Cinematic 07",
37384#@gui : "Classic Films 01","Classic Films 02","Classic Films 03","Classic Films 04","Classic Films 05",
37385#@gui : "Landscape 01","Landscape 02","Landscape 03","Landscape 04","Landscape 05",
37386#@gui : "Modern Films 01","Modern Films 02","Modern Films 03","Modern Films 04","Modern Films 05","Modern Films 06",\
37387# "Modern Films 07",
37388#@gui : "Night 01","Night 02","Night 03","Night 04","Night 05",
37389#@gui : "Urban 01","Urban 02","Urban 03","Urban 04","Urban 05",
37390#@gui : "Vintage 01","Vintage 02","Vintage 03","Vintage 04","Vintage 05"}_0
37391
37392##### PIXLS.US
37393#@gui : Preset = choice{1,"All [Collage]","None",
37394#@gui : "Amstragram","Amstragram+","Autumn","Cinematic Lady Bird","Cinematic Mexico","Dark Blues in Sunlight",
37395#@gui : "Delicatessen","Expired 69","Faded Look","Faded Print","Hypressen","Magenta Yellow","Metropolis",
37396#@gui : "Modern Film","Newspaper","Night Spy","Progressen","Prussian Blue","Seventies Magazine","Street",
37397#@gui : "Sweet Bubblegum","Sweet Gelatto","Taiga","Tarraco","Unknown","Uzbek Bukhara","Uzbek Marriage",
37398#@gui : "Uzbek Samarcande","Velvetia","Warm Vintage","Whiter Whites"}_2
37399
37400##### Purple11
37401#@gui : Preset = choice{1,"All [Collage]","None",
37402#@gui : "Going for a Walk","Good Morning","Nah","Once Upon a Time","Passing By","Serenity",
37403#@gui : "Smooth Sailing","Undeniable","Undeniable 2","Urban Cowboy","We'll See","You Can Do It"}_0
37404
37405##### RocketStock
37406#@gui : Preset = choice{1,"All [Collage]","None",
37407#@gui : "Arabica 12","Ava 614","Azrael 93","Bourbon 64","Byers 11","Chemical 168","Clayton 33","Clouseau 54",
37408#@gui : "Cobi 3","Contrail 35","Cubicle 99","Django 25","Domingo 145","Faded 47","Folger 50","Fusion 88",
37409#@gui : "Hyla 68","Korben 214","Lenox 340","Lucky 64","McKinnon 75","Milo 5","Neon 770","Paladin 1875","Pasadena 21",
37410#@gui : "Pitaya 15","Reeve 38","Remy 24","Sprocket 231","Teigen 28","Trent 18","Tweed 71","Vireo 37","Zed 32",
37411#@gui : "Zeke 39"}_0
37412
37413##### Shamoon Abbasi
37414#@gui : Preset = choice{1,"All [Collage]","None",
37415#@gui : "City 7","Coffee 44","Date 39","Day for Night","Denoise Simple 40","Desert Gold 37","Directions 23",
37416#@gui : "Drop Green Tint 14","Elegance 38","Golden Night Softner 43","Golden Sony 37","Green 15","Happyness 133",
37417#@gui : "HLG 1","Industrial 33","Morning 6","Morroco 16","Night King 141","Rest 33","Shadow King 39","Spy 29",
37418#@gui : "Thriller 2","Turkiest 42","Vintage 163","Wooden Gold 20"}_0
37419
37420##### SmallHD Movie Look
37421#@gui : Preset = choice{1,"All [Collage]","None",
37422#@gui : "Apocalypse This Very Moment","B-Boyz 2","Bob Ford","Life Giving Tree","Moonrise","Saving Private Damon",
37423#@gui : "The Matrices"}_0
37424
37425##### Youssef Hossam
37426#@gui : Preset = choice{1,"All [Collage]","None",
37427#@gui : "Cinematic Forest","City","Darkness","Hallowen Dark","Sea"}_0
37428
37429##### Others
37430#@gui : Preset = choice{1,"All [Collage]","None",
37431#@gui : "60's","60's (faded)","60's (faded alt)","Alien green","Black & White","Bleach bypass","Blue mono",
37432#@gui : "Cinematic-01","Cinematic-02","Cinematic-03",
37433#@gui : "Color (rich)","Faded","Faded (alt)","Faded (analog)","Faded (extreme)","Faded (vivid)","Expired (fade)",
37434#@gui : "Expired (polaroid)","Extreme","Fade","Faux infrared","Golden","Golden (bright)","Golden (fade)",
37435#@gui : "Golden (mono)","Golden (vibrant)","Green mono","Hong Kong","Instant-C","K-Tone Vintage Kodachrome",
37436#@gui : "Light (blown)","Lomo","Mono tinted","Muted fade",
37437#@gui : "Mute shift","Natural (vivid)","Nostalgic","Orange tone","Pink fade","Purple","Retro","Rotate (muted)",
37438#@gui : "Rotate (vibrant)","Rotated","Rotated (crush)","Smooth crome-ish","Smooth fade","Soft fade","Solarize color",
37439#@gui : "Solarized color2","Summer","Summer (alt)","Sunny","Sunny (alt)","Sunny (warm)","Sunny (rich)","Super warm",
37440#@gui : "Super warm (rich)","Sutro FX","Vibrant","Vibrant (alien)","Vibrant (contrast)","Vibrant (crome-ish)",
37441#@gui : "Vintage","Vintage (alt)","Vintage (brighter)","Warm","Warm (highlight)","Warm (yellow)"}_0
37442
37443#@gui : Thumbnail Size = int(512,0,1024)_1
37444#@gui : sep = separator()
37445#@gui : Strength (%) = float(100,0,100)
37446#@gui : Brightness (%) = float(0,-100,100)
37447#@gui : Contrast (%) = float(0,-100,100)
37448#@gui : Gamma (%) = float(0,-100,100)
37449#@gui : Hue (%) = float(0,-100,100)
37450#@gui : Saturation (%) = float(0,-100,100)
37451#@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both")
37452#@gui : sep = separator()
37453#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37454#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37455#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37456#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37457#@gui : sep = value(0)_2+
37458#@gui : sep = separator()
37459#@gui : note = note("<b>Note:</b> The color LUTs proposed in this category comes from:\n")
37460
37461#@gui : sep = value(0)_0+
37462#@gui : note = note{"<center><img src="data:image/png;base64,\
37463# iVBORw0KGgoAAAANSUhEUgAAABoAAAAgCAMAAAA7dZg3AAADAFBMVEUAAAAGBgYwHyUFBAQMDAwuHSMQDg8+LTMICAg2JStDMTfmu5gKBgfs6evgs5Di\
37464# popINz06KS/tzrnnuarUmGxSQEVHOTE4JyNHIxggEBMVCg4NBwj29fXmx8Pu0rzoyrPlyLHoxKzgtKTdr5/pvpzjuY7ep4ritIjbooXgpYTgtYPcn4PA\
37465# lYPZnIHYmn7NkHS6iXS7gGevc1mOVUhVRUiWW0GHSjM4Jy0+LyscGB4TCQkoBwk2Cgju7e7z1NDuzcjnx8bqzMXn0b/lxbvotaPit5PlsZLYrYvnqouY\
37466# jInmrYXjq3/cnH/NkH/iqHzTl3zfpHnanHneona+kXCignDGimvHhWvVi2Z+Z2bMkWW3el6ldl3RflzAgli4bFadXlRrXVNvVlOoak+vY09hU0tYRkuh\
37467# W0NOPUNZSUB+Tz5URDp2RzVcQDVqPi5yOy5WNihHLyAdFxcrChNCGA4aCQphCAjw6unry9HyzMrrxL/wxbfuyrLIr6/iqqWspqXnvaTtwqDnt5/mtpjl\
37468# r5jbqpbfrY/ftYvFm4e5mYWWf4HYoYDEi3uPg3nGm3jYjXTEkXHOi3GFdXDDjm/ThW+6e2+vfWzSlGnakWmzdWiZc2fOi2PGe2HCh1uoaVt2Y1iZa1fB\
37469# dlZ4VFOIXlFbSlCUWEyHWEhmS0aST0WBRDmPSzhvQjJaPCl3QChmNii6EiiiFyY0ISGQEB+bCxswHBlcDxh3CRIgDBEzEQ7g3Nzc3Nzu4tbevcPw1L7S\
37470# yL3TrqzUq5mamJnCp5XCp5TZpJDroonQlYXon4S+hoOJgYPqnX3clHzAlHqBdHbboHTBgnTYnXG+f3FxZm+6j216YGnLhWfUg2WlcmSzcGSWa2N3ZmPE\
37471# iV3IeliJXFW2e1HKdFGmVVGSYEyGU0toTUugaEqsYUpwVEqhYEh5V0Z7U0bIYUOQYUN/KUNIPkKGTD9vQzyJQjppPDhWOTWZMC7AGC0qJyhfLyRdLyA/\
37472# IBgKBxgwJhcvIRcsEhdFKRSCDRRICw5SCAxjV0wNAAADGElEQVQoz23NBUxbYRAH8O9JX1+FlrrhMIa7O8PGcN1wBgx3xtzdBXd3GQ5zd3d3d3d/Lcmy\
37473# LLvkkrv/L5cD9nIJqbF8MguGYZIgORkmwZbkjZ5TklpWgmlrvvJjrWC+FQmGE9YkUGGYRTYza0yaMgnMSxVZ8QdYG80oMCzg95JIMIVFpVLlCfKMPV4j\
37474# gGFLEkyEFBJJamQyVd7ME2yx35FqRaFQWNKdSiZbUiyJQT6JNwnIKRy6/jL9TUtN8Iq9e1YYm9Q21NdPvsLj8SYDua1HJq7za7iopqKswlZ119TW2jd3\
37475# 55yV5xoJGjfxyQVmxC62Enscez5jkdtCzkxFJ6dtepeB/azImwFaZ1W2KykvXLreJDxoCcdZcfp0R06B9CpybFCQipIqw+NYxO1m8zg/jouiotPcOiDn\
37476# rH1Qe+1aZSVVLcMDh8+UW8SFaKrrFdemGIE5b33Ga2hoqLKV3Zeu8lle1J6oubg4SyjM0wPPvt3Zr0Fzmz97Ns1jlWFoUVzoIjXT7C+f7PSAnWhA352h\
37477# 47ZgAc1D08/QUH/xLE5zRuf3vAIwbJPtPWECgzZmLJOppau7xMVxhpppZkZ38HIgpqcH+MtoPJPpfdTVwcFR3byrK2XzNCBBOku4XsukpO29odDV2WGm\
37478# +kMLi2AFBYAiwmsV/jq0MTq+3POF61xdZqipJ7a/uLubIMg2MbLUl7FM5wQ3QFfXIISpv9rcIoslP4+gqd3RpYFeXr5cAwMDfZ+Tp8ti0l7nWL9/DlCc\
37479# PtIWdSrQPzAsJHTDeqOy6hjTjF4Ms7YGGHGWZ14Rxi2JqqqLvx9/KcY0LTsHQzFMSnRb4b0bYVejqioro2+1ZnZ8HBajECQGOIqi4qnCpybG4eHR8a2v\
37480# 2jI/iDAUgXARQHAcsxHTR9IiVhsZmzR1ZAls6CiOQLk9ACAQKpFAuERQbmRc3ZSSboNDOA5Bg+8AABAKAQhCEIz34NHj5BxIVrl9mwgiYqKBtFAMQmRC\
37481# /9UvJYAgyKjIFpnYDfX3/In+NsQ2/0ff4L8w+sAufyhX9F+S/My3/Wz9Gwm+5rUIT8ACAAAAAElFTkSuQmCC"/>  \
37482# <a href="https://www.abigailgonzalez.com/">Abigail Gonzalez - FreshLUTs</a></center>"}
37483
37484#@gui : sep = value(0)_0+
37485#@gui : note = note{"<center><img src="data:image/png;base64,\
37486# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAADAFBMVEXU1NTV0cfT0MXNyb6fmo7V0cvX08i2trza1tDX083UzsHQx7YSDxHW1NTb19PM\
37487# ysjKxb68uKqdnJ1MRUjO0dnKzdna2NfBws3BwcnTz8fOycHIwLXEu6+hn6BkWVdBPD8uKCvU1+DX1NHT0dHIydDZ1c3PzMu9vsm9vcPQzcLJxsK1tcG4\
37488# uMDTzL3Iw73OyLvLw7nNwK+sqau+tqmZl595dntoZWZsWlpVUVNVTUxIQ0M6NDclJCchICQgHh8dHB4FBAXW2+TRz8/Y1MnKyMXNxr3JwrjOxbe2tLOs\
37489# rLLIwKyjpKzFuqq7tai5s6SNi4+emI2clIiLgH1zcHt8dHJxbXJ4Yl1ZS0hQR0gtIiUYFxoNCwzh4+bQzs24ucXV0MTRzMSws8TIwrrTyrmurrjMxLXF\
37490# vrSxsLTLw7KoqbKlpKien6jHvKePjpmZmJaWkpCIhYySjYuOiIaZjYWIgYOCfHungHiTfXB3cXB2aGKDa1xnUlFUREh3T0MzLzM5LzA6JigoHR8ZFRfP\
37491# 0t3f3dvCx9jT0tbEx9TT09G7vs/Fxs24vM2jqsbKxru7ubaxra60r6eop6ehoKa5qZqVk5qblZWlmpPGto6dloqjlIaAf4OcgHqWe3mBd3h3bWuMcmqD\
37492# cGZqYWBaV1uDYVN4X1NfTk5tVUo6OT5ZQD1MPDpANDNILCvm6u3i5urR1N3W1tbKzNPU0M6wts7LyMvDwcPSy7+xsru+urWqqK/AtqyamqzOrqq8rqm7\
37493# pqjayafDvKW3rqG7vJ+zqp+upp/Ir5ivpJetoZewlZSJipSymI24kYuvi4iTin6RhXqvg3iScnFtaG+YfG5hYWqXcWRuZWR9ZmGMb2BhXFuKYFlzXVho\
37494# W1RaVVJhTEJOQ0JIPj9nQz3s7+7Dy925wdq+x9nBvb24srbYyq63s66rq63Qv6i9qKTZxqKqn6LFup3Lu5esr5aej5bjx4yDgoyqnoeig4a7qYCfjoCb\
37495# i3qkiHh5amagcWN/XmNKS1mEXExqRkNuRDpq42GmAAAD9klEQVQ4y0XRZVRTYRzH8YfpHBtzzAUbsQIcsEA21w0ijXSjlHR3p510d5fd3d3d3d3ddxw9\
37496# ft/+Pud/zz0PSElJwaWeOJGZ6eXV5Ok5/n+enp5NXl5ewMnJafm+g/v37dkzw93dfdLfpkK5u8+AAhtYLPutU7eaxBg7LFs27l9ToBwdo6Pj4oC9vr69\
37497# iYmxzfRxVlYwWEy6b0aVoY4ODAZDobjcKD09wGIy9bW7kRVMB87Ku3xdcj0dOWGCDoTCUFwI2M+cyYpxgHZDOBJJ6aBKZTKp9WTktGmQCUONAbtI6IB2\
37498# 1zWtz/wqkcnwj3V1J0+eBp0Jm6IH9O3sIh1sjLQ7Zm3l9mOdVwvxVZh/AmUFAbNIh+nhcKQpxsB0/fZjp84XyBowkNAC6DNA38zMOtIoHA7tc9fuPHw8\
37499# 51LBtR2Y8Og4Fxc3N7cpcAgstF5mZGhqYDA31G5nW2th4dWL4RiHR7sO8FxckkyQWoDFjoH5hIeHPxz3P93lv1nX5vnbp4de73WBABNoAdxg/nqSBb/9\
37500# 4umiInE/hb85apfe+727eW5wMBMALJYZSjAnW8ya7SH+XiSn+XngKpebJO1O4r2CgNlEgGWFhhJJi8nkWQJqEQ3vc0ZIiXV64hQdx0uaqgPAxInWTMZK\
37501# 4mJzS/NZbB+xWPguTeCMQCQkxBgbG48zHANzCSuJa9ZYkjikbf5ZyYIt0D47gWINC4PeTAuwd+bYziOYW3LI5hHJiRwEYgsC4VpNWY6CggFIrF4xhzhv\
37502# 1WIOmWy5yZVjsYDEdmW78nHVXC5340YIrF7BWDBvCXRhQ32qd3osAvqbxNnOfBxfz9HR0QaA+bYM0oJFi+YQLCy98096p1NiN8UnxnNicdW1tZ+aDwFg\
37503# e/d+xKIlEbYEEqIjv6E+D+fs7Jp2hF2Do3xuPlhQAJg72OuWLl1HtCVa1Hg/qEwVCeLjhXSVpKYOFxXFG+gDz3Jaty29t4TBILJ9qb77M0QeAgFNHRxc\
37504# l5z6srGxbwAcPdv2JuJICmMVOZsaIM/PvixMSxsMRquTPYS1Hxu/nAWtnW0vqlr4CXmBtJISekC3RCQS9SiUNJ8WYW7u0QM80OmdleEbKJUGyvuHRuSB\
37505# eLG/f++AQlE6iu++MZTb2wHEed1U/K1bN/tzTn37WUyT3i4u/j2sVKvKy+hB5ddunAGBFy5QAwKaL13JOX+l+HZQRdnoaBm9RKGoGBkevtmVKwM+Pnh8\
37506# aUB+V2lhX++voSANGh2iQSuVwWjqycymBj8V+JHooZS2l44Eo4PKystDNJqQkBB0j192VkZWtm+7Ag0GW+okSppcTqfTg1TqCnSFWqVSlwxKzp3z8xP1\
37507# aDR/AFuiV5UIiV63AAAAAElFTkSuQmCC"/>  \
37508# <a href="https://freshluts.com/users/1">Alex Jordan - FreshLUTs</a></center>"}
37509
37510#@gui : sep = value(0)_0+
37511#@gui : note = note{"<center><a href="https://freshluts.com/users/10185">Berat - FreshLUTs</a></center>"}
37512
37513#@gui : sep = value(0)_0+
37514#@gui : note = note{"<center><a href="http://fixthephoto.com/free-cinematic-luts">Free Cinematic LUTs</a></center>"}
37515
37516#@gui : sep = value(0)_0+
37517#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADkAAAAgCAMAAACfHyDkAAAC9FBMVEUUI\
37518# TIXITMfITUcITQfIzYhITYZIDNBMHbKPi/HKE3HLkPGKn9BL3PIMj/JODXJOjLHJ1TKRC3HJXxrOIfIJnZFLm/HKFzHK0fKPDDHJ23HJ2XINDzJNTnKQ\
37519# S7FLoJpPXYmJUgiJEEeIjrJNzcrJDTKSS7KQy1xOot1QIdiNIRWMH1KMHlFL3UuJlAmIjbNXTWKR5GFRpB8QY52PYtmNYbFMoRdMoHHJ357QHpPL3pdO\
37520# HZQMHUpJ08cIz7MXDzNYTdKJjbKQC7ENYbHLYBfNH1TMHpaNHlVNHZKMHJGLnJCLGrHKGrHKGHFT1YrIju+NzfNVDXKRDUzKTVaLDLLTDCRSZKBRJCCR\
37521# InGVYZuPYR+QYJyP37IJXrIJ3NQMnE8LW2FR2nIT2bHKklCI0MqJkHOWDfIO4FnOYBYMH9qPHyXSXpjOnnBJ3Z2QXJbM3FML2WUTGMzKl7BKFktKlg0K\
37522# Fg2KFJkJVJALU/KSU68KE7IM0szKkouJUeLJUXIQELMUDsZIzmuVDifUDiXLzh+QzeNSTZLMTaZSZJ8Q4vJToe+NIbKSIOyMX7BKX2kTHqsLnrLSXdvP\
37523# nZUM3BvNm9gOGyNSWtWNGtLLWuFMGc4LGeUK2evKWdyNWS7UGBXM2A/Kl/JPV5IMVt3JFmqVVSjMUy9WEbLVkO3LkJsJEE1JEDJPz08Ijk1IzZjOTU+L\
37524# TWGMTPMUTLJQDB/NjCgOS+/QC6kSI+HRYqZR4e4QIe1UoasT4G8MYB1PHqGOnfFUnWSOnWaMHTILnDJO2/KRm22KWynS2qlLWqeS2hnMmfIL2ClJ16EM\
37525# VlOL1m4VlO3LFKvJlLJQE2zWExfN0vMVUq/LUZ6JkTEXUDLST2uND2VTjnDXji4VzhvQDZaNzVpODS1QC6sPYefP4KfP4FzNni2Jne6UnOkR3GqNHBqM\
37526# XCuT2WIJGBlKlyxUlmMQFjHK1aIJVWdS1StTU+RM05VJE5zP0WCJEJYI0BTIzzFWTSmNzHHTC7FQC5jTTRlAAAEvUlEQVQ4y6XTdVRTcRTAcd7e3htjU\
37527# 8E5kBkLN2ADBgiyMWCEdJdSSiMCEkqISimK3d1SAlIidnd3d3d3/uP9bXAQD/qHfv/hnnv4vPvgbBqUzqN2EtGh/5A4jncCCaEwKKjx1s2JN4PUyu/8t\
37528# WsNQSpS+3HCpFIpSKiDCWosvXV74qQJ4687ODiYmY1vRHD0inMwTwInPRlz2czsa8MvkqAGlTYgceMGGLPEgPUhISEi0ffbCMoXOqwXie4AXCWvMBOJR\
37529# BtAoqSlEyeMVyqViYmJASBEOn36GBgY6KjaQFBHb5ZXBahG6YLQGGWIjs7GYLXkn69SFhVdCQwMbG7uqYlycuoDIb8xmDouIiJTKYLRkTi5ObIiUcfAo\
37530# EVIaJAkbur1du26fv26orqhekKaKPAtwoTI0LRzAU5OzU3C0aERMZdDnJx+BFMoIE3Z7HeFvaDevXv3gwcg73/Vv5UHU1dZRGYqAzU17zj6RYbOrQiAZ\
37531# YsQSYGXJ/tVoVYXFPDqyvcnVsx/+mBPoOr6N+E4IyNuVRGMTUSZRWTMWtj7w0mQdibGnosq9bRUnWAyaTQTYxvr/lsK0Xn/YGLRlORM+GO6+tcnwDNOF\
37532# cG2RKiWTGMbj6On4/Sgi0fVUsIa/KwQvX0JnAxPO3UF5iYiY0pyzIV1MDpS2qSra9SKMXQ6PS6ucrWpHY+/WmyY4tsF8nfE041SMy/AeLUuPzmc+zIPx\
37533# hIKDmmQfJDDXaOWjNKmQ5+kJPzPxJaH8rSgEjzfMGmu71kYL1EyLFMX+o7V0qp2xNUSH2lsM9zaPX2ZrjY0pgYncS9xhO/YOHh5R8G8cMWRvLF6etX1C\
37534# awk7so82BZIWyUpiAYpdks/7OOjq6s76jPOk1gd8qVDBdIcVmrayrMw1lAyWAo4SaePgZOtkuR5SqzFVm7zl3M4Pj4+Z+KXSnbGrkH3v1DmJSmOxL7W1\
37535# j5Tn+/iwl2JtgVEq8Qgno21mMVym39Q39vbm3M83eVx7DE4X4DnWLmlxa6BF6khFlspUtD2dB3eJlHZErGVIUuxa8TQofr6+k9m7T7A4XBG1QlsXdxnH\
37536# fDmcI778cRu3Ef74aUuEaQ6tRSstrZiGYan7hnRAxo6Z84+fX3vN5QcF3f57v0w5uKLrd1T0HZ5PNkmIUQXg7Q04u7tjhoyBPzBeL6tq2L7dHjScsJUY\
37537# ssNGwZjOd4u1ZTvBrK/0dzZMplMrZ9TsofbysMGdh8yIlfgJYlKmcqA0Y/sKKF8F5CDp8hnMFTJHvrZ29q6b5vOkMmW+cHJnWHDGLKZ5STWllqiclggL\
37538# UJ37RgIMRjlWLarhxx+nTHzA+lpEzVrKiwP12KdSCzDEKS5+f0BfaHZtXy2R9T26TAuo5raeCjChvXtOzOX7FQKFln2tzAfNHmr84ABO3LhQ8metw3GG\
37539# fH2Xh5s7lQY9xJYJxLiJyE5aNNWZ+clhL2JJ9sjYprzjBdUUxM22/3etGmz47GOsr2E8MHmcHTT3SVUjZEmbPRVZWdjxUxPJg1GLx72R4mNS0avu2UVV\
37540# cM+mkmDou0wDR5NNS7lY3+UkKBswYKyBNgWL6XRmNEj7WHHZ6LRrhj7q2zPPivLDhyKl5XFK4afv8l/7iccu3t+6IPmuwAAAABJRU5ErkJggg=="/>&n\
37541# bsp; <a href="https://911templates.com/30travel_presets">30 Cinematic Travel Color</a></center>"}
37542
37543#@gui : sep = value(0)_0+
37544#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAgCAMAAABAUVr7AAAC91BMVEUh\
37545# ISH9DA39+QAhJCkpKSElJSElJDMxMCJFQCE4NyEIXKcmPyIqNSH80gUTJrp8HLEmJ04fOEgzJ0EiLDUhMi8hIib8ahT1phACygX5xwQFsdYbJKUjLj8gN\
37546# TlfIiw2KyJfWB5oWx38gBhCSdiIG7tyGqsUH6TzEJnsEY49J00dRUr8Nzr31DA+JTAu5i8hKC78xyEzRh/3tBj3rBcArwYFvwRr0gEq1vEBgtEBo8wDcL\
37547# 4TJKxsGqPnEoPlGn/kE3cZVF9HK0ovJjYsLS4xIitTJicqIiYhSiL5mCE+PiFgOSFIRSBWUB/6vB78jBuEcBkHtQj5ywOT3gAAxOAAu9sueMsCZ7UUIbA\
37548# EU50yN5cdO1dWJUFKJTexKS4nMCr68icvKiNBLSJIMSEiWSBRSSBgSx/76R1lZRyUjhf8dBZg0wj37QMBuAKk4gF81wEBqcyLPskCecj4Q6buOZnWO5Ic\
37549# SXghJXOdJmhoKVptIUgbVz5SJT79jTd59C1HJSv9/Cjg2ya9aSZTMyK/uCEffyAfah5PWh6UGx40Uh00YhuDfRl0dRj6Uhf83QZazgTt9QGD2wAAzOadS\
37550# OKURNcCrdABw80ksMYtbbgTVLZVHa0bmKtwH6u0FqkLv6CkFpVnM5QZWZPPEocdK4YtNIMacYFjH3BPJ2ruLmkcNWFEJF01JVdTKlUfL1RaJ00HpkH1DT\
37551# 47JzzyijrlfjUfPzL16S7gzSv8fivetijNpSRz7COARSKBJCKXeSGgKCGskyBFkiD3CyBtOB8+VB39+RfU0RbGwxZMbBYMkxFxjQz82gjx1Af43gRGyQE\
37552# sseIXvN1dStM3aNCGNMYCkMQkbr4DhrhwHrh+PLfBPrNQKbAApq85IK4mZ6MJkp0OaYXxEHgpxHITZmoBtmgZTmD0TzyeFzQ62y1p3Cpm1ypixidXuiXS\
37553# 4yObYSOSUCLLhx5LVx6R7B37nhx9XxyNWBs+dhpA0RlYfRn8MxEUnw3l8gaOywaizgW15gMlxgLHnPo6AAAA/XRSTlP9/f39/f39/f39/f39/f39/f39/\
37554# f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f\
37555# 39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3\
37556# 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39wCUWrAAAA0ZJREFUOMt103k803Ecx/GFbLNpmTW1\
37557# piHLJmoSMxlmreauoQO5ReQoldwlUpR03/fpzH3kKDfRfd/3fd/nH31+3x8P80evv5+P9+f72EHoa+i0aVNNU1NlYyCZLCiQE6A2lKAUCAC+Pj71WfOgx\
37558# vpmp/QgTsAgg0BC/JLj41FPyleeckoP5MCQkkhKiI9bHHd8CGpddXIZoKABM2JqadLyJYvjbO3szuCEyaxOLn+LmT6hTpeAACAWn0SEuYDJZCaXvQklEd\
37559# Vw4cA/FnvD1s5WLOYh8t3AYAEguUlDqCMRO6XqQM+/vnn3Plsxj8e7j+64uQEy0DGpcg8hq6Ez/P2baDbcfTyekZER9hgDORg3cx0dk1p9Z5hRVacf23z\
37560# Jhsul7QFx8GlWVuNKuVz+0XWSuc4kkwYXshrcgZHdNlwazXDPwfgE+HCdmj+/Mxe4umIzNfokIkGdXhAbi4Qh7ejy4iRfmSw99FOtQIDPwGsI6sOXXbi5\
37561# kwvC8GhhYbGPb2pgICnUvQYnlU0ujkAOjx09bvTtndds8vkSicTUlMMhh+g3CQSjIP9VeiQgc4FMmT1s2KECPl9S6iANCCCS9LK7uzU1Z46aX6WvTJYNp\
37562# 9PpDlKplEh21nM/ux6RFYNJEZutrS3186M6uvyHHDqhxdbVZvhRYSQsvAcnbYgcxsgOayvrpd5augwgZu/DctpVIiN7NGfiz2WfuLhDY5b1IiurA4+NgV\
37563# AoZqtzwiNVsCL9w/XIBFV20XYvDdzcszeuANHS0dWmgmqtzA4Boq211EtjDmZYHncfJgYHr+3o+v1nAyJp7WHORILqRLb39itzwIg8Nm7cWtLZ+eNXVJQ\
37564# iCjOt/uHY1ziif+ayp6eHx9aRUF6UQpGnAJO2Kgz7MaCZA14aV7ewWJ6eezHSq8iLtrSM/pm2IgcbgWDG+845ligG0CML6G80iNzcD6+zV+M/THTKfpvw\
37565# vEgUE1OyEJphaRmRG5Hy/BuI/r/ARF1j+11CIUt0azKqNyKiLuXllxYzKhLoFGaObBMK91rMgCy+1qW8OL12DYUKV5SMFqBdDyagnr0CABMDAn8PoMTEz\
37566# MyMjIzM4OA1ZpTBAh9iMBgVlOlYFApVCfwDpbUmiFdSYzcAAAAASUVORK5CYII="/>  <a href="https://rawpedia.rawtherapee.c\
37567# om/Film_Simulation">RawTherapee Film Simulation</a></center>"}
37568
37569#@gui : sep = value(0)_0+
37570#@gui : note = note{"<center><img src="data:image/png;base64,\
37571# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAC91BMVEX+/v78/f4nKTn+/f4nKzspLTxJQkQlJzh3odlzndQyM0EsLzwlKDUlJzD6/f6X\
37572# xvmUwfWItuuFsuh9p907OkExMjw2MzUrKS/v/f6PvPGMue6CreNuls5wXVpaT01SR0c1N0Y5OD0mKDcoKzUiJDX2/f2cwfWXu++KfXOHdW+UdWiDcWeQ\
37573# cWSEa2JxZmF9ZlxpXVhtWVRUUVJGS1A9PkdDP0BAPEA/OjwrLTsmKTosLjgiJS/R9v3B6P2byv2lyPv6+/pvmtBskstNVGRnX116ZFx1X1hGSFdaVFFh\
37574# U1BOR0xBR0xeTkovMEBFPDw+OTkxLzcpKzen1v6j0v6gzv6ozP6t0f2fy/3I7fvr7O2Ltet/q+J9p996o91rkcZpjcVlisJfhbxhdY5oeo2ejYBbX29o\
37575# bW57YVlfXFdlWlR3XFNlU0xoUEtLTkotL0hTRkQ2NUIsM0ErLkFFRj8vLjO23f6s2v6x1f7c/f3h/P274P2w3/yQtuyEr+VwmtJwj7uJna1ogaWYnZtr\
37576# gJt1hYthboN5gYBvdn2WhntaZXpda3lsd3NPXG12bGl7b2aKb2R9bGN5ZmOAaF9NUVpgV1dPV1ZUWVJTTkxXSklbSkZNRkQ1NTs7NTY0MDEoKzG32v7X\
37577# +/2exficw/iUu/D28+7Y3N/t5NyNstrH1dh0n9fZ19XEzM7u28FiisFbgbhZfLSzrKSlpqDLtpi9qJW4oY2Ulo17hYhbbIaki39jaHlMXXeulHVDVmxP\
37578# XGluZmRrYl87SV9YYF5bX1tiXlVnVU8tNkpGR0dFQUY1O0FZRz83OzhEOTfo/P7n/P3l/P3o/fzy+fr8+vKo0/Hx8e+cwezc5+ql0er17t2gx9210Nyc\
37579# udyyzNPT0dKbtc56oM6Kq83z6MyEocqDpMOGnr3izryhrq7Vx62eoKuCj5x5hpvGsZpWdJqil5KomYazloKOeXKPhHGMc2tWWmsqV2qffWZxbWZdZmGE\
37580# Z2A2QlpUXVmBaViAZlMyOlJoAiFnAAADzElEQVQ4yy2TZViaURTHL/i+sIG4OYkVIKAbDOkWN91AWsBtujZm94x1d9jdrru7u7u7u7vjw+6L/j/c++H3\
37581# O/ec5zzPBRs3boiPi4vp171v+3YjhvtNnT596uShQydNWtrTk0lgQ3x8XGxM9yntV4/w83t7KJemNJt3Pu7Yo0ePxUvgsRTEr4+L7Qd5uxHDYn55e3vz\
37582# 3I3Uk4aZ10JDo0JDBy1eAtbHxni437AHx/V6PYVB5ygJisqkO9Fjo6OjokJBLNZ9NeST91D0FArD7co/kaWQW15NGL1w4djoKNA2XbdO3XIplJaCZheb\
37583# xaqVp1btGNy794TRo8cCyLHyTr5TeXpKAd3FljLFTOZBY/HzkBDMARjv1snXt+tIHoVRcKbByjSVlwmFQv6azuMXQQV4+NCuXbsYWuCAOcdM4RVCwad5\
37584# iR9WhAV2HhcyGLQbjvEuHVfWn2U0c+RMcblIwE/c+mLdMhzO5+K4EAC5L+QdnmaeduXX19XKjWX8XVufXcZ5AeADuwCsP+QdkjKtBGm4Kc0iFfN3bbmJ\
37585# AzC4wPPjQCvvs9ImY9WlSCRHU48eFMx78ygMM7zgEwCOB/nAh8aMBodEJLSkphlL+O+3rFvWKlwAEEM+4L6RoGJJSoMq0lIPFAvmJW7bdBUTAs8BWD5o\
37586# 4ID+08Q2p0MS1KvX5zJxyUf+9hmbw+AMuMDOwIPHXJkrPqlmhUOhl0giKhVsnzEE1yYMgnjijfnFUpWmDgpBonKmNVxwD+PQ8AkEEE+8VUSqkqmobGZF\
37587# qcnCYrOk7+AWsMBdgSfTdx46TiZT0wicBllKSobVwbZUrWnlwAve/7LRSC6PnCtnczSyn+np6cdkKcIVONihLdnZeWStFv26T6Th2GSm9Myc+sRNa1dd\
37588# D/MoXjiQl52HomRtrrLWSWtUEwiEnMxtcwrnTFvlWSVYDlAUjSCTecGjqBp6k4vDURP+7k0+kjztro8XDO4lIOpQMh7VQoPmzFGpCBpC0N4f1b9n3b4U\
37589# NmTI8rW7ARFBETyKx0fSSG4H25HhzPhWcthePWvujrkLEmbO3A3weAQPg2hH0YKDqeo/p63S6sN2e2FhQsKC2SNHzgZ4LjR0eIQ0KpgX6VZqmlroarld\
37590# qagxmxULEqBA5OoQBNGS/EnwW0WSaU59AZ126kR+lmJ/TY1h33wQoCPqiBH+/v4kLhS8kbMMKuOU/fuZLMX8I/lmwx4QEEAkQoEEBYTLRfD0ZhW1slFN\
37591# z1Imb042fCnChABiXgTJPyKAFozoUHcTg5pkq7QdmPN6RtL+WUX/AdPoMUu7nvi/AAAAAElFTkSuQmCC"/>  \
37592# <a href="https://www.facebook.com/eric.ellerbrockom">Eric Ellerbrock - FreshLUTs</a></center>"}
37593
37594#@gui : sep = value(0)_0+
37595#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJsAAAAgCAMAA\
37596# AAysvoIAAAAz1BMVEUAAAD///9AQEDQ0NCFhYU+Pj4EBAT+/v6ioqLk5OTi4uL29vYKCgrr6+t7e3v7+/sGBgYnJye8vLxcXFxVVVVra2v09PQ1NTXu7u\
37597# 6srKyoqKghISHLy8u2traysrKdnZ1zc3Pd3d0cHBz4+PhgYGBFRUUrKyu/v7+YmJiIiIiBgYEVFRURERHx8fHp6emOjo5nZ2czMzMvLy/19fXGxsagoKC\
37598# UlJR4eHhSUlLV1dWEhIRKSko5OTnm5ubY2NjOzs5qampNTU3BwcFtbW0lJSWTtii3AAADCklEQVRYw+2XaXPaMBCG9w2OxWHikDRgwBAIR7hvCFfu/v/f\
37599# 1NUiDKFM6TD5QDs8H+JIWsnP7GoHIJwuZ7fj3QoXp0fBuF3S6XF5dju7HcG3uP0IrZkQhUL+NVEuFHog5ioU8EF0EwyqOkR4nAxoRbWRf/XTbw5pTNxNP\
37600# UsrKjxK0wqzc1x2gqUV4d/c7rAmR6QQ4x1RIEHMBAEeUSQYLHWIIfZGTCtTgtD5QUwQ2X4nIcf/u30SYFDxguQmCPb3uRXDQmHX7ZYnR8AzP2rilg4LWQ\
37601# nJh8OZIstxFh/4qYq5XBwoleX1boZXnwGvRUxCzMekMYt5Pk6NV27RsNDY53ZPhh038QNuiBG3BzLoEF0v5xWwiD44YVM97Lqhhbw+ds2PJkf1iOkC6ST\
37602# aLeOW1Iv2VQmqLG55MnyrG9WBDC0USqZgKaKNG2XEnOw4StU8UNi4yV6F4kG31yvh4qBbV+KergM32wfqVNOCARs3x+StDIxoAbx8dWtx4rLa7eVKeP9D\
37603# L3QPuRmGTQnJ1Gr3MwV3QI/Ana5cWlhdqXteLQJtjiUfKOv9qrLtJjMX2s2wt09jcWF+0M2TuKi4GZKfpN10t84gbPepp2/h0oXHNnMg/9XNg1pqt1Jcm\
37604# OxzC9sCHXSrmkAJKc48wNeBpqbh0ch3jZt6mbXZRS7omI9IpVK3CsPstlvfhWppt5wtfGMvJOjSRTKrz1NwK6S5bhs3PoZdoi3drW0E1LfcbE734zF9Or\
37605# A1u26J7bxlJSOPph+H9YTjDCaciqAXblZX6AkbOra4tWzbqfi8KUVH5K0T0Qx23HhW01+7NTtQPZ2uvAJcz9N/64FbdQh3QVTkF6Q0lSRwKwV/jkR0bKx\
37606# M4haLCLm/czOkdtwMy7UbfSp4Uuhe0QXj+hUK3KgLRJ13BfcnCWlgFDSKiuWqhz6z+pY1DdzmVkNXwTI0iaqWVSZBZgW2KViWYyYrJPR7jUY5QYIlx5A9\
37607# t6zU1LIKtGLA8w5ZQmFqLm8iOLb3T31HOjHObv+v2yn/dj5Vzm7H8Qt/zkuzg1TiwQAAAABJRU5ErkJggg=="/>  <a href="https://f\
37608# iltergrade.com/free-cinematic-luts-video-editing/">FilterGrade Free Cinematic LUTs Pack</a></center>"}
37609
37610#@gui : sep = value(0)_0+
37611#@gui : note = note{"<center><img src="data:image/png;base64,\
37612# iVBORw0KGgoAAAANSUhEUgAAACIAAAApCAIAAADf4mxWAAAKkklEQVRYhV1XTYxdR1b+zqmq+959P91td7fdjp3EsZOZJNaAEjEjmAi2CCFAGjQLZsVi\
37613# diPEAokFYsNqtiCEGFhPpAFpAEUIxI8gIghBEqE4E0JiO7GdxG33z3vd/d677/5U1flYvG6PmaPSlUqnqr7zW/cruXh+jSQhpIiICmOM6nqwTGQAqmoZ\
37614# oiRJEwpWQmYBeDYxCqAGKEBmwAAAutJ7MSTLFBicJTiaiApSNjrniWRmFIAEBGJnO01ESAIQkVOdnc3FAQLAzEQEgDeB88HMet6VI2dSqLHtFlE0G0GC\
37615# TpSnZlMhICmiK3tXp5y6JCLQMwsAQIiV2huZjJLsucvbX3vlyxvPvlyqvvPOWzd/9MnJrA7e5aQZ5igAZOUWV7YqQNIewzgRAwU/Bn4smkGngaRaWh/o\
37616# 9vZ2v6eSUuGVzEwGmMA9uUdEACVPvyRBPqHCWWJAAcUopk4ASaIUp73B0Lte7JqubYXmnRM4YSZJMSJzVScigJ2N0zwLAZiQjzEAiJzCqxDCRGEoB2ub\
37617# F4eD9a5uLJqqmpkZnNPH2YYYxER/fJaIKOQM+9QPgcHI09pEBhWidIHJMS6HozVfhNQem2TAU+h9Z3DeOhV5HJOcsxr11FYznFaz8HTQnJytV4KkCiAE\
37618# SfFhMBg5J03T5JxVIVBAieycO2uPswpWWU1NTn2i4GwYlCRXASOphBcz5xyZvSsG4/NJbFkvVF3wCgDiBBRR587idgYGqqpmJlGQRiPIU7zHFQEQEBG/\
37619# QgPgQlGO1xcns6ZpiqLo91eNqaKSDHYa9Cd8AgAEURgBoRBnGcog1JkaqSBJ6mq9iPhQDIbnEOt62Ybgt86vJcIEOWcz2BNCgCuwbKvIqMA77TlXhlAE\
37620# 3/M+eA3qgrrgvXfOq6qqA6DBF/1h7qqmSf1B2e+NLUMKJMtBC4o4QgmqOOcIWKIKDAYhIA50KipUwKAEM5lXuQU8xRmFFFXvQ1kvj9smPvXsJbFeSkYx\
37621# gVKMJCAkxUAlAYWtGkNFvKBQUcFKQwglGySZJRCi3gwilixDJBT9ZTWLZufPbxxPmpSSGbzvp5RWoQdgZkwEoISHd6IK9FQLFUcIIeLESWeg2spbFXgn\
37622# KRtgor4sZLactYxy8cL2wwe3Vb1naDWH4MycqQR1UUh0AwhQWO6cFN5b4VhCvXphciKWQ0/YJBqdeN9Y9CbiCBFCnNAmR7u9EltbG/PZTFUz1dq4rBdG\
37623# Z5oVaAmV0FlUQc+HHpLz6Pl1r64Y2rpfK3WwkKqqsplBXRW7wqtPmYUKmZ0LTHW1aPuDwXh8rlrUIYiBX3v1xa+/+qU0ui6aNFbLRbVc5Gpx3DWT9mh6\
37624# 0rmQm3UUIetgbbh1eaev5f7ew8n+0d7kcFZXA6+VZW+r+xw5hF5THU+my9HautMiNtkHzTk+f3XzV3/5Z6/+zG90kTTnNGWaifm4m/Zu3b93a/LFybIj\
37625# jGuDzfXLLxVr5+689+6bf/XG8bH2QxG7xtG8emdMJIog86ODRTV78YWnhblqaqW1WdB13vUXi4g0hxslOLMmSZFwqXfh0uWNy888+8X0we5sEU2KcqM3\
37626# 3Dp346uvHtz68PbdT51zJFW8V6GZgBIkV7Np23U7l7abpk6pA1TMeiFsX77uR6Ucvt1V2a+/EEZb4gqhNS1l+NMcP/fMU/vN4afLWZVyk/bvpKb66s/d\
37627# +Pe33757/7Do9eu69jlHQRBxqV0cTQ/axi7vPH00mbrgVSGA9zLcerqpln/9gzfe/Z9bt+/ONst+Ebh15crvffd7IVg53Pnh9/7y337wF+vr66/90qs3\
37628# vvLMYlaPzunLN57/6M6u82Vi8kKDqIhrFkdHh5PQKy5evPjfn30izq3+jCGE0eZTszvv3/70k7sPjm5+dM8lXTT1jRvTjdJ1Ohr0yzv37r35Xzevbl37\
37629# +Nbd7/7Rbz17bbxc1Jvb52KM5j1WlIdi6tzJcbN7OOv39Pzm8OH+pO8D0Weue+UaZG1+MrHsRr2B9EIYluNhOR6Pp/f+AbwHMy/FYKypxO7e9ESu7Lzy\
37630# 81deulquh3lCQk6EFxFVOEXXtccnJ1tbO2UxnB8dlz1HpAz0hwNBrGaTxWK5vbkBQ7IULSfjP/7TW1fPf/Dar/xa0xzGLMSxcD4eXkDxU8Xlq9h4r7LY\
37631# Z5HR82ZGJrNUNfXJfH5p5+kU25PJxPtCNGeBK88Jw3TvQU649qWnCgmUlBPVhf29+Wd3duvuZPeTj2r0Z11hZeE3lAJwnLDWCqL6SPOqzkG1KGhaLXnt\
37632# y9cXTZsRisGIKTqouJEAB3uPIO7K0xcLCcmiKAEs5vXB8cnN/x09mlUs0LRdQzIEoBMU0VynoYpdZ+aVSogZDg6PF9Vs/+hvf/g3/zo/qdt4NCiL2NSu\
37633# cJB4cHBQjobjjXHbtiEYU3LKxbJqq3S0OKqPzayrgsfMtQ9v8ivPd3KhJUl6DZ0mLyJCEe/rmJuufTS5HxOchixxY9RnRq8XECcPH00uXNgO6qpqvrk9\
37634# UtXgpE3N0aJmr0ySiCKbpJ57+63/7IfFy699s8+lGQwZSB5GY3JeRJV0DuiXvdgmXxRmJlBIaBfTw4Pj69dfqKsGUIorfBFCuP7SC58+fGd+dHgcq2nK\
37635# A8tlP9z//ODNf36/OV4+uPMeIBEZTB4AkUU8YRmE0xQbBbMpnO9I7/3+g0/qqt3eOncyXxRlH+q6yF45/O3f/Z3ndr7/xt+9GRv21StjJOYp3nqwF3O6\
37636# ffc2NFmC076KiPfeCMsk1cxM1RB8Uue8CDU2+3uPmjZubG/vHU2UKtHlvs5n8fDzz7/x7d/8/T/4zvnNdabsvF8rtK4Ws1ne3T+cL0JpqhQi62OysxIv\
37637# KkZVBcCUlGjr+e7nDyjh/Lmdw4MT8c7gveuT6U//5M++/8d/eHFnsHVlPdGyCcIgEfP5o8N5dTSfaJE1uJyz/0nqbsxmlAzSNKn4anH0MKXhqPTeT6ez\
37638# 0PMiatkVXg/2Zx9+cPdHH955eHffeUZGy7Hw5d78OGubDckgKUoWPSPVBCCWBc57f8q1AKo7OHy4t3dwfms95a6qKjjrUlS48XAQG8zgPv74sDpuxSwm\
37639# QNpvffMXvv7KtVzXdd264MUVIk6fQMHqdAKGTEFMJqqH08n+3vTS5Uuz2axtojqSKcUaaNrYsF5Gyx068Y4SoDJaC9/59q9/6xu/uDEqSUU2E9OfCJqZ\
37640# pZRMICIxw3mZz6rp0ezSzpXDvalFQzZ1yWknmlJeSpKubjKRjYzLcbn2+uv/8uev//2LLz5z+cqF1HWCrI5P5EYIQhyQ4VUsw7FnqOezhk53dnbufPwR\
37641# SOdC19Q5x6IY9PvhcNH46JlU1TufBr3e/rR+6/3/uH3/7he7E69wznVd/H/eiAhWfI60lL0WzNY0yWkYrJXH08nG2jjHzEiFu/XR/b39+fG02z1Y1BZF\
37642# LYs8ms2alHx0736wd/+zaRFKy6JSyPZoDA9VTTmvnkunTNwHiwlAEVxZ9EbjUuBi200mU6gIXJdTjFGUKt45pw5Q5JxzzE57BkJqVW/ZZetkc22oqhAx\
37643# swyG1VPs9C2mZGY2VWWOJL33q3YmmWgASJNVuYpAGHNWSuqyCwXRkGLZmaX/A0GLBDxi5LN0AAAAAElFTkSuQmCC"/>  \
37644# <a href="https://freshluts.com/users/64078">InAvision - FreshLUTs</a></center>"}
37645
37646#@gui : sep = value(0)_0+
37647#@gui : note = note{"<center><img src="data:image/png;base64,\
37648# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAC/VBMVEUEAwju6+4FBAnv7O8FBgoIDxju5+oPERcICQ8HCA0GBwwLEBkRFR/u6e05SFU6\
37649# PkYzO0YSFx8MEx0HDRYOCQ1aXmcpOEcgLToJCxFHT1lFTFUtPUwsOUUhMD0YHCcTGCEKERvt5OdrcHZdYmpUX2dSWmNKVWBQVl9EVF4/T1o9S1c3RVEz\
37650# RFEzPkomNUMlM0AqMTwdIysKDhUGCxPu6evDtLh0eH1ta25XXWVOXGVNVF1JUVtMUFo/R1ExQE0vPEkqNkIyOUEfKzgjKzYqKzIiKTIgJS0SFhwdFxsQ\
37651# DhMLCg/ZxsjIu8DIuLq+sLO5rLGwpKZ7foSThIBjaHFpaG1lZWtZW2NLWmNTWV9QVFpNTFA6Qk1MRUk3PEksPEkuNkJBPUEoLjobJTMmKC4rICUgICUV\
37652# GCI2ISAgGh4QFB3m2t3d0NLVwsPOv8O7sbnOtbLIsLG3oJ2mm5yYk5bPnZKGh4l8dnm6eXJwb3JmaG5ZYmlVWWOIY2FKWV9DT1pATFiKW1Y7RVG4YUqP\
37653# UEE4NjscKTcdKDRwODAeJjBFKSoTGyQoGh4xGhkgExQUDRDTwMTLvcK4tMGvr7zKtLSvqK63qaver6rAq6q1pqepoKGXlqC5op+zmZOVkJKrlpHKl4yB\
37654# gIXfj32JeXxtdHh0dHZwcnSmfnJobXJoa3DEeWxeY2xnYWhSXGWfa2G0dV+4bV6ybl2QZFqZX1m2Z1WBWFR5U1CRVk5GRk5lQEFWP0ExNjx3QzhbOTc2\
37655# LTFOMTBfNC0XICwdHyo1JSc+Hx04GxsWFBns4OLp3N+/ucXAt77MurzQuLjRsanFqqmfnKbQrKW9qKWqoKLCpJzZp5qfl5nFopi4m5WHi5WgkpHfl4y7\
37656# kYWch4WYhYF8fX+DfX6Jfn24gnt/fnunfHpeaneRenWqenKCcnKkeXBYZ27BhW2Xbm3Kf2yTZmZyYmRhYmN2ZGJuX2C/clxKTlqqalhYTE4+Rko9QEpv\
37657# SUaBTkU2OUFNNzY8LzJaMSoaIyUoFRRPuIDSAAAD7UlEQVQ4y0WQY3RjURSF77y+l4ekQeM0TdJYbZPO1HabqW1zbBt1O7Zt27Zt2zbW3FmZtWb/uD/u\
37658# +dY+ex8QUSoMDAxiMn0Xzu3RgUajtXN0dGxjl0MbBwcH0Li3KCfIzdmk8knv097VTvwfQyCCLcnOczeq+Bk+N0627+D6D3CEAIQgEBakyx7uru2fnt4v\
37659# 9dL5rtAEInYLO1AUmufm7MzUZKgy0udf7tEj0avjYWhDo7nS7EBJkZu7i8DIzxzIV/VPndl9StduiV6dxhyL79y5oyv0AaVCFw93qWmokck06nxuzp7R\
37660# dUq3pKTELl4JCfHtj3ToCGqlppBQaQhcE8zUqvqlzp5xduqkSRMTJyR4He80ZuxYYAmWuki1ZmdBodkwWD1gyb3ePadNm5qcnNRtwuQTCfHxoFYoFIa6\
37661# ObsLgs2GLPXgLL+nix6+efs4tXuvUxfOeHWZDmpHh4fDGIKQQrPWb2WWXq/ZWBVbHtu07tnMXnOSrriAvaWjw11gzsJgU4BGbdDrc1t4vNhDtt0jF6++\
37662# 3uVBISgNFwo9BAIBc6hRO4Q5RG9k2XitzVVVLSNXj1g8btx4YHELDWFmqf1DpAKDQZmi9sttInktv3/W7Rm5Krnz0fEgIlxq0gebhvqp56YYFviZzeyd\
37663# X3c0HzxQt667d8rpidNBvcVjdKjH8AXKnhfP9Z6lFK+YP2jzmrU79jzq1DNsyEptCpBFscXeqt6zvG/3ueozCufNeb5+zbu1H570Sc4JioqSMUB9hHjA\
37664# ALHmjs+i1ywSAWDQtvUbRsx7UUXimRYZ7ikCubmSwADvpUsXZn+KAVCxW5dtGFF3AKGI6PdicaAvSBNn5ATcUmoirFYUIAio3Hpt3rKDCEoSeGAmP00J\
37665# lvfr7z04m8W2NsbABQixj7d/1bA4gJKUiDWQr/QF7C1hkrBoOYfD8fwLMHD4ogSgCJT4rOMv8Qf1siiWNQanxzBEMAHPhsRFHqJq4AqEYOmW83VAVsmo\
37666# lFVyoEg4v3u/vLqsjEVQNhQCA9MkvoAhp3PkcjlHzkABqO77cr8nhX1DCZJCyDBNWh4fMBg1jBqc7kkXwQrf+w5rwHFPGYB9EHS7vy87E9TQ6QwnOr0t\
37667# RsC/TX0bmnCRaNcPkiQpKjpAZ8kBXC7mBAG6ApZANm3+0ozvHjZ5G4qiFFUi8S+RAC6mwLgYxuU6VVTj5a0AKd85qFcriiJoDDvI31IAuG0xblsoLLL4\
37668# 4/biONGujQ35r/KrAYJGF+QFsEsAxlVgmEKByUflR0b/Yo0qjgUVKyqK8wEqiiqQFFmBAlM4/VVjZJm1rMIWBw9JbImM4wGEwtnDC/b9AdM3HYNA1prq\
37669# AAAAAElFTkSuQmCC"/>  \
37670# <a href="https://freshluts.com/users/120">J.T. Semple - FreshLUTs</a></center>"}
37671
37672#@gui : sep = value(0)_0+
37673#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAMAAACf4xmcAAACMVBMVEULF\
37674# iILFyEOGSMJFSEOGyQOGCELGCITHykLGCELFh8JFh8NFyEQHSYSHicUISoNFyINGSULGCQWICoJFiMhKzQbJS8mMToKFSIdKDEZIy0RHSchLTgZJi8XI\
37675# iwTGyQQGiMpMzsbKDQYJTAKFiQ4Qkg0PkYlLzgeKjQNFh8yPUQfLDcXIy0RHisQHCkWGyRUXmojLzkjLTYTIC0PGyceHyTr7/jK2eyhssZIVWFDTlo3Q\
37676# 047RksvOkIuOEEtNz8pNj8nMz4eKTMaJzITGSEQFyHe6PTV4O7D1+vE1+a+0OTH1uPCzt6VsNGdrL2PlKNhfJxqd4ZocX4+SVQ6RlA3QUgtOkclMTwXJ\
37677# C8aHybl6PLT4fLQ3uzM2erR1+O5zOOvxNysvtSEps2jtcqNqsiAocecp7hohaR+jaJyfolSaodLYn6DdX1kcHtnZ3RZZG9JWG1LWWVPWWM3TGMyP01GR\
37678# EgqN0QxNEAjMT8aKTdFNzEdJSkzKicXHyHk7fbG3vbN3PTW4PHM3PDn5Oq2z+rC0Oa0yd2sxN2ivdy6yNrLy9avvs6otsmTrMmmrsCUpsB7lbd5krOMn\
37679# q/VuJmFj5lsgJlYdJSDhZF7fo2ciIxvfIuMg4Z8cYCDbndeanZCWnNiYW85UW9OVWlhYmRrWWSJdmNiWmFQU15LT1xXT1oxRFo8SldQR0snN0oyPUdTT\
37680# UQ4OkRoT0EeLD5IRD0uLTcyNTVWPzIfIzA7MC4sKCsgJCokISPHXUUCAAAD5klEQVQ4y0WSBXfbMBRGnxRbppniLJx0gSZpk6w0bFfumJmZmZmZmZmZG\
37681# X7dnqLBPZKPYl19T1IM4PMB/YcBGd0gHhCZ2kTTDJtWJfv22/2LW/ylLDQZdM02dCA45ppKq/r2+3BpN6CFGiI80Bo/lgjouFBHTZZR+7qzH/g4clITo\
37682# gzu+vVvMkBsaggNi+7s15dreqawR0Nsm9KQ83B1vUcAqA18b6h1f+/uAUDNy+g+7gNCb9/ZohKqyVgZV2Jc949kD86GdJ/nAeLjXX3x+JvnUkNiPkIMo\
37683# jJmzNBs1CCkY9hfqp5+Lm1udChzfCoxDAMrJ2XUMA47j0LfrTt5Y/Xa0acm9LYzoPlIIdlTmFFlaKihI7ale+6Evf0PzZx7YM7ARjmEe9YL5pdPM6pk1\
37684# IDHAYe4U8cO7d9/7sxhC5bvcDyZkUKg77snj3r+pglCGTZw3ohZJ4YOX3lllQNJanfv+vl6yx7ZIMAlnsbJeAMPzh95es6YB+fukRDB7WuFAj9GCAjhj\
37685# sBlAxcdPTJq+PFVs1+Cx0+laTpCdNR0DiAE1KULRgwYNm/MyoubPJfoREMIERpHJSri0rGjhw2YdezC1UWbXFtV+aWhhirIMpUrMMYcZ/mooQNGLj4/f\
37686# ulElzJbNiqgDZQjNOpIN2f3HzF/4Zjx97cx8VZggEp4OLUqRZ3ma6OHn1mycPzaXY6NcQSNCkAQzeyt65X/iz0/O2DxsiXLroeT+IufUcA1zNMqd+yBv\
37687# e3WuP2H9417z+wMIEQgNB0712wA69m4USPHTpzM+Ecl5jjAs4iKI57mQeOGDVOb8oO220zHGd6EpqoMG2PYwXUdZcKgmLN1Ui3gxH8IUCoJKB9Rc/Kgd\
37688# NO6urxDKWNiNQ+CaNQ0eeuFmKaU2Di1nB60rrHp71Jhg1+gKKap4ENpHRxT2qelTL6Mi8KFSCSSSCT4g8t+q94pK1asCQsoJvcEEO6NhMPhThxEEv743\
37689# eaS1T74bVkRYGpFCwgaGgKBMHrxNZPjVmzFYMvycwktoQVzuYaGXC4XzDVwr7NuRcKyJq75k/UnjEIw2CdY6blAJy9/eVLAsgbXFYUnwvAIfThtbX2Cs\
37690# Xw43jsS2DgpGPHXNVuWhZY4BJMY1LS1ttZks63B2q54vFicvjnvN3s1x5rEBVmYho1CS3ZKtjpb01bfmS4Wi+lXZWASje7Y7jBGJSVuYWGJMWhpaclWV\
37691# 9e019em0tPT6S5L6RVV0ltLrhS1FKzMs7Bodkp19ZAhQ9qnxWK1qVRXKh+bXnKcUpcLUI5iZUlCD/c2pQa9jo5pHR31tSjmU/l4vAwcH7gsKjHqMvob3\
37692# PCdJnRHhgoAAAAASUVORK5CYII="/>  \
37693# <a href="https://sellfy.com/p/SGnG/">Kyler Holland 10 Free CLUTs</a></center>"}
37694
37695#@gui : sep = value(0)_0+
37696#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIMAAAAWCAMAAADU6N+kAAAANlBMVEUA\
37697# AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAABH0wLDAAAAEHRSTlMAv0Dfn4CP78+vIDBgcFAQ4LAwBQAAA\
37698# opJREFUSMeVlVmy4yAMRcFMNuA47H+z/TQgMSSp6vsRhAT4IAQxoBMUill1gdCd/Z+sTagI4iG5ettQztrbmOpAV1+h4oQX2uUF6pFy3BEUqHs00r0yoD\
37699# dZe7ZduE5cImdlwzIh9RIwPQ5tMEuww5wMI2Nr7/ebJz4ehLg3+BvrLSJvzdGtARQaiRgsdml7nlcLl6xIMUy0oy4xVCIvnQ1iu1qLSQFE3cMML/mQA4T\
37700# OOc9DhtJGhkDkHnfx/qrmZgBJ0cjgpO8uwcN2ZYgTg2Xcxzzt/xl6mzilOprsnYHOwvWoJXaO9CU4qke+M0h6+0rEcCrDNHhZr+ih8b3ovdLZ8FfyuDNQ\
37701# cS+1Wem6qWcMTugJhnphyGNxlFsPUKzrOIRBvfZPMwNeq2v0zQxq00siS3mj8K1V+cYJvr522MlQJ3bGhOYFQUplcDNDkaUqMejCPM572Ttcm8+VNZ69f\
37702# /CC7Qz7TGII+oGFQXaVWrd+MWg9AoKW+n59NwYrRxFAyiC5K2PBXP2AvjPU/Cc+1P3jc+HMDKplZpAS8UYZoFXhxRAL5KJ+c2p7w0qVGVTKoJmV1OSRQR\
37703# PF0t5cTiLuqpeQV4b9CXEvKJE+Whnu8fk/ve764sCSF3sLw6RfDJ32MMLgJgbbD+iG9ET5o2BLGW6zFPtvhv0dC/pkEIMXhu5EyZejWJK9kaF9ZIirUw+\
37704# Sn60yMlTsKEOcGVJWhkZQwtAWBmXMnbjLcou1gEqtCcNDNjA0bJmB3UGsi1vDyrymnas1HTydwzGAsk/UrX164Dh2Lurkg1rGrGDj81QaqdrWM8mYHpSL\
37705# ayobMwVfAVSz+arDgwrt5wDlbgjnCaqAY0EvUwLo+bAaK/fYPzbBi89MLkUDAAAAAElFTkSuQmCC"/>  <a href="https://lutify.me\
37706# /free-luts/">Lutify.Me Free LUTs</a></center>"}
37707
37708#@gui : sep = value(0)_0+
37709#@gui : note = note{"<center><a href="https://michaelezra.com">Michael Ezra</a>\
37710# </center>"}
37711
37712#@gui : sep = value(0)_0+
37713#@gui : note = note{"<center>Moviz LUTs</center>"}
37714
37715#@gui : sep = value(0)_0+
37716#@gui : note = note{"<center><a href="https://freshluts.com/users/992">Ohad Peretz - FreshLUTs</a></center>"}
37717
37718#@gui : sep = value(0)_0+
37719#@gui : #@gui : note = note{"<center><img src="data:image/png;base64,\
37720# iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAC/VBMVEX9AEL7AUL9AUP8AED9AUH6AkL4AkP6A0L4A0H7AUT6AkD7AUD2BEP6AkT+AUX0B\
37721# UL+AUD4+Pj0BkT9AUTxBkL3A0D9+fv2BUb5Az79+/r/AEL6+fr6/Pf81uPlD0b4BEXuCUT6/f32+/j8+Pjv8O/bGEnhE0frC0T2BUL++/38/fn98/f96v\
37722# HML1XXH03oDUX+8/z99fr38fby8vH83+nk4uT50d/4us/uVn7tPWrrM2DqGVHSI1DoEUjzCEXxB0X5+f397vj88vTy7PP58vH65+z62ur84+jn6Of5x9j\
37723# a19bTztD0sce0r7HncozkPGywVGvrNmTkLmDQNFnCN1fZKVbRJ1TsEkzyDEjpDUfyBz77Aj7sCz39/f3/8vn29/X79vT39vL19PL77u/96O7t6+vs3975\
37724# ztzOz8/TyMj2sMPzqcLzpb31oLrDtbnNsrm7u7f1l7P1ka7xkqvvgqOtnZ+0m53ndJPtbY30ZIazeoWodYPmWHylb3inZ3euZnboSXTuRnG6Smm3RWDaM\
37725# mDrLlvFNVrsJ1rlIlfLKlTqH1TvGk/bIk7pGUv5A0niDUP/AUPnC0HuCEH05ens5eXb3tz6y9vtz9fk1Nbt0dT3rsvQwsbIxcXqrcLGvL3Pt7r0m7byob\
37726# XFra6xqqzOnqqtpKPtjKKolaDJj53udpvahZmkkpbvbpauj5PwaY/lZI2egoi+dIToVIS8boLFYHvjXHu6YXboRXS1WnHjRnC9T2+vXW7KUW7XRWzoQWy\
37727# +QmCyTF/mK169PVvtIVPdIFLjHk/jE03tDErZHUn0BTz2+fr+5/Xz6uru6Oju2Nvv19vh2dn2xNPW09L4xdLY0ND3tczjv8jhxcfWwMbhtL71rL69t72+\
37728# vLrSt7rfq7nqp7fMq7DVoK7jmq68oaXelKTgi52nm5rMkprNhpa/hJTngJTmYIvgboquhYmmf4jqY4KxdIG8aYGcdn3YZX3QaXvZX3vnTHfrT3WqWmvjN\
37729# WjOOmPJOF/GM1XYIFXtDUL6xIL1AAAFQklEQVRIx6XWA3QcQRgA4J2ZNc527KRtnDRJbdu2bdu2bdu2bdu2XueS9vU2TfX6v3f7bubm239v5p+5IwAgCM\
37730# JC/HWkB/8ewB2e7QzGyEawLA0YiPugQjsMut/8HgBGoJQRfln79y8T7p+ipH8eb7H8ACSJoOjoU6zz4pszpl5uOuv+qtIBGi3uJjIMEo/f/7nHssIFfNU\
37731# +matUCTFXjRw3a3MSY0K/+r7KoTueXMpXc0zzJW+Kvi8aN79BVDWfkTd6aTIGAEp+71rXyVGoe9lkmoNaQIvD+y6yqw/U7SaiDAji9fs6N8s55u0IAIAg\
37732# CAQSBEQYd88JNYd20ul/FiDQr0uzyEIDRB4CAiHkfpEWzhnwNG9Q3rUigjDdzKLhxQrnnDDARA2RBATAtyLgJE2l1bWCYvoByMkAx4ml7kTm7xfIA47j9\
37733# w/33zcoPNzPX6dQQP2XIpkM01IAR3pOr4Lzf3Gi5hanRAHgLB+2rkObJk2mzLj72qiAApM02RoSzylkgHCWmlm9eQVuCACUf7GFBX19zEGV1d55XRxEgm\
37734# bzqGzTjQB5AMA6uozO3ZungIWM6FLY1yfqdONGhexZco8QAL6F45ZPre16eUmWaV21kYsXAMmGtfY1Ty9Rjg7UJXZrrOPdQLnpULV2ckBvnZQ5jnIXq25\
37735# jAYM9iSEtSE+LLr0bACbrGduFSjIQsWZ0VD8thFDheuYbNFkHaKgSAEWlDaIc87zzDJYtg//jGrEpTggJ6OqgrhxTlhPx2n+vYsDqntcI7S0D4W3U02ga\
37736# A0Jck8uQ+UFSMsC91HfAbDicubvskbI2CWqrJyGEDDtovNWr2tVeAXqa/ZYC8HSpY+Y44FFPVNikoKWpgGCVq3N7mW11FvTaywhEGgBU2Clze8YTfKpnW\
37737# GFSuQHD6jpFmYPVVY8u2CWyAKUSqXR9qxyULGhYwavSCo5M3t0yr9rLZhvbqZKe4oEbhNU3tAfpwMOK5LcHYHhlQota3gav3EWUPOXukfrUt8alA9a2pj\
37738# RAQECamJTijUK8vA4WZxgIgUTvqGftTiDPWZpobilJGAAAIIRYcXuLhHgZmgdCAovAHgVCE2Tr4HfN1kBJYyBgkBpGTfkr3sHHyxEQA93GfHkGEp7hmGe\
37739# zl00DNEwlTo1upW9w/nKp2tHRN9YlyzBiVWT2eMm9mY2lXSLExyRkhnasHdzASCuGQSZrG59WogxQH+rYWikx4CNW3i65J9kppfj1nK32aZcKlFunVFkr\
37740# yQC967yt7mCEABvRocaR68vXbejacWY+dd1EmsY1PLRrdP6yWhlgNa+yqJfQIq0sv7x6cHD12qPyRVbOFtubggQUNaVmZ1lkFJHnnqaYQWO97QMlmtKUb\
37741# pE/KkdISGieCUsTWdJCQMa/cwF7Isd5AkAxmke5srUsx0Atp4zYWTw+PmFnQAUTj/BnAdsK52pn5LSyUwMAsKdhtixtKwgizgJ4nmXdVwAgUann3JwNk6\
37742# BWm/5spUvEeNcsUt6owIcXoVKpIL7i/RTQc2F0zHYJKhTpAGnSrR9pzTEncQjHQQInTy0QjV+xudF5EjSSFjfThYDoLTFq6/iX4Q4W4AxIUDrCty27mPN\
37743# kAoM1Bj8FYvo2zuGd6+y9TR/DypTp36dH18XNokNbDFCSMGOA7xoQF5vZoK497tzUpk0n1ovOkr1hfIWKLPELQJJICHQVbxVrz54JR3Z7ofl9dYGy9crg\
37744# d4hkxeTBJYp2W1+0xEAnRapY8If/ATxLEUJFk8lUUYUsBCmwiIC/JQhhRgAc35sY/FP8L/gKJ0Uj1jCuNAUAAAAASUVORK5CYII=\"/>  \
37745# <a href="https://gumroad.com/sarikasat">Olivio Sarikas - Free LUT Packs</a></center>"}
37746
37747#@gui : sep = value(0)_0+
37748#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAClFBMVEUA\
37749# AABzk3pTeVfj8PHk7u3Q39rp9PW1x726zcSUrJt4l3/Y7fLq5Ojt9PTv9fPn9Pbp8fHk7+/h7u66zMO5y8JtjnRxkXdkhmlnim7R6/Hc7/Ln9fbn8PDq8\
37750# O/n9Pbt9PPR4Nvn7ezg6+zO3di3y8K2yL64zMOZs6KYsaF1lXyu3vDq2d/o5url5uru9PPo9vfm8/Xn8PC2yL+3y8G3ysCkuqtmim1oi2/0PVwAsO7m8f\
37751# MBru33O1kGqe3r8/QKrO32PFrp8vT5PFjzO1r3N1YIp+zyOln0NFbnydHn8fLB4/ABrO4Npez2PVv1OFdNTU3j9PQRoesFq+0LpuwQouvpvMaCgoLzZX1\
37752# SUlK95PG03vB4ze9Rve4ZsO4HpOwVnermx9C9wMHyc4jwbINra2vV6/Kl2PGs3e9aw+4gsu4Ire4vse0Mre1AsewRqewVn+samuoZl+nn3uPW2drstMDo\
37753# tb+nq6ztjZ6HiIjucIbySmX0P11YWFj6OFT3MlLs9Pbm8/Xn8fTu8vPh8fPB5fG34/Gl2/GQ1fGb1PGf2vBox+5vxO5Uwu7k6+0TsO3n6exRsewDqezh5\
37754# +kSmenc4+TqxM7rt8Lsp7XwkqOen6DshpmRkZKAgYHvXnbwWHFnZ2fzRmP0QmBVVVX3NlX5NVL5L07j8vPe8PPM6fJsyfGDyPCs3u+Dzu9gw+9Hvu49uu\
37755# 5duu1Que1KrO1Ku+xJtewmrey/1+ssoOodnuoTkOjj4eXM1eTS1OLa39/k2N7Y29zbztnl0NfjzNPsusS3ubrrrbm1uLjnrLixsrLmpLGur6+urq/snqz\
37756# olaTvip2WmJjxfZHsfZCLjY3raIB9fn7uYXjxUGryTWj1O1r4PVn6PFdLSkoQ11vXAAAAOHRSTlMAGwXd07u0iIRHJv796eTi4tjXiG8eGQ8M/vvp3dnU\
37757# 1L+6urmBdGxCQRj+/vv76+rn2oWCgmwODToLEi0AAAKySURBVDjLZdNlW9tQFAfwFCiluDOc4XPf2hQyUqF0pe3oVvcWdxhuw204A4YPm7u7u7t+mSW5G\
37758# S0Pv1d5zvkn5ya5F7JaExftGO7uGe4YHbcHWo3itC0oS5N9tbVbczYoxIkCrWQX69aa3SdkMns6WExhX4/KLdbOtr8uyiVbyMTkq3RMnFAbSLV5iLODOo\
37759# eoszTdTJJuvYPz8v0OWfmgmtOWy/wvt8uBQs6PUpN9YdcFplVuFhWsY1dLDgu42C5k2dAF+uJ9/63lp0F9sC1v7wpaN3yI05OlRzop1peeR2EgYx+gyvD\
37760# Bvp99db2iY1AqlRZmCMjAqcJEQBuyG4rf9PvP4tO8xETeORQmoRoeCBS60KGdsiU2u0adystrESAIIkBRBEFPXucR9JnbIcdj9Wz24jutvh01yafkpvGK\
37761# yW/P0E79AZz+yFoozMzG1N69hgxJ0ifTlXXKV+l/7yMFqYTLoZCHAg/Uf26Ej0oqDr9Rzj9E5JJDsCrtIK7AHQsk4LiPBXjgrXJ+thkLCNBLaTgsEGZOI\
37762# HxvHJLMfsFG1DVPYU+YvnfLUFRUNBCKLZJLBBrKUVNVVZVp/CP6Qt77ssF8ZthgMGCL3CFrSCL87MVfE8bfFOmvSeIabxYXD2fSoPiNIhBYeN8Ek5oquU\
37763# kJM5klxSUudOxTm7mAuBQmPRCLRKKF51dKBjwDsJ/lakwB7uwnnICrf2EBRs2N22oa/ruDLckkDuGHjM/AiSs7N/tBGN/SOYYNfvkMeXW8PwZsOapMbBN\
37764# QvBaTybIIcuv7e5dZE+KUWrI/5kVZ3vbesjkOKHMmJsBVbZm3s825o5Za+GCuMZm4XbEhgrLi6PluGalm8Dn8sa8MDp9hGQmOAfOt/HzsXUcrPxmnLR9G\
37765# Xe19/KDVAui0SC9PD69IGj3AWv0HukgIS3/cZL8AAAAASUVORK5CYII="/>  <a href="https://www.on1.com/free/lut-packs/">\
37766# ON1 Free Photography LUTs</a></center>"}
37767
37768#@gui : sep = value(0)_0+
37769#@gui : note = note{"<center><a href="https://marcrphoto.wordpress.com/specials/698-2/">PictureFX \
37770# - A Free HaldCLUT Set</a></center>"}
37771
37772#@gui : sep = value(0)_0+
37773#@gui : note = note{"<center><img src="data:image/png;base64,\
37774# iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAC+lBMVEUAAAAAAAAAAADw8O8AAABiYmLv7+84ODgAAAAaGhoJCQjs7OwmDwjQ0NDu7u7k5\
37775# OSPj4/s7Oy2trYAAAAAAACpqanv7+/Z2dnHx8fl5eWRkZFsbGzCwsLg4ODi4uLPz8/s7Ozn5+dfX1/v7++oqKgyGA0uIBseCwTb29vS0tIdDwzy8vLt7e\
37776# zHxsb+rB26urrr6urz2sGTc63vUQPn5+fk5ORYkczWbo4kJCT9pBrY3N1YsJjVKAfkMQToRQCwsLCZe7CPaqZZMGX8pir3ggvd4d/y5tjT09Pz0q2am5v\
37777# SZIJKHEakFBn7lBXkKA6xFQ7MHwL1awHzZQDyWwDbOgDy7eVZm9OTk5ZasYp/gYJZOXVsbnEZNmr3sGGzNVKzK0P6mzdNFjeEGjNeFC0dHiH9pQ/6jQje\
37778# SAPgYADX19fOzs5hjcXYn7yoqKh2VJIeQ4v0xIlbs3/GTWq7Q2LyZlH5q05hIkovMkKtHzDt1gLt2+Feo9eBpsSqqqrRfZ7QdJmJiotnRYRDVX72uHJJJ\
37779# FpdK1cVOVVKRk83Hk9yKE36qEBxFS6eGyz8nB32dw3XHwGek7eFhLSQeLFCa6/zzqN7XZvzx5ZKopXGVnVMLGkRXl5rMl6UNE1aGz66KC6UVSfY0wdmJA\
37780# TCFwQ+EgStbADqWQDv19niyNdqncmvyb1Wf7jj3bOQkrKGda9YqqmZu6Xt5aBBbZlusYbzhoD0vX7w33pfRXlqOG9HRW5bYWSLNl6eJUByHjyQGC7rTir\
37781# +sSX2nwHNwgDJYgDBUABvks3E28y0wcyNrsi1rMKyoreOdK7Kw6tyqaGXX5OGd3Pma3OXd2xTWWctRWIpb2AoIl73mVDFrzgmLTNbjRJ3OBHQpgC/hQDa\
37782# hABgoNGlvtDgts7yzMzuwb6cg7qipqmkpKS4gJ+JtJs/gpuDdpdWcI44kIYnZ36rjn0eTnt9V3p3RHovjHEnhG9sdWoigmXweFRcglMtekwkF0qkc0Lh1\
37783# z3VRDurkQCLhADNWgC0rmDQAAAAK3RSTlMABRL+FyPzRw0IIuj+Ou7HXtmKNi0Y5aqgl3lULNTPwKxuN82+rJCPjmZXgENtfQAABTVJREFUWMPtl2dUUm\
37784# EYx4NLBmju9t7jBUFFAyQxxdSMUjRNwTRzlZlarkzLnC0tLRtmVrb33mV777333nvXOT0X7XTuvcTl5Nf+XzzH1//v/p5HDrzU+Z9/TP3mteybtmLUps+\
37785# xkFqya9FnNBEOMf8boEGb1u0aGmFId4xMLVq3aV4fSQVcju56EwzRBxMNac9i6NxNQ2RIMCm/KVtn3wgZFNEQuU5AA8rzsX49bT1te1IFHjg017VbXfWE\
37786# nIshtoTfgoBAjsw4VAHS/qAO/ZDksKgwAoEn5cd5IFSfItCF0vecNj0nOaxQCQSiQCAPoc4M8qurEdFf298YMiFMqYyKCvP8I+DOj5PDTzPyGtlmRAFbz\
37787# +kJG0NwgcLIyD1uCWs3rcWgjgtkucMfGFMAFgRAT8/pG3NCJrgVSqCuUETtmTdy5KZ+mFbAC+FhkgBMYwLANiEnJHmCm0RiExlV5KvyvXENCGt5mMhDEO\
37788# iB8NTVC8Cm4XU3iY1NpMJXpfJVxdzYO2/kJlwgMwtVAxgkgClhghyo20Cg7+fnp1LF7AOFFQgX4P8FYESYILm6L4ny9bO3t/cDwPW9XzFcIIhnCGDabht\
37789# tX1nkZ+/s7LxoEQ5YgYQeAi8BogWk31mcsBsIEolSoYJ+dLTzopj9Cy8hngM/LkhIC0jfkLThokQiKVQqYQHOzv7+/tE4oFpAjmgBi08nJSUVKRSKoiJf\
37790# GKA7DogGAQwEMoNE9IB+G5KSqqrstYG+TCbzj/658MdxrUAcogegux+q+vTpXhOZj4+Pf8z3K1d4GL4BBwMAwqGnv8n6aDMZ4uMji/7y8dMaJBwsCPRC9\
37791# ID0oWO6Da+o9JFBoF1ZWVlRvjk7WwQCme9OGACAfq/hoX1r0rt370nls4O3rsRAwOv8aqdldID00d0AMKAiDapQ3hUeHj5+tutWKQjIt52f4dR/GQ3g9h\
37792# gtILQiLS1t0qTwiYMm7hyf5/oE44FAftkbJ6f+7voB+AQzRwwIDYX+romDIDiAjwuczS97DwqPeIYBduyCxw+EzElJeYULBObnl70FwHqpXsCS0TABAHZ\
37793# Afc4cb28t4SgmAgFNftkzJ6fExIN6AcKhWsCO8J0pKSl5ed548oS4gEajeQ4Cievd9QLwfwOMUD4edg/JzfUeN64ga7BAvlWjefl4df/ExDWYfgAM0WtE\
37794# +fbZW7ZsCXZ1nZWbCwAP9WW1Jj5+3WpcwAHRAIAwc/v212PHbg4OnjUrd9zn1Cy+OCI1Pn5q7Ik1yw6KEBlAfVNd/PDkyWHDcAIgLlwo4G8TR0D/QEb19\
37795# PQAJL3n0qN0GKS0dGx2dtB9ccS5qVNjD0iRoQA0pcSuR3VKl2dnFogjlsfGxmYgwwEOq0rsID3s7Ipj1UHiiHWOjo5LD+kHWCCCwqpjOMHFpfipV6r4si\
37796# MOQISwyAAz4nkxEKDvslKtrhYIOEw4N2aSP1wbEQHC4pIS6B8NzEoVpzoGBATclBLOW1IAloiUI1NWTjniELQNNgj9URnE08ZsEoDTFkOU8JBcnSo+Nwq\
37797# ylHRMuaoyWI0QNfyrC0zEhw5nZNwiHbSk3jTZbU0pfeHcufNNCoRUMGbOBABZwZwyxHEcoOZRAZYsjo5LPLcp+ar64ur8BSYC6vMtuTqvumxus1ZEiTPQ\
37798# P0URaNGMy2boAjA4LOtmjc2MsT8A6BPLpi0am1uzCH0Cgs3iWltZ1fudDiYd6xFiZWXNZTEJ81Ms2EwmqybcTl25LEKYTDabwaD/TlMTDhdkCaEpUnUYd\
37799# f7HgPwCbcV8KluLH2kAAAAASUVORK5CYII="/>  \
37800# <a href="https://www.pixelmator.com/pro/free-luts/">Pixelmator Free LUTs</a></center>"}
37801
37802#@gui : sep = value(0)_2+
37803#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA5FBMVEX///\
37804# 8AAABJSUnMzMzKysr39/fW1tb9/f3u7u7w8PD6+vrn5+d9fX2VlZXy8vKSkpKHh4e3t7eMjIz8/Pz7+/t2dnZra2tQUFD5+fn09PTq6urj4+PU1NSpqam\
37805# amprt7e3c3NzY2NjR0dHDw8O/v7+vr6+ioqKDg4NFRUXOzs7GxsY1NTUqKioGBga5ubmmpqaenp56enphYWFcXFxNTU09PT3g4ODe3t61tbWxsbGrq6uP\
37806# j49vb29lZWVXV1dCQkIKCgrAwMC8vLx+fn5zc3M5OTloaGgyMjIjIyMZGRkTExMeHh4Ueor/AAADBElEQVRYw+2X53LaQBSF74IaoghVQIjei023Daa4x\
37807# 3be/31yV7vYBKyMCPlhe3JmgHMumk+rvbArATlRQM4iJ+gMARE4QZHvCxDFowFlwZcHkLtN9wl5NGo5YJIEpqyfVJ6kPUCS+LLF+Svh6tUYIMFzzE8pnh\
37808# IfAzJjsqNr6zgA19mylZhxghwaUNV1jY2hlsIoX7b8VEcv6ANqNTax2QIN97pw2IUoP4xLp/FHyvfnaMfsG9lA76APAJjwJv+8V8wv0Bpv3EgMAgHwruw\
37809# U84R5WeOwCh1WHMIA+EWUmDdxTl4rUJrhJEsQEuDSQpUH8Z6QjbvEShfCAkxaON+m2IqQn1hoQGgATHcBoET8NsERgKffARmCujwGQHYBWYdQTZXwgBwt\
37810# dHgwDQwP+FrlQgMkWnB5KKC3rSG+t6ywgCLmNvd19JsYWDZ+JuVwAPcGc4F5j6DKtJm0M/NQAHX0/kPM99DeMdtHWwsGdLZJNGhcsAau0Wp84F30L14gg\
37811# BiSiD5V95u+ov528UT9kK8HixmGXqERBEBNRmtulOAl7SYAsKulAscDRmSrx7oFfwEA5VZzHGc0qKrAVYkysYLKUycIEFr/Af8eoBuGQwGGYdxBKAVvrs\
37812# UvClDib1I+w13atwOIirxXieUOABpZtycSOESDq+eN67afhVq/TXcCudHPZPJoPJrj7Z5Xstu9orkHUIkIlZkMBL2QMOV0FyAdBZQ0ToGQiQPP0SZcN0B\
37813# NFz8ApIjpA6Co1zV5CxBGMlgVcQdwj/BS/gDQrbWawABiZpqDLSC3fqgrADuAzkuyasEB4K5WBg7ITsb8jD5O0Ppzaq45AFx9lYl/cAmwBQwbV0MOUEuK\
37814# ApD31/RGne5sBTWugjxI/gFQtS050WGAfGLQknF2YqYoe7YJcsuLRQQAXdsDNNhG3iRziD7aimJvKl7ETiYfElkn0RwXoIyDGDrJJR5W6RWTs8s9wIXkV\
37815# 8rSBbiSJIqSpCgSCnO2KlwA5CQLx1P1J78kdGJf67/w6QEnP3yfqF/1kj/apHwTgAAAAABJRU5ErkJggg=="/>  \
37816# <a href="https://discuss.pixls.us/t/help-to-create-a-set-of-pixls-us-color-luts">PIXLS.US Contributors</a></center>"}
37817
37818#@gui : sep = value(0)_0+
37819#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAZlBMVEX18v\
37820# ny7PauecS0g8jt5PPr4fHz7vju5/Tm2e7IptjXv+Pw6fXo2++wfMXMrdrj1OusdsPq3/C3icu6jc2yf8fdyOfUueDEn9Xl1u3ezOjaxeXStt/BmdK8ks/\
37821# h0OrPst2mbL6lar0clnGlAAABY0lEQVQ4y5WT2ZqDIAyFhbIp++JWt5n3f8kBtVorc1Eu+ID8JCcJFF8NwimlnPxn5oJZO9d1xzDImIGwXRmgmzyUbW9v\
37822# bijT/tHQbb3AVvOrHQkl6zd3cUcv9zs/XuP2pnojgHbLZ0xmnucVMam7am30kZ/yubQHh/ZVZ9DKNUr1qVxPpZp04IZdQVmubkzZDLAgFRybZr2iH5tOH\
37823# EScjOKRhUUYbTx74OQKil1vLFvd4sWDCBhe+W4DCjeTxM0hzvOvVBBF4Me0U7UDUw/WIoS1ltFvAigpyhfgNE3qLFwj7UAcByDxCmDJ8wCXOFmAHes88G\
37824# wFSCJFPeaBqWdbJwBEOQBBirde46HNAeUgyN5MHvQdEBLj13NgDKJPAISekeNBiWdAV8B6xVIKJwH1BZDlZj8JLVt4An45/B86cGMiAABnrioYJrdvh1h\
37825# HgJNSutIWJPszYyOZYMyi3ZyFCCm+HH88KhF9A33HkAAAAABJRU5ErkJggg=="/>  \
37826# <a href="https://purple11.com/free-luts/">Purple11 - Free LUTs</a></center>"}
37827
37828#@gui : sep = value(0)_0+
37829#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAgCAMAAAAynjhNAAAA1VBMVEX+\
37830# /v4ODg4DAwP7+/sQEBD5+flRUVFubm7z8/Pw8PCioqJSUlKAgIC3t7dqamo6Ojrk5OTn5+elpaU/Pz8iIiIYGBjh4eHa2trX19eysrKtra1eXl5YWFg9P\
37831# T0tLS0kJCT29vb09PSJiYlwcHBgYGBVVVVMTExCQkIyMjInJycUFBTt7e3U1NTR0dHOzs6+vr66urqRkZFzc3NJSUkgICAICAjJycnFxcWvr6+cnJx3d3\
37832# dlZWXp6em/v7+1tbWWlpZ7e3s3Nzfc3Nynp6eEhIR8fHxaWlqIUZ+rAAABlklEQVQoz4WT13KrMBRFZUsCgwFhegdjinFvcb1p997k/z8pJJa7ZrIeNGd\
37833# maaTzsDdoXvFk6PFT8xrQbJxZBZ7lHVaNCxeNVrlmQ4BVzY/Rg0YkHWPwDXbmZHCrzXdp0gInWqOUmBeN+spsCMEFOHxe9AdUV4HXweAW+Gd7qFCtUbGz\
37834# IGCg7nKzCcJx92ch4UyL3u5O2g3AHefNnueVXq9XH+mmS1fkEdVQI1wYZYoSxsWSc5N7LXLeG2cL68K37AU3ftBtWWqrdi+YQbjVXczSYync1v/OdI+ll\
37835# 3PyfwihkJZr1uNGFCmS9JZxosDUhFSryiDiEDBXs5ws/vRLF7N1Bz8v9xPl4DD0S60B3hXiNNhbD3r5qnAdAKyPQlv0xdad9kr93a81dIIyjPIJvNFAnc\
37836# ryOqkHPJrJ8lQ9ad1JjhHDGB4fwnRMRnkDoFxTAYvOP9+sw4SizGWE6YWPBqco8lMBXkthQ6NIg0xSJznb7t95aN7WYKDPR7QG9mtpPpYoDmiJsuquRBT\
37837# Dd1VXMa4r+EuBvwA2hClvJ4GaGgAAAABJRU5ErkJggg=="/>  <a href="https://www.rocketstock.com/free-after-effects-t\
37838# emplates/35-free-luts-for-color-grading-videos/">RocketStock 35 Free LUTs for Color Grading</a></center>"}
37839
37840#@gui : sep = value(0)_0+
37841#@gui : note = note{"<center><img src="data:image/png;base64,\
37842# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAC91BMVEVLNHxKKWxjXKBTO4Pr6nZoYqXWemJON4BNLnVtbqzRa19aUZeILkrJIyaQ69Nd\
37843# V5vr6YyUaoPs63qIY3pKL3nag2QzHFnXc1jlnk9zQk1TFUdoMT1XxK9saKjv36BZS5Xi6JDq64TEb3bikW/hhmvSdWLe41pqOFjgaVI5GkmOEzHJQiu8\
37844# Fh5u5NOD58dq18Viw8J357/l2bxt3LaqmK1dU5/o657p6pNZSJFWQY7X2oxUQ4lFNYatdX+khXybbni1b3Xm2HB4V25TKm6SXW24ZmmiYWeGVGadUGBe\
37845# JGBIHFqGSlnJZVhVFVLQalFtIE/PXUlVKEi/QEbjxkWtMETi40GdOD+SJTx4JzuoHjjcMDdIFDK5GyufECS9IB2A69zHwsiT8L7s5rRy5a3Y7qPGsqKC\
37846# mpuBeZVRdJLHzYnTm4nIh4aJcIJVXHvh5nrlmHrmv3Xl623gdGO/cmPFXE1MLE3TgErhgElGQEeEO0ciGEdJG0PlcUKHHEDJVz65JD3fPDhlFzDULSnC\
37847# LSWDzOGm0dt81dBe1Lea8bCH7bDSvq6Nf6qF36fPyqXNjqJqhqE6QqB2d5muiJjJ5pWjmJFShJB90Y+xu4lmV4nW4oRSNnjJqHLNdXGqdmiHNWXd42Ti\
37848# 1WNlOWLGX2GyVFy2ZlvJV1pSOlrXW1nmkFU2LlLoslCORk6tRE15NEvlp0rlXEo7Y0eWNUVdPEBkHTzgTznSNTnMJTfTRC2wHyPC79mUu8Sqs8Oqn790\
37849# q7ZdqLWNl7WzrbOMnq+DtKtydaedw6WprZzij5dfzZPDvo6ZeI5qZ4XQwH7FnHt6WXk0N3dsPnTlzWy0mGxnLWztiWooF2pEZ2nkxWc4JWW4iWPewl/M\
37850# R1/YnF1DQViYKlaOY1Xfr0ujgj8bMjGJJS53DieS2Liai6k4W6bc3Z9gwZmKgJDxrI7zqIyl1IttQYVajoCMhnyWYHpYU2rgrmeOkmWpLGDOk17YiVrF\
37851# LVXm2lHdp0W4cjZMJjAgVhoxthLEAAAEA0lEQVQ4yy2RZVTaURjGr4LIGDAQAREFJEbZ3d3dCfbsmt3Oud7sdnZ317q7u7u7ex/2F/c7771fnt957jn3\
37852# BZl2+HPbjtXRaJHa4Rs37t6zt3JjdeyRIyuWUFZWlgG9lnhrWry29sHT/UN3kw/FxFXur4zdrrycy8hIgxAzPF6fFt6kWFoUUBQUdO5AfVxybJO0zDLS\
37853# 0qC37WON9p7+kCdisUikKhajD1Qn42FQsswq4GpnqxvOfSIOxWCgwSwKxrY/I7RI6+mtkrAStDJt9zpgFueD160LDg6eDxII7m3XldLTWymBbgimbU/p\
37854# Wkz4e1H9vb29R734BR5FM45dRnp0QzqdbmhoBLL4+Wav4p5XXZWXancyJTm7uV0u8rZuoRuaGJmYGMFggMAdwl+LS25y43eVdOW7KSoqerT3EWyMTGAm\
37855# MBhMVhZQKf1Jk5O5fHDVcXbqPD8EEtDdFk6ykhQ6gOJAqJn+O3m+aiVqIvRdlfOFyx4BrWZOSFlTU1MAZAEIzM09VffoUf4N+ze8OdGFfBKpXZCR6GQD\
37856# lS9hCubzqC7hFS7oktTX6bO3S77e+1EykpDgZiMP/iPq8X9skeA50N1qNzBVMBMYNBdw/2yCkw1SHtkiEfz9A0tdEpm2tvYuFK8Qr4JS3wDzm5e4zlJI\
37857# JFIeQEOhUKf/mFs4BFo6otHomZCxQPRohu7pIZKUPFRigwS5dz992LaG4MI8XsG87RuE9uAKVBdqNuyqdpaSAMrLjQ1o22KTLC3tRxwfB4wVcFUxC3Ua\
37858# YbsdSDckghwcLodbs8bawt6uezQvj5slUF2/LkZTbVP6cWs+ieQMOlavhavspJknmVky7RwpWb4iSNinqaHmeuUKOxGfBVZLBKu0+4QkB4K1l2+wKma9\
37859# t6bmZrWMwpycVPNDYHUHHK5y5uKtO70UKnVqbn1oaFmZvabGZjXzwpzBtvr3ACqQw5252GmVVihcmBAulgmFwhglSDhcODj47Vr9kgDHnUyz6ryV7c7j\
37860# CYuLPXmZm5R0dLQ2ZSsoKNz5AjpSUlJwJ3dadV7/ziYXu5PJnrw2rJKWuk7YpeFhhXEfkMKIxxlrZ6Rd/5xd7IlIZZPJvGMSQf3o+PD4Lx/AaGQYG0f2\
37861# sFgshKc7KxVBZBOfYpWwWupa6tATD3xAI8OgERfZgyIi3N3JLCKRxT6rs+sldmtEVNjNnOwHfqDWIN5YRd9sIB2BQBARHOg6vCO6IWLrlqiwmNnfPn6A\
37862# wYhXgf7aN53DRnA4KA654ugO/bc7tmyN0NhX9hASDJqb5eBykSN9KFdXFArFSTwY3aBfe2JLFFZpQ+nDn34gulkFWpdBeV6fa2YmCsXUfVHbEK1/IiIK\
37863# u3+DItXK7x94612QcMazZgAAAABJRU5ErkJggg=="/>  \
37864# <a href="https://www.facebook.com/shamoon">Shamoon Abbasi - FreshLUTs</a></center>"}
37865
37866#@gui : sep = value(0)_0+
37867#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE4AAAAgCAQAAAAWXYa8AAAG3UlEQVRY\
37868# he3WaYyV1RkH8N/cWa7AzCiCIiMuSBW3AtagYLQWitpQa7VVazRgMLbEiEuitqRRa6WgRUxFTRfjiMaoVGuNQm1IaajLB7WKbcS2Am5RtDDDNsyd5c7c+\
37869# /TDe+879w5D0v2T//PhPec5zznvc/7Pck6NMr6f9rarxCqfGAqzHTak/N/HmLR3e/Kpq5ydbtI/ZVcZ/337dltZMaoybsapPy13O6BjDL4J2kvyd8ASd8\
37870# IfNNDutHSD1Y4GJ6SSnej2WsVPRlbNDcK1VaMq44p27a1fjSNAxuegSxcjjEtnh5sw5KqTfZgeD5+3w5YhFfP7No7T3aRNrbqWlz2o4Bj3F8q7/sLbrvb\
37871# D0qjenW7Q4nJ3GOVh39DHNLIWu9FUtyekNT9hbTNaLPSxY/Elvmo5rJar+nk1bVXGFWF/a92Helf5gVsNt97CVKWlRBxl8hocgTrjLPATUGMCmqyzFGMs\
37872# cqrFcKHnNZVXdyefakdV07Y3c2X0uc/awcKWtHfx4HUvmaZliPTZar5VTrSBYyHrJK/oKYArbAKrdQ5tRaZqtDNtjipHRbdu3Z7z3NAbJKi3yCKaZGXRo\
37873# 8c663xEeMCF0ACTNWnxXnnZmIryMQT2Yu5kl6g13pG+C45zCdjsdRxVGlE/sPRVI9nQtMOZXqjebLMW/uTSVDDb8ybb5hZLTTF+36RhMHPo1G6rjVv2bJ\
37874# m3RafOPjk5Ob2gPMrJiYFFO3XyI99TDzUV270s351vyJfjabfXjNHuU42m2VPTWam7FwaYy2o0hneSWNvypDvNtMNmq+D4ROmjZMSwrMvLK8eWf/yYq+o\
37875# fkFhYwnwOtQ3eTA7XpNlmpzjBbusM092gxlwTMHpfxk11tYOqZvpef2rC1B0HqwXnlaXqcWaF3jTZMv+Pe8avyyTNKit8xctwpAsadDvaWw7pru3ett8X\
37876# nOYlWFxRKaEoXKdQNi7jEaPJ+pITZaz2Lr6ceXhsQto5RnrX06DWGWrQoCmpDMOgxQywzlIHOgCcrh4jzLfAVH9Ei6IjrJcd9mI37VrU6WUcTS4wwe7yd\
37877# Vh1K45VVJwa2yJBPu6Px/I394mT4pPoL0k/iNnxUL458lFGV/wyfh5iTBSiEjNiZjwYuyokb4QQcyOiKw4PIS+3PZfLNeQUxfHxUUmvEPdHbcGzib/SmE\
37878# vIa7XGIa41yvW21jHPWO3u8r5p5mjzM5B3s35jzXWxe7DLSpd6Ja2MH8hZ4yIstwd8mvIwzG2uGJSIrcZZaaWxbnJYZaKVmRsbxdgVmRCiMUaEvJgYfdE\
37879# Vk0MIMTKEfHPkozOyIcQ5EfFiCLEwIu4u6ZVbW0S0VEnmRsTfIx/Hh4JCmbn9ohj50o63xP4hZS49QU6vRnNk0CnHNi5S5yl/hnZtO9u0oVhqJT6GGxId\
37880# Q4t5XMaiKkmfPerNV4s+u+kloS81rkOrWiusd2Xyw0OZTJJP3WaZaKKJ6Y4Z9nM93kpFX7TMMsvMJr0nby3JRqRa7VY5P6bVqElSiYLlwnIbLNAIw5xl/\
37881# IDxzbpFXTwTOyMi4uOYGUKsiYgLQngk1cs3F/KFQmFDvB27ImJbHJO6tYw7QmhL3FrGQalbfxwnRX9xbVGxvZjLNeTkRCZuKGl3xXlJCCwYYK5DK/1eNd\
37882# 5NPnaoZxyMPRgFF5luuukDj8iMJvvb5GQb0xM+aooppri3wmVnl2Q7Kih/05NmmqmDLIiiu413jfcMszJ5ElbcHJclcbQwhGiOv0XEpSFui4gVyUn6y62\
37883# 5kC90FrIxKnZEX5IsxSESom1fCXFXiKOLPfHbeDVy0RAKCgrJ/PB4PSIWhHBNVcwxr6apRON7JdN/peBbCV21aqfX1tQOaG+3TF3yWh/qftzrKqrEJq3O\
37884# qniBzckcCLq8W7VdWucu06pLj81Ocbac32ODFa60zj0+cIZvW+JWkrdYQf29FjjHrLS6TXJVqfeCv5R6c+0Gea2V1hUX1841TheY7WFtHvBXk5yv15pBR\
37885# 7lMcWTxifQu2BVfLzkiGw+l1X9zTA79zfl8vjOfzQuxICLeiMyghIj4Tlrnyuiocqt+sTii5NbGQmv0lfR6Y07y52sGXHKZR1GzxIcO8Knf2E5BT1IBjj\
37886# NDk02e10Ohrvg1RasU80Y0OFeN3+kw0YkVZ13vfZxbinfo9ywON9VGb9GpsdlZ+j0nisgcaZbRtjrBjeSFSQOZNtIT+sXCyvDdZaGHyqGatnzaOgfN/Cv\
37887# trop+Tt/AaGEIveYkZg1E3xLmJa/mfVb3/xQ595HziY2OSYUTspnrkrwcTaOb4cZkauCx2cGK/5VV1XjawMWSdV2vpUm/Z7DiQCnZ+n8w6zN8Bv4Bs+PS\
37888# zRd3ZU8AAAAASUVORK5CYII="/>  <a href="https://www.smallhd.com/community/movie-looks-download">SmallHD Free \
37889# Movie Look Pack</a></center>"}
37890
37891#@gui : sep = value(0)_0+
37892#@gui : note = note{"<center><a href="https://freshluts.com/users/52679">Youssef Hossam</a></center>"}
37893
37894#@gui : sep = value(0)_2+
37895#@gui : sep = separator()
37896#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.
37897#@gui :         Latest Update: <i>2019/10/11</i>.</small>")
37898fx_color_presets :
37899  category=${arg\ 1+$1,abigailgonzalez,alexjordan,berat,cinematic,cinematic_travel,creative,ericellerbrock,filtergrade,\
37900                       inavision,jtsemple,kylerholland,lutifyme,michaelezra,moviz,ohadperetz,oliviosarikas,on1,\
37901                       picturefx,pixelmator,pixlsus,purple11,rocketstock,shamoonabbasi,smallhd,youssefhossam,others}
37902  presets=${-_fx_cluts_$category}
37903  index={arg(1+$1,${2-27})}
37904  thumbsize,strength,brightness,contrast,gamma,hue,saturation,normalize=${28-35}
37905
37906  if $normalize==1" || "$normalize==3 # Pre-normalization
37907    repeat $! l[$>] split_opacity balance_gamma[0] , a c endl done
37908  fi
37909  if $index>=2 # Apply CLUT
37910    path_clut=${-path_cache}
37911    name=${arg\ 1+$index-2,$presets}
37912    clut $name,{0$_is_preview" && "!isfile(['{/${path_clut}clut_$name.cimgz}'])?17:48}
37913    repeat $!-1 if $strength<100 +map_clut[$>] . j[$>] .,0,0,0,0,{$strength%} rm. else map_clut[$>] . fi done
37914    rm.
37915    adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
37916    if $normalize==2" || "$normalize==3 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi # Post-normalization
37917
37918  elif $index==1 # "None"
37919    adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
37920    if $normalize==2" || "$normalize==3 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi # Post-normalization
37921
37922  else # Collage
37923    repeat $! l[$>] if max(w,h)>$thumbsize rr2d $thumbsize,$thumbsize,0,2 fi endl done
37924    N=$! +to "Original",1%,1%,7.5%,2,0.5
37925    Np={narg($presets)} repeat $Np
37926      clut_name=${arg\ 1+$>,$presets}
37927      clut $clut_name mv. $N
37928      repeat $N
37929        if $strength<100 [$>] +map_clut. [$N] j.. .,0,0,0,0,{$strength%} rm. else +map_clut[$>] [$N] fi
37930        adjust_colors. $brightness,$contrast,$gamma,$hue,$saturation,0,255
37931        if $normalize==2" || "$normalize==3 l. split_opacity n[0] 0,255 a c endl fi # Post-normalization
37932        strcapitalize $clut_name clut_name=${}
37933        to. $clut_name,0.01~,0.01~,7.5%,1
37934      done
37935      rm[$N]
37936      progress {$>*100/($Np-1)}
37937    done
37938    k[$N--1] frame 1,1,0,0,0,255 - 128 append_tiles {s=floor(sqrt($!));w>h?[s,0]:[0,s]} + 128
37939  fi
37940
37941fx_color_presets_preview :
37942  _is_preview=1
37943  index={arg(1+$1,${2-27})}
37944  if !$index gui_warning_preview "Preview disabled in 'Collage' mode"
37945  else gui_split_preview "fx_color_presets $*",${36-38}
37946  fi
37947  u "{$1}{$2}_"{2*($1==0)}\
37948        "{$3}_"{2*($1==1)}\
37949        "{$4}_"{2*($1==2)}\
37950        "{$5}_"{2*($1==3)}\
37951        "{$6}_"{2*($1==4)}\
37952        "{$7}_"{2*($1==5)}\
37953        "{$8}_"{2*($1==6)}\
37954        "{$9}_"{2*($1==7)}\
37955        "{$10}_"{2*($1==8)}\
37956        "{$11}_"{2*($1==9)}\
37957        "{$12}_"{2*($1==10)}\
37958        "{$13}_"{2*($1==11)}\
37959        "{$14}_"{2*($1==12)}\
37960        "{$15}_"{2*($1==13)}\
37961        "{$16}_"{2*($1==14)}\
37962        "{$17}_"{2*($1==15)}\
37963        "{$18}_"{2*($1==16)}\
37964        "{$19}_"{2*($1==17)}\
37965        "{$20}_"{2*($1==18)}\
37966        "{$21}_"{2*($1==19)}\
37967        "{$22}_"{2*($1==20)}\
37968        "{$23}_"{2*($1==21)}\
37969        "{$24}_"{2*($1==22)}\
37970        "{$25}_"{2*($1==23)}\
37971        "{$26}_"{2*($1==24)}\
37972        "{$27}_"{2*($1==25)}\
37973        "{$28}_"{1+!$index}\
37974        "{$29}{$30}{$31}{$32}{$33}{$34}{$35}{$36}{$37,$38}"\
37975        "{0}_"{2*($1!=24)}\
37976        "{0}_"{2*($1==0)}\
37977        "{0}_"{2*($1==1)}\
37978        "{0}_"{2*($1==2)}\
37979        "{0}_"{2*($1==3)}\
37980        "{0}_"{2*($1==4)}\
37981        "{0}_"{2*($1==5)}\
37982        "{0}_"{2*($1==6)}\
37983        "{0}_"{2*($1==7)}\
37984        "{0}_"{2*($1==8)}\
37985        "{0}_"{2*($1==9)}\
37986        "{0}_"{2*($1==10)}\
37987        "{0}_"{2*($1==11)}\
37988        "{0}_"{2*($1==12)}\
37989        "{0}_"{2*($1==13)}\
37990        "{0}_"{2*($1==14)}\
37991        "{0}_"{2*($1==15)}\
37992        "{0}_"{2*($1==16)}\
37993        "{0}_"{2*($1==17)}\
37994        "{0}_"{2*($1==18)}\
37995        "{0}_"{2*($1==19)}\
37996        "{0}_"{2*($1==20)}\
37997        "{0}_"{2*($1==21)}\
37998        "{0}_"{2*($1==22)}\
37999        "{0}_"{2*($1==23)}\
38000        "{0}_"{2*($1==24)}\
38001        "{0}"
38002
38003_fx_cluts_abigailgonzalez :
38004  u blade_runner,blue_house,blue_ice,caribe,cinema,cinema_2,cinema_3,\
38005    cinema_4,cinema_5,cinema_noir,cinematic_for_flog,day_4nite,eterna_for_flog,filmic,\
38006    fuji_hdr,goldengate,matrix,monochrome_1,monochrome_2,old_west,science_fiction
38007
38008_fx_cluts_alexjordan :
38009  u action_magenta_01,action_red_01,adventure_1453,agressive_highligjtes_recovery_5,bleech_bypass_green,\
38010    bleech_bypass_yellow_01,blue_dark,blue_shadows_01,bright_green_01,brownish,colorful_0209,conflict_01,\
38011    contrast_with_highlights_protection,contrasty_afternoon,contrasty_green,cross_process_cp_130,cross_process_cp_14,\
38012    cross_process_cp_15,cross_process_cp_16,cross_process_cp_18,cross_process_cp_3,cross_process_cp_4,\
38013    cross_process_cp_6,dark_green_02,dark_green_1,dark_place_01,dream_1,dream_85,faded_retro_01,faded_retro_02,\
38014    film_0987,film_9879,film_high_contrast,flat_30,green_2025,green_action,green_afternoon,\
38015    green_conflict,green_day_01,green_day_02,green_g_09,green_indoor,green_light,harsh_day,harsh_sunset,\
38016    highlights_protection,indoor_blue,low_contrast_blue,low_key_01,magenta_day,magenta_day_01,magenta_dream,\
38017    memories,moonlight_01,mostly_blue,muted_01,night_01,only_red,only_red_and_blue,operation_yellow,orange_dark_4,\
38018    orange_dark_7,orange_dark_look,orange_underexposed,protect_highlights_01,red_afternoon_01,red_day_01,red_dream_01,\
38019    retro_brown_01,retro_magenta_01,retro_yellow_01,saturated_blue,smart_contrast,subtle_blue,subtle_green,yellow_55b,\
38020    yellow_film_01
38021
38022_fx_cluts_berat :
38023  u brownbm,cineblue,cinebm4k,goldentime,green_and_orange,monochrome,sevsuz,sunlightlove,western,westernlut2
38024
38025_fx_cluts_cinematic :
38026  u deep,dimension,enchanted,flavin,frosted,shine,ultra_water,wipe
38027
38028_fx_cluts_cinematic_travel :
38029  u blue_cold_fade,bright_teal_orange,bright_warm,clear_teal_fade,cold_clear_blue,cold_clear_blue_1,deep_blue,\
38030    deep_dark_warm,deep_high_contrast,deep_teal_fade,deep_warm_fade,faded_green,greenish_contrasty,greenish_fade,\
38031    greenish_fade_1,hard_teal_orange,neutral_teal_orange,neutral_warm_fade,smooth_clear,smooth_green_orange,\
38032    smooth_teal_orange,teal_fade,very_warm_greenish,warm_dark_contrasty,warm_fade,warm_fade_1,warm_neutral,\
38033    warm_sunset_red,warm_teal
38034
38035_fx_cluts_creative :
38036  u anime,bleachbypass_1,bleachbypass_2,bleachbypass_3,bleachbypass_4,candlelight,colornegative,crispwarm,crispwinter,\
38037    dropblues,edgyember,fallcolors,foggynight,futuristicbleak_1,futuristicbleak_2,futuristicbleak_3,futuristicbleak_4,\
38038    horrorblue,latesunset,moonlight,nightfromday,redblueyellow,smokey,softwarming,tealmagentagold,tealorange,\
38039    tealorange_1,tealorange_2,tealorange_3,tensiongreen_1,tensiongreen_2,tensiongreen_3,tensiongreen_4
38040
38041_fx_cluts_ericellerbrock :
38042  u avalanche,black_star,helios,hydracore,hypnosis,killstreak,nemesis,night_blade_4,paladin,seringe_4,serpent,terra_4,\
38043    victory,yellowstone
38044
38045_fx_cluts_filtergrade :
38046  u fgcinebasic,fgcinebright,fgcinecold,fgcinedrama,fgcinetealorange_1,fgcinetealorange_2,fgcinevibrant,fgcinewarm
38047
38048_fx_cluts_inavision :
38049  u 7drk_21,bc_darkum,brown_mobster,cold_ice,dark_man_x,film_gb-19,formula_b,gremerta,hitman,jwick_21,london_nights,\
38050    louetta,nightlife,vfb_21,vintage_mob
38051
38052_fx_cluts_jtsemple :
38053  u brightgreen,crispromance,crushin,frostedbeachpicnic,justpeachy,lateafternoonwanderlust,lushgreensummer,\
38054    magentacoffee,minimalistcaffeination,mysticpurplesunset,nostalgiahoney,springmorning,toastedgarden,\
38055    winterlighthouse
38056
38057_fx_cluts_kylerholland :
38058  u kh1,kh2,kh3,kh4,kh5,kh6,kh7,kh8,kh9,kh10
38059
38060_fx_cluts_lutifyme :
38061  u hackmanite,herderite,heulandite,hiddenite,hilutite,howlite,hypersthene
38062
38063_fx_cluts_michaelezra :
38064  u deepskintones2,deepskintones3
38065
38066_fx_cluts_moviz :
38067  u moviz_1,moviz_2,moviz_3,moviz_4,moviz_5,moviz_6,moviz_7,moviz_8,moviz_9,moviz_10,\
38068    moviz_11,moviz_12,moviz_13,moviz_14,moviz_15,moviz_16,moviz_17,moviz_18,moviz_19,moviz_20,\
38069    moviz_21,moviz_22,moviz_23,moviz_24,moviz_25,moviz_26,moviz_27,moviz_28,moviz_29,moviz_30,\
38070    moviz_31,moviz_32,moviz_33,moviz_34,moviz_35,moviz_36,moviz_37,moviz_38,moviz_39,moviz_40,\
38071    moviz_41,moviz_42,moviz_43,moviz_44,moviz_45,moviz_46,moviz_47,moviz_48
38072
38073_fx_cluts_ohadperetz :
38074  u cold_simplicity_2,d_o_1,retro_summer_3,subtle_yellow,teal_moonlight,true_colors_8,vintage_warmth_1
38075
38076_fx_cluts_oliviosarikas :
38077  u analog_film_1,atomic_pink,beach_aqua_orange,beach_faded_analog,bw_but_yellow,city_dust,dark_orange_teal,\
38078  day_to_night_kings_blue,duotone_blue_red,faded_pink-ish,flat_blue_moon,honey_light,infrared_-_dust_pink,neutral_pump,\
38079  shade_kings_ink,sunset_aqua_orange,sunset_intense_violet_blue,sunset_violet_mood,violet_taste
38080
38081_fx_cluts_on1 :
38082  u 2-strip-process,aqua,aqua_and_orange_dark,berlin_sky,blues,\
38083    bw_1,bw_2,bw_3,bw_4,bw_5,bw_6,bw_7,bw_8,bw_9,bw_10,chrome_01,\
38084    cinematic-1,cinematic-2,cinematic-3,cinematic-4,cinematic-5,cinematic-6,cinematic-7,cinematic-8,\
38085    cinematic-9,cinematic-10,\
38086    classic_teal_and_orange,earth_tone_boost,fade_to_green,film_print_01,film_print_02,french_comedy,green_blues,\
38087    green_yellow,\
38088    landscape_1,landscape_2,landscape_3,landscape_4,landscape_5,landscape_6,landscape_7,landscape_8,landscape_9,\
38089    landscape_10,\
38090    lc_1,lc_2,lc_3,lc_4,lc_5,lc_6,lc_7,lc_8,lc_9,lc_10,\
38091    moody_1,moody_2,moody_3,moody_4,moody_5,moody_6,moody_7,moody_8,moody_9,moody_10,\
38092    nw-1,nw-2,nw-3,nw-4,nw-5,nw-6,nw-7,nw-8,nw-9,nw-10,oranges,\
38093    portrait_1,portrait_2,portrait_3,portrait_4,portrait_5,portrait_6,portrait_7,portrait_8,portrait_9,portrait_10,\
38094    purple_2,reds,reds_oranges_yellows,studio_skin_tone_shaper,vintage_chrome
38095
38096_fx_cluts_picturefx :
38097  u analogfx_anno_1870_color,analogfx_old_style_i,analogfx_old_style_ii,analogfx_old_style_iii,\
38098    analogfx_sepia_color,analogfx_soft_sepia_i,analogfx_soft_sepia_ii,\
38099    faux_infrared_bw_1,faux_infrared_color_p2,faux_infrared_color_p3,faux_infrared_color_r0a,\
38100    faux_infrared_color_r0b,faux_infrared_color_yp1,\
38101    goldfx_bright_spring_breeze,goldfx_bright_summer_heat,goldfx_hot_summer_heat,\
38102    goldfx_perfect_sunset_01min,goldfx_perfect_sunset_05min,goldfx_perfect_sunset_10min,\
38103    goldfx_spring_breeze,goldfx_summer_heat,technicalfx_backlight_filter,\
38104    zilverfx_bw_solarization,zilverfx_infrared,zilverfx_vintage_bw
38105
38106_fx_cluts_pixelmator :
38107  u black_white_01,black_white_02,black_white_03,black_white_04,black_white_05,black_white_06,\
38108    pmcinematic_01,pmcinematic_02,pmcinematic_03,pmcinematic_04,pmcinematic_05,pmcinematic_06,pmcinematic_07,\
38109    classic_films_01,classic_films_02,classic_films_03,classic_films_04,classic_films_05,\
38110    landscape_01,landscape_02,landscape_03,landscape_04,landscape_05,\
38111    modern_films_01,modern_films_02,modern_films_03,modern_films_04,modern_films_05,modern_films_06,modern_films_07,\
38112    pmnight_01,pmnight_02,pmnight_03,pmnight_04,pmnight_05,\
38113    urban_01,urban_02,urban_03,urban_04,urban_05,\
38114    vintage_01,vintage_02,vintage_03,vintage_04,vintage_05
38115
38116_fx_cluts_pixlsus :
38117  u amstragram,amstragram+,autumn,cinematic_lady_bird,cinematic_mexico,dark_blues_in_sunlight,delicatessen,expired_69,\
38118    fadedlook,faded_print,hypressen,magenta_yellow,metropolis,modern_film,newspaper,night_spy,progressen,prussian_blue,\
38119    seventies_magazine,street,sweet_bubblegum,sweet_gelatto,taiga,tarraco,unknown,uzbek_bukhara,\
38120    uzbek_marriage,uzbek_samarcande,velvetia,warm_vintage,whiter_whites
38121
38122_fx_cluts_purple11 :
38123  u good_morning,going_for_a_walk,nah,once_upon_a_time,serenity,passing_by,smooth_sailing,undeniable,undeniable2,\
38124    urban_cowboy,well_see,you_can_do_it
38125
38126_fx_cluts_rocketstock :
38127  u arabica_12,ava_614,azrael_93,bourbon_64,byers_11,chemical_168,clayton_33,clouseau_54,cobi_3,contrail_35,\
38128    cubicle_99,django_25,domingo_145,\
38129    faded_47,folger_50,fusion_88,hyla_68,korben_214,lenox_340,lucky_64,mckinnon_75,milo_5,neon_770,paladin_1875,\
38130    pasadena_21,pitaya_15,reeve_38,remy_24,sprocket_231,teigen_28,trent_18,tweed_71,vireo_37,zed_32,zeke_39
38131
38132_fx_cluts_shamoonabbasi :
38133  u city_7,coffee_44,date_39,day_for_night,denoiser_simple_40,desert_gold_37,directions_23,drop_green_tint_14,\
38134  elegance_38,golden_night_softner_43,golden_sony_37,green_15,happyness_133,hlg_1_1,industrial_33,morning_6,\
38135  morroco_16,night_king_141,rest_33,shadow_king_39,spy_29,thriller_2,turkiest_42,vintage_163,wooden_gold_20
38136
38137_fx_cluts_smallhd :
38138  u apocalypse_this_very_moment,bboyz_2,bob_ford,life_giving_tree,moonrise,saving_private_damon,the_matrices
38139
38140_fx_cluts_others :
38141  u 60s,60s_faded,60s_faded_alt,alien_green,black_and_white,bleach_bypass,blue_mono,\
38142    cinematic_01,cinematic_02,cinematic_03,\
38143    color_rich,faded,faded_alt,faded_analog,faded_extreme,faded_vivid,expired_fade,expired_polaroid,extreme,fade,\
38144    faux_infrared,golden,golden_bright,golden_fade,golden_mono,golden_vibrant,green_mono,hong_kong,instantc,\
38145    k_tone_vintage_kodachrome,light_blown,lomo,mono_tinted,\
38146    muted_fade,mute_shift,natural_vivid,nostalgic,orange_tone,pink_fade,purple,retro,rotate_muted,\
38147    rotate_vibrant,rotated,rotated_crush,\
38148    smooth_cromeish,smooth_fade,soft_fade,solarized_color,solarized_color_2,summer,summer_alt,sunny,sunny_alt,\
38149    sunny_warm,\
38150    sunny_rich,super_warm,super_warm_rich,sutro_fx,vibrant,vibrant_alien,vibrant_contrast,vibrant_cromeish,\
38151    vintage,vintage_alt,vintage_brighter,warm,warm_highlight,warm_yellow
38152
38153_fx_cluts_youssefhossam :
38154  u cinematic_forest,city,darkness,hallowen_dark,sea
38155
38156#@gui Colorful Blobs : fx_colorful_blobs, fx_colorful_blobs_preview
38157#@gui : Colorspace = choice(1,"sRGB","Linear RGB","Lab")
38158#@gui : Background Color = color(200,200,200,0)
38159#@gui : Display Blob Controls = bool(1)
38160#@gui : sep = separator()
38161#@gui : Blob 1 = point(25,25,1,1,0,0,0,0,5)
38162#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38163#@gui : Blob 1 Color = color(255,0,0)
38164#@gui : Previous = value(-1,-1,-1,-1)
38165#@gui : sep = separator()
38166#@gui : Blob2 = point(75,25,1,1,0,0,0,0,5)
38167#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38168#@gui : Blob 2 Color = color(0,255,0)
38169#@gui : Previous = value(-1,-1,-1,-1)
38170#@gui : sep = separator()
38171#@gui : Blob 3 = point(50,75,1,1,0,0,0,0,5)
38172#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38173#@gui : Blob 3 Color = color(0,0,255)
38174#@gui : Previous = value(-1,-1,-1,-1)
38175#@gui : sep = separator()
38176#@gui : Blob 4 = point(5,90,-1,1,0,0,0,0,5)
38177#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38178#@gui : Blob 4 Color = color(255,255,0)
38179#@gui : Previous = value(-1,-1,-1,-1)
38180#@gui : sep = separator()
38181#@gui : Blob 5 = point(5,90,-1,1,0,0,0,0,5)
38182#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38183#@gui : Blob 5 Color = color(255,0,255)
38184#@gui : Previous = value(-1,-1,-1,-1)
38185#@gui : sep = separator()
38186#@gui : Blob 6 = point(5,90,-1,1,0,0,0,0,5)
38187#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38188#@gui : Blob 6 Color = color(0,255,255)
38189#@gui : Previous = value(-1,-1,-1,-1)
38190#@gui : sep = separator()
38191#@gui : Blob 7 = point(5,90,-1,1,0,0,0,0,5)
38192#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38193#@gui : Blob 7 Color = color(255,255,255)
38194#@gui : Previous = value(-1,-1,-1,-1)
38195#@gui : sep = separator()
38196#@gui : Blob 8 = point(5,90,-1,1,0,0,0,0,5)
38197#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38198#@gui : Blob 8 Color = color(0,0,0)
38199#@gui : Previous = value(-1,-1,-1,-1)
38200#@gui : sep = separator()
38201#@gui : Blob 9 = point(5,90,-1,1,0,0,0,0,5)
38202#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38203#@gui : Blob 9 Color = color(255,128,64)
38204#@gui : Previous = value(-1,-1,-1,-1)
38205#@gui : sep = separator()
38206#@gui : Blob 10 = point(5,90,-1,1,0,0,0,0,5)
38207#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38208#@gui : Blob 10 Color = color(255,64,128)
38209#@gui : Previous = value(-1,-1,-1,-1)
38210#@gui : sep = separator()
38211#@gui : Blob 11 = point(5,90,-1,1,0,0,0,0,5)
38212#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38213#@gui : Blob 11 Color = color(128,64,255)
38214#@gui : Previous = value(-1,-1,-1,-1)
38215#@gui : sep = separator()
38216#@gui : Blob 12 = point(5,90,-1,1,0,0,0,0,5)
38217#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
38218#@gui : Blob 12 Color = color(64,128,255)
38219#@gui : Previous = value(-1,-1,-1,-1)
38220#@gui : sep = separator()
38221#@gui : note = note("This filter can be used to create custom palettes with given color shades.
38222#@gui : It has been inspired by
38223#@gui : <a href="https://research.adobe.com/project/playful-palette-an-interactive-parametric-color-mixer-for-artists/">
38224#@gui : Adobe's Playful Palette</a>.")
38225#@gui : sep = separator()
38226#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/08/26</i>.</small>")
38227fx_colorful_blobs :
38228  N=12
38229  colorspace,bgR,bgG,bgB,bgA,display_controls,\
38230  x0,y0,rx0,ry0,R0,G0,B0,p_x0,p_y0,p_rx0,p_ry0,\
38231  x1,y1,rx1,ry1,R1,G1,B1,p_x1,p_y1,p_rx1,p_ry1,\
38232  x2,y2,rx2,ry2,R2,G2,B2,p_x2,p_y2,p_rx2,p_ry2,\
38233  x3,y3,rx3,ry3,R3,G3,B3,p_x3,p_y3,p_rx3,p_ry3,\
38234  x4,y4,rx4,ry4,R4,G4,B4,p_x4,p_y4,p_rx4,p_ry4,\
38235  x5,y5,rx5,ry5,R5,G5,B5,p_x5,p_y5,p_rx5,p_ry5,\
38236  x6,y6,rx6,ry6,R6,G6,B6,p_x6,p_y6,p_rx6,p_ry6,\
38237  x7,y7,rx7,ry7,R7,G7,B7,p_x7,p_y7,p_rx7,p_ry7,\
38238  x8,y8,rx8,ry8,R8,G8,B8,p_x8,p_y8,p_rx8,p_ry8,\
38239  x9,y9,rx9,ry9,R9,G9,B9,p_x9,p_y9,p_rx9,p_ry9,\
38240  x10,y10,rx10,ry10,R10,G10,B10,p_x10,p_y10,p_rx10,p_ry10,\
38241  x11,y11,rx11,ry11,R11,G11,B11,p_x11,p_y11,p_rx11,p_ry11,\
38242  =$*
38243  if !0$_is_preview display_controls=0 fi
38244  if $1==1 srgb2cs=srgb2rgb cs2srgb=rgb2srgb
38245  elif $1==2 srgb2cs=srgb2lab cs2srgb=lab2srgb
38246  fi
38247
38248  {0,s=min(w,h);[s,s]},1,4 k. 100%,100%,1,1,1e-8
38249
38250  repeat $N
38251    # If center point has been moved -> Update radius point.
38252    rx$>,ry$>={"P = ["${x$>},${y$>}"];
38253                R = ["${rx$>},${ry$>}"];
38254                oP = ["${p_x$>},${p_y$>}"];
38255                oP==[-1,-1]?P + [10,0]:P!=oP?R + P - oP:R"}
38256
38257    if !isnan(${x$>})
38258      x,y,rx,ry,R,G,B={"const w1 = (w - 1)%; const h1 = (h -1)%; "\
38259                       round([${x$>}*w1,${y$>}*h1,${rx$>}*w1,${ry$>}*h1,${R$>},${G$>},${B$>}])}
38260      r={max(1,round(norm($x-$rx,$y-$ry)))}
38261      if $1 ($R^$G^$B) $srgb2cs. R,G,B={^} rm. fi
38262      f. "*
38263        pexp(x) = x<2?(res = 1; px = x^2; res+=-1.17282*px; px*=x; res+=0.683221*px; px*=x; res+=-0.110353*px):0;
38264        const r = 1.2*"($r)";
38265        dist = norm(x-"$x",y-"$y")/r;
38266        w = pexp(dist);
38267        j(#0,0,0,0) += w*"$R";
38268        j(#0,0,0,0,1) += w*"$G";
38269        j(#0,0,0,0,2) += w*"$B";
38270        i + w;
38271      "
38272    fi
38273  done
38274  sh[0] 0,2 /. [1] if $1 $cs2srgb. fi
38275  f[0] "*begin(bg = [ "$bgR,$bgG,$bgB,$bgA" ]); i(#1)<0.5?bg:[R,G,B,255]" k[0]
38276
38277  repeat $N
38278    if !isnan(${x$>})" && "$display_controls
38279    x,y,rx,ry,R,G,B={"const w1 = (w - 1)%; const h1 = (h -1)%; "\
38280                     round([${x$>}*w1,${y$>}*h1,${rx$>}*w1,${ry$>}*h1,${R$>},${G$>},${B$>}])}
38281      circle $x,$y,3,0.85,0xFFFFFFFF,{v=avg(crop($x-3,$y-3,7,7))>128?0:255;[v,v,v,255]}
38282      rectangle {"const x = "$rx"; const y = "$ry"; [x-2,y-2,x+2,y+2]"},0.85,0xFFFFFFFF,\
38283                {v=avg(crop($rx-3,$ry-3,7,7))>128?0:255;[v,v,v,255]}
38284      line $x,$y,$rx,$ry,0.5,0xF0F0F0F0,255 line $x,$y,$rx,$ry,0.5,0x0F0F0F0F,0,0,0,255
38285    fi
38286  done
38287
38288  if 0$_is_preview
38289    u \{$colorspace\}\{$bgR,$bgG,$bgB,$bgA\}\{$display_controls\}\
38290      \{$x0,$y0\}\{$rx0,$ry0\}\{$R0,$G0,$B0\}\{$x0,$y0,$rx0,$ry0\}\
38291      \{$x1,$y1\}\{$rx1,$ry1\}\{$R1,$G1,$B1\}\{$x1,$y1,$rx1,$ry1\}\
38292      \{$x2,$y2\}\{$rx2,$ry2\}\{$R2,$G2,$B2\}\{$x2,$y2,$rx2,$ry2\}\
38293      \{$x3,$y3\}\{$rx3,$ry3\}\{$R3,$G3,$B3\}\{$x3,$y3,$rx3,$ry3\}\
38294      \{$x4,$y4\}\{$rx4,$ry4\}\{$R4,$G4,$B4\}\{$x4,$y4,$rx4,$ry4\}\
38295      \{$x5,$y5\}\{$rx5,$ry5\}\{$R5,$G5,$B5\}\{$x5,$y5,$rx5,$ry5\}\
38296      \{$x6,$y6\}\{$rx6,$ry6\}\{$R6,$G6,$B6\}\{$x6,$y6,$rx6,$ry6\}\
38297      \{$x7,$y7\}\{$rx7,$ry7\}\{$R7,$G7,$B7\}\{$x7,$y7,$rx7,$ry7\}\
38298      \{$x8,$y8\}\{$rx8,$ry8\}\{$R8,$G8,$B8\}\{$x8,$y8,$rx8,$ry8\}\
38299      \{$x9,$y9\}\{$rx9,$ry9\}\{$R9,$G9,$B9\}\{$x9,$y9,$rx9,$ry9\}\
38300      \{$x10,$y10\}\{$rx10,$ry10\}\{$R10,$G10,$B10\}\{$x10,$y10,$rx10,$ry10\}\
38301      \{$x11,$y11\}\{$rx11,$ry11\}\{$R11,$G11,$B11\}\{$x11,$y11,$rx11,$ry11\}
38302   fi
38303
38304fx_colorful_blobs_preview :
38305  _is_preview=1
38306  rm {s=min($_preview_width,$_preview_height)/2;[s,s]},1,1
38307  fx_colorful_blobs $*
38308
38309#@gui Colormap : fx_colormap,fx_colormap_preview
38310#@gui : Colormap = choice{2,"Adaptive","Custom","Standard (256)","HSV (256)","Lines (256)","Hot (256)",
38311#@gui : "Cool (256)","Jet (256)","Flag (256)","Cube (256)"}
38312#@gui : Dithering = float(1,0,1)
38313#@gui : sep = separator()
38314#@gui : Number of Tones = int(32,2,256)_0
38315#@gui : Number of Colors = int(8,2,8)_0
38316#@gui : 1st Color = color(0,0,0)_0
38317#@gui : 2nd Color = color(255,255,255)_0
38318#@gui : 3rd Color = color(255,0,0)_0
38319#@gui : 4th Color = color(0,255,0)_0
38320#@gui : 5th Color = color(0,0,255)_0
38321#@gui : 6th Color = color(255,255,0)_0
38322#@gui : 7th Color = color(255,0,255)_0
38323#@gui : 8th Color = color(0,255,255)_0+
38324#@gui : sep = separator()
38325#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38326#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38327#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38328#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38329#@gui : sep = separator()
38330#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/27/12</i>.</small>")
38331fx_colormap :
38332  repeat $! l[$>] split_opacity to_rgb[0]
38333    if $1>=2                # Pre-defined colormap.
38334      index[0] {$1-2},$2,1
38335    elif $1==1              # Custom colormap.
38336      (${5-28}) z. 0,{3*$4-1}
38337      r. 3,{w/3},1,1,-1 permute. yzcx r. $3,1,1,3,3
38338      index[0] .,$2,1 rm.
38339    else                      # Adaptive colormap.
38340      autoindex[0] $3,$2,{if($3<=32,1,0)}
38341    fi
38342    a c
38343  endl done
38344
38345fx_colormap_preview :
38346  gui_split_preview "fx_colormap $*",${-3--1}
38347  is_ad,is_cu={2*[$1==0||$1==1,$1==1]}
38348  u "{$1}{$2}"\
38349    "{$3}_"$is_ad\
38350    "{$4}_"$is_cu\
38351    "{${5-7}}_"$is_cu\
38352    "{${8-10}}_"$is_cu\
38353    "{${11-13}}_"$is_cu\
38354    "{${14-16}}_"$is_cu\
38355    "{${17-19}}_"$is_cu\
38356    "{${20-22}}_"$is_cu\
38357    "{${23-25}}_"$is_cu\
38358    "{${26-28}}_"$is_cu\
38359    "{$29}{$30,$31}"
38360
38361#@gui Color Mask [Interactive] : fx_mask_color, gui_no_preview
38362#@gui : Color Metric = _choice(13,"RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","Linear RGB [All]",
38363#@gui : "Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
38364#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [red chrominance]",
38365#@gui : "YCbCr [green chrominance]","Lab [all]","Lab [lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
38366#@gui : "Lab [b-Chrominance]","Lch [all]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]",
38367#@gui : "HSV [all]","HSV [hue]","HSV [saturation]","HSV [value]","HSI [all]","HSI [intensity]","HSL [all]",
38368#@gui : "HSL [lightness]","CMYK [cyan]","CMYK [magenta]","CMYK [yellow]","CMYK [key]","YIQ [luma]","YIQ [chromas]")
38369#@gui : Spatial Tolerance = _float(10,0,100)
38370#@gui : Color Tolerance = _float(5,0,100)
38371#@gui : sep = separator()
38372#@gui : Output Mode = _choice(0,"Masked image","Color mask")
38373#@gui : sep = separator()
38374#@gui : note = note{"<small><b>Note:</b> This filter is CPU consuming, so use it at least with 4+ cores
38375#@gui : (or reduce the size of the interactive window to speed up computation).</small>"}
38376#@gui : note = note{"<small><b>Interactions:</b>\n
38377#@gui : Use the following actions in the interactive window to build your color mask :\n\n
38378#@gui : - <b>Left mouse button</b> make the color pointed by the mouse wanted for the mask.\n
38379#@gui : - <b>Right mouse button</b> make the color pointed by the mouse unwanted for the mask.\n
38380#@gui : - <b>Middle mouse button</b> or key <b>R</b> resets color mask.\n
38381#@gui : - Key <b>SPACE</b> or <b>TAB</b> toggles view modes (half/full-masked RGB or color mask).\n
38382#@gui : - Keys <b>CTRL+D</b> increase window size.\n
38383#@gui : - Keys <b>CTRL+C</b> decrease window size.\n
38384#@gui : - Keys <b>CTRL+R</b> resets window size.\n
38385#@gui : - Keys <b>ESC</b>, <b>Q</b> or <b>ENTER</b> exit the interactive window.
38386#@gui : </small>"}
38387#@gui : sep = separator()
38388#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>01/20/2017</i>.</small>")
38389fx_mask_color :
38390  cs=rgb,rgb_r,rgb_g,rgb_b,lrgb,lrgb_r,lrgb_g,lrgb_b,ycbcr_y,ycbcr_cbcr,ycbcr_cb,ycbcr_cr,ycbcr_cg,\
38391     lab,lab_l,lab_ab,lab_a,lab_b,lch,lch_c,lch_h,hsv,hsv_h,hsv_s,hsv_v,hsi,hsi_i,hsl,hsl_l,\
38392     cmyk_c,cmyk_m,cmyk_y,cmyk_k,yiq_y,yiq_iq
38393  repeat $! l[$<] to_rgb nm={n} nm ${-gui_layer_name}
38394    +x_mask_color ${arg\ 1+$1,$cs},$2,$3
38395    if $4==1 channels. 100% fi
38396    nm $nm rv
38397  endl done
38398
38399#@gui Curves : fx_curves_interactive, fx_curves_interactive_preview
38400#@gui : Colorspace = choice{"RGB","CMY","CMYK","HSI","HSL","HSV","Lab","Lch","YCbCr"}
38401#@gui : Output Preset as a HaldCLUT Layer = _choice("Disable","Lowres CLUT","Highres CLUT")
38402#@gui : Apply Transformation From = _choice("New Curves [Interactive]","Curves Previously Defined")
38403#@gui : Colorspace = value(0)
38404#@gui : Keypoints = value(0,0,100,100,-1,0,0,100,100,-1,0,0,100,100,-1,0,0,100,100,-1,0,0,100,100)
38405#@gui : sep = separator()
38406#@gui : note = note{"<small><b>Description:</b>\n
38407#@gui : This filter allows to apply color curves on your images, in many different colorspaces.
38408#@gui : Click on the <i>Apply</i> or <i>OK</i> buttons below to open the G'MIC interactive windows and
38409#@gui : start building your color curves.
38410#@gui : When you're done, exit the main image window: your modified result will be transferred back to the
38411#@gui : host software.\n\n
38412#@gui : Once you've set curves, you can save them by pressing the <b>Add to faves</b> button below the filter tree.
38413#@gui : To clear control points for your curves, click on the <i>Reset</i> button above.
38414#@gui : </small>"}
38415#@gui : sep = separator()
38416#@gui : note = note{"<small><b>Interactions:</b>\n
38417#@gui : Use the following actions in the interactive windows to manage your colorization :\n\n
38418#@gui : - <b>Left mouse button</b> on a curve creates a new color control point (or move an existing one).\n
38419#@gui : - <b>Right mouse button</b> on a control point deletes it.\n
38420#@gui : - <b>Left mouse button</b> on the main image window shows the initial image until button is released.\n
38421#@gui : - <b>Right mouse button</b> on the main image window adds a keypoint to all curves from picked color.\n
38422#@gui : - Key <b>R</b> on a curve resets it.\n
38423#@gui : - Keys <b>CTRL+D</b> increase window size.\n
38424#@gui : - Keys <b>CTRL+C</b> decrease window size.\n
38425#@gui : - Keys <b>CTRL+R</b> resets window size.\n
38426#@gui : - Keys <b>ESC</b>, <b>Q</b> or <b>ENTER</b> close the current window.
38427#@gui : </small>"}
38428#@gui : sep = separator()
38429#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>09/28/2014</i>.</small>")
38430fx_curves_interactive :
38431  nm "Color curves"
38432  repeat 4 __xcc_C$>=0,0,100,100 done
38433  if $4==$1 l[] ($5) s -,-1 repeat $! __xcc_C$>={$>,^} done rm endl fi
38434  if $3 # Apply transformation from previously defined curves
38435    _xcc_colorbase=${arg\ {$4+1},rgb,cmy,cmyk,hsi,hsl,hsv,lab,lch,ycbcr} x_color_curves last
38436  else # Run interactive curve builder
38437    x_color_curves ${arg\ {$1+1},rgb,cmy,cmyk,hsi,hsl,hsv,lab,lch,ycbcr}
38438    u "{$1}{$2}{$3}{$1}{"$__xcc_C0,-1,$__xcc_C1,-1,$__xcc_C2,-1,$__xcc_C3,-1,$__xcc_C4"}"
38439  fi
38440  if $2 # Add HaldCLUT layer
38441    (0,255) (0;255) (0/255) r[-3--1] 2,2,2 a[-3--1] c
38442    if $2==2 r. 256,256,256,3,3 r. 4096,4096,1,3,-1 # High-res HaldCLUT
38443    else r. 64,64,64,3,3 r. 512,512,1,3,-1 # Low-res HaldCLUT
38444    fi
38445    x_color_curves. last
38446  fi
38447
38448fx_curves_interactive_preview :
38449  fx_curves_interactive $1,0,1,$4,"$5"
38450
38451#@gui Customize CLUT : fx_customize_clut,fx_customize_clut_preview(1)+
38452#@gui : Keypoint Influence (%) = float(100,0,100)
38453#@gui : Lock Uniform Sampling = choice{0,"None","8 Keypoints (RGB Corners)","27 Keypoints","64 Keypoints",
38454#@gui : "125 Keypoints","216 Keypoints","343 Keypoints"},
38455#@gui : Spatial Regularization = int(10,0,30)
38456#@gui : sep = separator()
38457#@gui : note = note("<small><b>Global correction:</b></small>")
38458#@gui : Brightness (%) = float(0,-100,100)
38459#@gui : Contrast (%) = float(0,-100,100)
38460#@gui : Gamma (%) = float(0,-100,100)
38461#@gui : Hue (%) = float(0,-100,100)
38462#@gui : Saturation (%) = float(0,-100,100)
38463#@gui : Post-Normalize = bool(0)
38464#@gui : sep = separator()
38465#@gui : Output Corresponding CLUT = _choice("Disable","512x512 Layer","4096x4096 Layer")
38466#@gui : Preview Type = choice{8,"Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38467#@gui : "Backward Vertical","Duplicate Horizontal","Duplicate Vertical","HaldCLUT","3D CLUT (Fast)","3D CLUT (Precise)"}
38468#@gui : CLUT Opacity = float(0.5,0,1)
38469#@gui : sep = separator()
38470#@gui : note = note("<small><b>Color correspondences:</b></small>")
38471#@gui : Action #1 = choice(1,"Ignore","Lock Source","Replace Source by Target")
38472#@gui : Source Color #1 = color(0,0,0), Target Color #1 = color(0,0,0)
38473#@gui : sep = separator()
38474#@gui : Action #2 = choice(1,"Ignore","Lock Source","Replace Source by Target")
38475#@gui : Source Color #2 = color(255,255,255), Target Color #2 = color(255,196,128)
38476#@gui : sep = separator()
38477#@gui : Action #3 = choice("Ignore","Lock Source","Replace Source by Target")
38478#@gui : Source Color #3 = color(0,0,0), Target Color #3 = color(0,0,0)
38479#@gui : sep = separator()
38480#@gui : Action #4 = choice("Ignore","Lock Source","Replace Source by Target")
38481#@gui : Source Color #4 = color(0,0,0), Target Color #4 = color(0,0,0)
38482#@gui : sep = separator()
38483#@gui : Action #5 = choice("Ignore","Lock Source","Replace Source by Target")
38484#@gui : Source Color #5 = color(0,0,0), Target Color #5 = color(0,0,0)
38485#@gui : sep = separator()
38486#@gui : Action #6 = choice("Ignore","Lock Source","Replace Source by Target")
38487#@gui : Source Color #6 = color(0,0,0), Target Color #6 = color(0,0,0)
38488#@gui : sep = separator()
38489#@gui : Action #7 = choice("Ignore","Lock Source","Replace Source by Target")
38490#@gui : Source Color #7 = color(0,0,0), Target Color #7 = color(0,0,0)
38491#@gui : sep = separator()
38492#@gui : Action #8 = choice("Ignore","Lock Source","Replace Source by Target")
38493#@gui : Source Color #8 = color(0,0,0), Target Color #8 = color(0,0,0)
38494#@gui : sep = separator()
38495#@gui : Action #9 = choice("Ignore","Lock Source","Replace Source by Target")
38496#@gui : Source Color #9 = color(0,0,0), Target Color #9 = color(0,0,0)
38497#@gui : sep = separator()
38498#@gui : Action #10 = choice("Ignore","Lock Source","Replace Source by Target")
38499#@gui : Source Color #10 = color(0,0,0), Target Color #10 = color(0,0,0)
38500#@gui : sep = separator()
38501#@gui : Action #11 = choice("Ignore","Lock Source","Replace Source by Target")
38502#@gui : Source Color #11 = color(0,0,0), Target Color #11 = color(0,0,0)
38503#@gui : sep = separator()
38504#@gui : Action #12 = choice("Ignore","Lock Source","Replace Source by Target")
38505#@gui : Source Color #12 = color(0,0,0), Target Color #12 = color(0,0,0)
38506#@gui : sep = separator()
38507#@gui : Action #13 = choice("Ignore","Lock Source","Replace Source by Target")
38508#@gui : Source Color #13 = color(0,0,0), Target Color #13 = color(0,0,0)
38509#@gui : sep = separator()
38510#@gui : Action #14 = choice("Ignore","Lock Source","Replace Source by Target")
38511#@gui : Source Color #14 = color(0,0,0), Target Color #14 = color(0,0,0)
38512#@gui : sep = separator()
38513#@gui : Action #15 = choice("Ignore","Lock Source","Replace Source by Target")
38514#@gui : Source Color #15 = color(0,0,0), Target Color #15 = color(0,0,0)
38515#@gui : sep = separator()
38516#@gui : Action #16 = choice("Ignore","Lock Source","Replace Source by Target")
38517#@gui : Source Color #16 = color(0,0,0), Target Color #16 = color(0,0,0)
38518#@gui : sep = separator()
38519#@gui : Action #17 = choice("Ignore","Lock Source","Replace Source by Target")
38520#@gui : Source Color #17 = color(0,0,0), Target Color #17 = color(0,0,0)
38521#@gui : sep = separator()
38522#@gui : Action #18 = choice("Ignore","Lock Source","Replace Source by Target")
38523#@gui : Source Color #18 = color(0,0,0), Target Color #18 = color(0,0,0)
38524#@gui : sep = separator()
38525#@gui : Action #19 = choice("Ignore","Lock Source","Replace Source by Target")
38526#@gui : Source Color #19 = color(0,0,0), Target Color #19 = color(0,0,0)
38527#@gui : sep = separator()
38528#@gui : Action #20 = choice("Ignore","Lock Source","Replace Source by Target")
38529#@gui : Source Color #20 = color(0,0,0), Target Color #20 = color(0,0,0)
38530#@gui : sep = separator()
38531#@gui : Action #21 = choice("Ignore","Lock Source","Replace Source by Target")
38532#@gui : Source Color #21 = color(0,0,0), Target Color #21 = color(0,0,0)
38533#@gui : sep = separator()
38534#@gui : Action #22 = choice("Ignore","Lock Source","Replace Source by Target")
38535#@gui : Source Color #22 = color(0,0,0), Target Color #22 = color(0,0,0)
38536#@gui : sep = separator()
38537#@gui : Action #23 = choice("Ignore","Lock Source","Replace Source by Target")
38538#@gui : Source Color #23 = color(0,0,0), Target Color #23 = color(0,0,0)
38539#@gui : sep = separator()
38540#@gui : Action #24 = choice("Ignore","Lock Source","Replace Source by Target")
38541#@gui : Source Color #24 = color(0,0,0), Target Color #24 = color(0,0,0)
38542#@gui : sep = separator()
38543#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/14/06</i>.</small>")
38544fx_customize_clut :
38545
38546  # Build CLUT.
38547  if !narg($_N) N=64 else N=$_N fi N1={$N-1}
38548  $N,$N,$N,4
38549
38550  if $2 # Lock uniform sampling
38551    uniform_distribution {(1+$2)^3},3
38552    repeat w point.. {round($N1*I[$>])},1,{255*I[$>]},1 done rm.
38553  fi
38554
38555  $=arg # Add user-defined color correspondences
38556  repeat 24
38557    mode=${arg{13+7*$<}}
38558    if $mode
38559      sr=${arg{14+7*$<}} sg=${arg{15+7*$<}} sb=${arg{16+7*$<}}
38560      tr=${arg{17+7*$<}} tg=${arg{18+7*$<}} tb=${arg{19+7*$<}}
38561      xyz={round(($N1/255)*[$sr,$sg,$sb])}
38562      point. $xyz,1,{$mode==2?[$tr,$tg,$tb]:[$sr,$sg,$sb]},1
38563    fi
38564  done
38565
38566  s c,-3
38567  if $1<100 # Need to compute a weighting map.
38568    +distance. 1
38569    if $1 ^. {1/(0.05+4*$1%)} else f. 0 fi
38570    n. 0,1 nm. influence mv. -3
38571  fi
38572  ==. 0 inpaint_pde.. .,100%,1,20 rm. c. 0,255
38573
38574  if $influence
38575    100%,100%,100%,3,[x,y,z] n. 0,255
38576    j. ..,0,0,0,0,1,...
38577    rm[-3,-2]
38578  fi
38579
38580  # Apply CLUT on input layers + global color corrections.
38581  if !$3 map_clut[^-1] . # w/o spatial regularization
38582  else repeat $!-1       # w/ spatial regularization
38583    +luminance[$>] +map_clut[$>] .. -. [$>]
38584    repeat $3 guided. ..,2,50 done +[$>,-1] rm.
38585  done fi
38586  adjust_colors ${4-8},0,255
38587  if $9 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi
38588  if $10
38589    if $10==2 r. 256,256,256,3,5 c. 0,255 fi
38590    siz={w^1.5} r. $siz,$siz,1,3,-1
38591    mv. 0
38592  else rm.
38593  fi
38594
38595fx_customize_clut_preview :
38596  if $11<7 gui_split_preview "fx_customize_clut ${1-9},0,0,${12--1}",$11
38597  elif $11==7 # HaldCLUT preview
38598    rm fx_customize_clut ${1-9},1,0,${12--1}
38599  elif $11>=8 # 3D CLUT preview
38600    _N={$11>=9?64:32}
38601    k[0] to_rgb w={w} h={h}
38602    +fx_customize_clut ${1-9},1,0,${12--1} mv. 1
38603    r. $_N,$_N,$_N,3,-1 pointcloud3d. o3d. $12
38604    l[]
38605      if $2 # Lock uniform sampling
38606        uniform_distribution {(1+$2)^3},3
38607        repeat w circle3d {0,round($_N*I[$>])},0.75 col3d. {0,255*I[$>]} done rm[0]
38608      fi
38609      $=arg # Add user-defined color correspondences
38610      repeat 24
38611        mode=${arg{13+7*$<}}
38612        if $mode
38613          sr=${arg{14+7*$<}} sg=${arg{15+7*$<}} sb=${arg{16+7*$<}}
38614          tr=${arg{17+7*$<}} tg=${arg{18+7*$<}} tb=${arg{19+7*$<}}
38615          xy={round(($_N/255)*[$sr,$sg])}
38616          z={round(($_N/255)*$sb)-0.1}
38617          circle3d $xy,$z,0.75 col3d. {$mode==2?[$tr,$tg,$tb]:[$sr,$sg,$sb]}
38618        fi
38619      done
38620      colorcube3d *3d. {$_N/255} o3d. 0.5 col3d. 0 p3d. 1
38621    endl
38622    +3d[2--1]
38623    pose3d. 5.10656,2.04904,2.723,-316.115,-0.0815767,4.97762,-3.59262,-41.7094,\
38624            -3.40685,2.95212,4.16756,-118.811,0,0,203,1
38625
38626    # Try to find the best layout for displaying preview.
38627    if $w>$h # Landscape mode
38628      r2dx[0,1] {0,round(w/2)}
38629      to[0] "Before",2,0,13,1,0.75
38630      to[1] "After",2,0,13,1,0.75
38631      a[0,1] y r[0] 100%,$h,1,3,0
38632    else # Portrait mode.
38633      r2dy[0,1] {0,round(h/2)}
38634      to[0] "Before",2,0,13,1,0.75
38635      to[1] "After",2,0,13,1,0.75
38636      a[0,1] x r[0] $w,100%,1,3,0
38637    fi
38638
38639    snapshot3d. {0,1.1*min(w,h)},1.2,64,64,64
38640    autocrop. -. 64 r. {0,max(w,$w-w)},{0,max(h,$h-h)},1,3,0,0,0.5,0.5 +. 64
38641    to. "RGB CLUT",2,0,13,1,0.75
38642    a {`$w>$h?_'x':_'y'`}
38643  fi
38644
38645#@gui Decompose Channels : fx_decompose_channels, fx_decompose_channels_preview
38646#@gui : Color Basis = choice(7,"RGB","HSV","HSL","HSI","YUV","YCbCr","XYZ","Lab","Lch","CMY","CMYK","YIQ")
38647#@gui : Action = choice("Decompose","Recompose")
38648#@gui : Output Multiple Layers = _bool(0)
38649#@gui : Include Opacity Layer = bool(1)
38650#@gui : sep = separator()
38651#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/19/07</i>.</small>")
38652fx_decompose_channels :
38653  if !$2 # Decompose
38654    if $4 to_rgba else to_rgb fi
38655    repeat $! l[$<] nm={0,n}
38656      split_opacity
38657      _s3=A _s4=A
38658      _fx_decompose_channels$1[0]
38659      s[0] c
38660      if !$3 a x nm $nm
38661      else nm=${-gui_layer_name} repeat $! gui_set_layer_name[$>] {``$nm}" ["${_s$>}"]" done
38662      fi
38663    endl done
38664  else # Recompose
38665    channels 0 nbc={3+($1==10)} nb={$nbc+$4}
38666    if $3 repeat int($!/$nb) l[0-{$nb-1}]
38667      a[0-{$nbc-1}] c _fx_recompose_channels$1[0] a c
38668    endl mv. 0 done
38669    else repeat $! l[$>]
38670      s x,$nb a[0-{$nbc-1}] c _fx_recompose_channels$1[0] a c
38671    endl mv. 0 done fi
38672  fi
38673
38674fx_decompose_channels_preview :
38675  repeat $! l[$<]
38676    _s3=A _s4=A
38677    fx_decompose_channels $1,$2,1,$4
38678    if !$2
38679      fs={round(min(w,h)*15%)}
38680      repeat $! to[$>] ${_s$>},5,3,$fs,{max(2,round($fs/15))} done
38681      to_rgba
38682    fi
38683  endl done
38684  append_tiles ,
38685
38686_fx_decompose_channels0 : _s0=R _s1=G _s2=B
38687_fx_decompose_channels1 : rgb2hsv8 _s0=H _s1=S _s2=V
38688_fx_decompose_channels2 : rgb2hsl8 _s0=H _s1=S _s2=L
38689_fx_decompose_channels3 : rgb2hsi8 _s0=H _s1=S _s2=I
38690_fx_decompose_channels4 : rgb2yuv8 _s0=Y _s1=U _s2=V
38691_fx_decompose_channels5 : rgb2ycbcr _s0=Y _s1=Cb _s2=Cr
38692_fx_decompose_channels6 : rgb2xyz8 _s0=X _s1=Y _s2=Z
38693_fx_decompose_channels7 : rgb2lab8 _s0=L _s1=a _s2=b
38694_fx_decompose_channels8 : rgb2lch8 _s0=L _s1=c _s2=h
38695_fx_decompose_channels9 : rgb2cmy _s0=C _s1=M _s2=Y
38696_fx_decompose_channels10 : rgb2cmyk _s0=C _s1=M _s2=Y _s3=K
38697_fx_decompose_channels11 : rgb2yiq8 _s0=Y _s1=I _s2=Q
38698
38699_fx_recompose_channels0 :
38700_fx_recompose_channels1 : hsv82rgb
38701_fx_recompose_channels2 : hsl82rgb
38702_fx_recompose_channels3 : hsi82rgb
38703_fx_recompose_channels4 : yuv82rgb
38704_fx_recompose_channels5 : ycbcr2rgb
38705_fx_recompose_channels6 : xyz82rgb
38706_fx_recompose_channels7 : lab82rgb
38707_fx_recompose_channels8 : lch82rgb
38708_fx_recompose_channels9 : cmy2rgb
38709_fx_recompose_channels10 : cmyk2rgb
38710_fx_recompose_channels11 : yiq82rgb
38711
38712#@gui Detect Skin : fx_detect_skin, fx_detect_skin_preview(1)
38713#@gui : Skin Estimation = choice(1,"Manual","Automatic")
38714#@gui : sep = separator()
38715#@gui : Tolerance = float(0.5,0,1)
38716#@gui : Smoothness = float(0.5,0,5)
38717#@gui : Threshold = float(1,0,10)
38718#@gui : Pre-Normalize Image = bool(1)
38719#@gui : sep = separator()
38720#@gui : note = note("<small><b>Manual estimation:</b>\n
38721#@gui : Use the sliders below to target as much skin pixels as you can.</small>")
38722#@gui : X-Coordinate = float(50,0,100)
38723#@gui : Y-Coordinate = float(50,0,100)
38724#@gui : Radius = float(5,0,25)
38725#@gui : sep = separator()
38726#@gui : Output Mode = choice(1,"Probability Map","Opaque Skin","Transparent Skin")
38727#@gui : sep = separator()
38728#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38729#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38730#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38731#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38732#@gui : sep = separator()
38733#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/03/01</i>.</small>")
38734fx_detect_skin :
38735  to_rgb
38736  m "_fx_detect_skin :
38737       if $5 balance_gamma 128,128,128 fi
38738       if $1 detect_skin $2 else detect_skin $2,$6%,$7%,$8% fi
38739       M={iM} b $3% * {255*$M/iM} * $4 c 0,255"
38740  repeat $! l[$>]
38741    if $9  # Opaque/transparent skin.
38742      +_fx_detect_skin a c
38743      if $9>1 sh 100% *. -1 +. 255 rm. fi
38744    else _fx_detect_skin # Probability mask.
38745    fi
38746  endl done
38747  um _fx_detect_skin
38748
38749fx_detect_skin_preview :
38750  gui_split_preview "fx_detect_skin $*",${-3--1}
38751  to_rgba
38752  if !$1
38753    circle $6%,$7%,$8%,0.3,0,255,0,255
38754    circle $6%,$7%,$8%,1,0xFFFFFFFF,0,255,0,255
38755    line {$6-0.25*$8}%,{$7-0.25*$8}%,{$6+0.25*$8}%,{$7+0.25*$8}%,1,255,255,0,255
38756    line {$6+0.25*$8}%,{$7-0.25*$8}%,{$6-0.25*$8}%,{$7+0.25*$8}%,1,255,255,0,255
38757  fi
38758
38759#@gui Equalize HSV : fx_hsv_equalizer, fx_hsv_equalizer_preview
38760#@gui : Preview Bands = bool(false)
38761#@gui : sep = separator()
38762#@gui : Hue Band = float(180,0,360)
38763#@gui : Band Width = float(40,1,360)
38764#@gui : Hue Shift = float(0,-180,180)
38765#@gui : Saturation Correction = float(0,-0.99,0.99)
38766#@gui : Value Correction = float(0,-0.99,0.99)
38767#@gui : sep = separator()
38768#@gui : Hue Band = float(180,0,360)
38769#@gui : Band Width = float(40,1,360)
38770#@gui : Hue Shift = float(0,-180,180)
38771#@gui : Saturation Correction = float(0,-0.99,0.99)
38772#@gui : Value Correction = float(0,-0.99,0.99)
38773#@gui : sep = separator()
38774#@gui : Hue Band = float(180,0,360)
38775#@gui : Band Width = float(40,1,360)
38776#@gui : Hue Shift = float(0,-180,180)
38777#@gui : Saturation Correction = float(0,-0.99,0.99)
38778#@gui : Value Correction = float(0,-0.99,0.99)
38779#@gui : sep = separator()
38780#@gui : note = note("<small>Author: <i>Jérome Ferrari</i>.
38781#@gui :       Latest Update: <i>01/14/2011</i>.</small>")
38782#@gui : url = link("Filter explained here","http://www.flickr.com/groups/gmic/discuss/72157625798533482")
38783fx_hsv_equalizer :
38784  repeat $! l[$>]
38785  to_rgb rgb2hsv s c
38786# From now on 0,1,2 are H,S,V
38787#3 masks:
38788  +f[0] if(abs(i-$2)<$3/2|abs(i-$2-360)<$3/2|abs(i-$2+360)<$3/2,1,0)
38789  +f[0] if(abs(i-$7)<$8/2|abs(i-$7-360)<$8/2|abs(i-$7+360)<$8/2,1,0)
38790  +f[0] if(abs(i-$12)<$13/2|abs(i-$12-360)<$13/2|abs(i-$12+360)<$13/2,1,0)
38791# From now on 3,4,5 are Masks
38792  +threshold[1,2] 0.01 *[-1,-2] [-1]x2 *[-1,3] *[-1,4] *[-1,5]  #0 saturation and value not in mask
38793# Hue shift:
38794  +*[3] $4 +*[4] $9 +*[5] $14 +[-1,-2,-3]
38795  +[-1,0] %[0] 360
38796# Saturation :
38797  if $5>=0 +*[3] -$5 else +*[3] {1/(1+$5)-1} fi +. 1
38798  if $10>=0 +*[4] -$10 else +*[4] {1/(1+$10)-1} fi +. 1
38799  if $15>=0 +*[5] -$15 else +*[5] {1/(1+$15)-1} fi +. 1
38800  *[-1,-2,-3] ^[1,-1]
38801# Value :
38802  if $6>=0 +*[3] -$6 else +*[3] {1/(1+$6)-1} fi +. 1
38803  if $11>=0 +*[4] -$11 else +*[4] {1/(1+$11)-1} fi +. 1
38804  if $16>=0 +*[5] -$16 else +*[5] {1/(1+$16)-1} fi +. 1
38805  *[-1,-2,-3] ^[2,-1]
38806#reconstruction
38807  rm[3,4,5] a[0,1,2] c hsv2rgb
38808  endl done
38809
38810fx_hsv_equalizer_preview :
38811  l.
38812  if $1==0 fx_hsv_equalizer $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16
38813  else
38814    to_rgb rgb2hsv s c
38815    (0,359) r. ..,{{0,h}/10},1,1,3 . f. 1       #create lower band
38816    j[0] [3],0,91% j[1] [4],0,91% j[2] [4],0,91% rm[-1,-2] #paste lower band
38817    +f[0] if(abs(i-$2)<$3/2|abs(i-$2-360)<$3/2|abs(i-$2+360)<$3/2,1,0)
38818    +f[0] if(abs(i-$7)<$8/2|abs(i-$7-360)<$8/2|abs(i-$7+360)<$8/2,1,0)
38819    +f[0] if(abs(i-$12)<$13/2|abs(i-$12-360)<$13/2|abs(i-$12+360)<$13/2,1,0) #masks
38820    -|[-3--1] +. 0.33 /. 1.33   #1 and 0.25
38821    *[2,-1] a c hsv2rgb
38822  fi endl
38823
38824#@gui Equalize HSI-HSL-HSV : fx_equalize_hsv, fx_equalize_hsv_preview(0)+
38825#@gui : Colorspace = choice(1,"HSI","HSL","HSV")
38826#@gui : Opacity (%) = float(100,0,100)
38827#@gui : Value Blending = float(0,0,64)
38828#@gui : Color Blending = float(0,0,64)
38829#@gui : sep = separator()
38830#@gui : Preview Mapping = choice("None","Grey","Color")
38831#@gui : sep = separator()
38832#@gui : note = note("<small><b>Black:</b></small>")
38833#@gui : Hue Offset = float(0,-180,180)
38834#@gui : Saturation Offset = float(0,-1,1)
38835#@gui : Value Offset = float(0,-1,1)
38836#@gui : sep = separator()
38837#@gui : note = note("<small><b>Near black:</b></small>")
38838#@gui : Hue Offset = float(0,-180,180)
38839#@gui : Saturation Offset = float(0,-1,1)
38840#@gui : Value Offset = float(0,-1,1)
38841#@gui : sep = separator()
38842#@gui : note = note("<small><b>Dark grey:</b></small>")
38843#@gui : Hue Offset = float(0,-180,180)
38844#@gui : Saturation Offset = float(0,-1,1)
38845#@gui : Value Offset = float(0,-1,1)
38846#@gui : sep = separator()
38847#@gui : note = note("<small><b>Mi-dark grey:</b></small>")
38848#@gui : Hue Offset = float(0,-180,180)
38849#@gui : Saturation Offset = float(0,-1,1)
38850#@gui : Value Offset = float(0,-1,1)
38851#@gui : sep = separator()
38852#@gui : note = note("<small><b>Middle grey:</b></small>")
38853#@gui : Hue Offset = float(0,-180,180)
38854#@gui : Saturation Offset = float(0,-1,1)
38855#@gui : Value Offset = float(0,-1,1)
38856#@gui : sep = separator()
38857#@gui : note = note("<small><b>Mid-light grey:</b></small>")
38858#@gui : Hue Offset = float(0,-180,180)
38859#@gui : Saturation Offset = float(0,-1,1)
38860#@gui : Value Offset = float(0,-1,1)
38861#@gui : sep = separator()
38862#@gui : note = note("<small><b>Light grey:</b></small>")
38863#@gui : Hue Offset = float(0,-180,180)
38864#@gui : Saturation Offset = float(0,-1,1)
38865#@gui : Value Offset = float(0,-1,1)
38866#@gui : sep = separator()
38867#@gui : note = note("<small><b>Highlights:</b></small>")
38868#@gui : Hue Offset = float(0,-180,180)
38869#@gui : Saturation Offset = float(0,-1,1)
38870#@gui : Value Offset = float(0,-1,1)
38871#@gui : sep = separator()
38872#@gui : note = note("<small><b>White:</b></small>")
38873#@gui : Hue Offset = float(0,-180,180)
38874#@gui : Saturation Offset = float(0,-1,1)
38875#@gui : Value Offset = float(0,-1,1)
38876#@gui : sep = separator()
38877#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38878#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38879#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38880#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38881#@gui : sep = separator()
38882#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>David Revoy</i>.
38883#@gui :       Latest Update: <i>2018/01/19</i>.</small>")
38884fx_equalize_hsv :
38885  cs=${"arg 1+$1,hsi,hsl,hsv"}
38886  repeat $! l[$>] split_opacity l[0]
38887    to_rgb
38888    9,1,1,4,\
38889    {"V = [${6-30:3}]*pi/180; [cos(V),sin(V)]"},${7-31:3},\
38890    {"V = [${8-32:3}];
38891      const sV = size(V);
38892      repeat (sV,k,
38893        v0 = k/sV;
38894        v1 = (k+1)/sV;
38895        V[k]*=(V[k]>0?(1 - v0):v1);
38896      ); V"}
38897    r. 256,1,1,4,1
38898    sh. 3 b. $3 rm.   # Value smoothness
38899    f. "[ atan2(G,R)*180/pi,B,A,0 ]" channels. 0,2
38900    +rgb2$cs.. +channels. 100% *. 256 round. map. ...
38901    +[-2,-1] rm.. +channels. 100% ${cs}2rgb..
38902    if $4 # Spatial smoothness
38903      *. 255 bilateral.. .,$4,{2+$4}
38904      rgb2$cs.. /. 255 j.. .,0,0,0,2 ${cs}2rgb..
38905    fi
38906    rm.
38907    blend alpha,{$2%}
38908  endl a c endl done
38909
38910fx_equalize_hsv_preview :
38911  if $5
38912    cs=${"arg 1+$1,hsi,hsl,hsv"}
38913    rm {0.8*[${-gui_preview_wh}]},1,3,\
38914    "$5==1?
38915       (H = S = 0; V = y/(h-1)):
38916       (H = x*360/(w-1); S = y/(h-1); V = y/(h-1));
38917     [H,S,V]"
38918    ${cs}2rgb.
38919  fi
38920  gui_split_preview "fx_equalize_hsv $*",${-3--1}
38921  if $5 r. ${-gui_preview_wh},1,3,0,0,0.5,0.5 fi
38922
38923#@gui Mixer [CMYK] : fx_mix_cmyk, fx_mix_cmyk_preview(1)+
38924#@gui : Cyan Factor = float(1,0,4)
38925#@gui : Cyan Shift = float(0,-255,255)
38926#@gui : Cyan Smoothness = float(0,0,10)
38927#@gui : sep = separator()
38928#@gui : Magenta Factor = float(1,0,4)
38929#@gui : Magenta Shift = float(0,-255,255)
38930#@gui : Magenta Smoothness = float(0,0,10)
38931#@gui : sep = separator()
38932#@gui : Yellow Factor = float(1,0,4)
38933#@gui : Yellow Shift = float(0,-255,255)
38934#@gui : Yellow Smoothness = float(0,0,10)
38935#@gui : sep = separator()
38936#@gui : Key Factor = float(1,0,4)
38937#@gui : Key Shift = float(0,-255,255)
38938#@gui : Key Smoothness = float(0,0,10)
38939#@gui : sep = separator()
38940#@gui : Tones Range = choice("All tones","Shadows","Mid-Tones","Highlights")
38941#@gui : Tones Smoothness = float(2,0,10)
38942#@gui : sep = separator()
38943#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38944#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38945#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38946#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38947#@gui : sep = separator()
38948#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
38949fx_mix_cmyk :
38950  repeat $! l. split_opacity rv to_rgb.
38951    fx_start_mix $13,$14
38952    rgb2cmyk. s. c
38953    *[-4] $1 +[-4] $2 b[-4] $3%
38954    *... $4 +... $5 b... $6%
38955    *.. $7 +.. $8 b.. $9%
38956    *. $10 +. $11 b. $12%
38957    a[-4--1] c cmyk2rgb.
38958    fx_end_mix $13
38959  if $!!=3 rv a c fi endl mv. 0 done
38960
38961fx_mix_cmyk_preview :
38962  gui_split_preview "fx_mix_cmyk $*",${-3--1}
38963
38964#@gui Mixer [HSV] : fx_mix_hsv, fx_mix_hsv_preview(1)+
38965#@gui : Hue Factor = float(1,0,4)
38966#@gui : Hue Shift = float(0,-180,180)
38967#@gui : Hue Smoothness = float(0,0,10)
38968#@gui : sep = separator()
38969#@gui : Saturation Factor = float(1,0,4)
38970#@gui : Saturation Shift = float(0,-1,1)
38971#@gui : Saturation Smoothness = float(0,0,10)
38972#@gui : sep = separator()
38973#@gui : Value Factor = float(1,0,4)
38974#@gui : Value Shift = float(0,-1,1)
38975#@gui : Value Smoothness = float(0,0,10)
38976#@gui : sep = separator()
38977#@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights")
38978#@gui : Tones Smoothness = float(2,0,10)
38979#@gui : sep = separator()
38980#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38981#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38982#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38983#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38984#@gui : sep = separator()
38985#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
38986fx_mix_hsv :
38987  repeat $! l. split_opacity rv to_rgb.
38988    fx_start_mix $10,$11
38989    rgb2hsv. s. c -[-2,-1] 0.5
38990    *... $1 +... $2 b... $3%
38991    *.. $4 +.. $5 b.. $6%
38992    *. $7 +. $8 b. $9%
38993    %... 360 +[-2,-1] 0.5 c[-2,-1] 0,1 a[-3--1] c hsv2rgb.
38994    fx_end_mix $10
38995  if $!!=3 rv a c fi endl mv. 0 done
38996
38997fx_mix_hsv_preview :
38998  gui_split_preview "fx_mix_hsv $*",${-3--1}
38999
39000#@gui Mixer [Lab] : fx_mix_lab, fx_mix_lab_preview(1)+
39001#@gui : Lightness Factor = float(1,0.5,1.5)
39002#@gui : Lightness Shift = float(0,-50,50)
39003#@gui : Lightness Smoothness = float(0,0,10)
39004#@gui : sep = separator()
39005#@gui : A-Color Factor = float(1,0,4)
39006#@gui : A-Color Shift = float(0,-20,20)
39007#@gui : A-Color Smoothness = float(0,0,10)
39008#@gui : sep = separator()
39009#@gui : B-Color Factor = float(1,0,4)
39010#@gui : B-Color Shift = float(0,-20,20)
39011#@gui : B-Color Smoothness = float(0,0,10)
39012#@gui : sep = separator()
39013#@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights")
39014#@gui : Tones Smoothness = float(2,0,10)
39015#@gui : sep = separator()
39016#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39017#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39018#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39019#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39020#@gui : sep = separator()
39021#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
39022fx_mix_lab :
39023  repeat $! l[$>] split_opacity to_rgb[0]
39024    gui_parallel_overlap[0] "_fx_mix_lab $*",0,{3*max($3,$6,$9)}
39025    a c
39026  endl mv[$>] 0 done
39027
39028_fx_mix_lab :
39029  fx_start_mix $10,$11
39030  rgb2lab. s. c
39031  *... $1 +... $2 b... $3%
39032  *.. $4 +.. $5 b.. $6%
39033  *. $7 +. $8 b. $9%
39034  a[-3--1] c lab2rgb.
39035  fx_end_mix $10
39036
39037fx_mix_lab_preview :
39038  gui_split_preview "fx_mix_lab $*",${-3--1}
39039
39040#@gui Mixer [PCA] : fx_mix_pca, fx_mix_pca_preview(1)+
39041#@gui : Primary Factor = float(0,-1.5,1.5)
39042#@gui : Primary Shift = float(0,-255,255)
39043#@gui : Primary Twist = float(0,-180,180)
39044#@gui : Primary Gamma = float(0,-100,100)
39045#@gui : sep = separator()
39046#@gui : Secondary Factor = float(0,-1.5,1.5)
39047#@gui : Secondary Shift = float(0,-255,255)
39048#@gui : Secondary Twist = float(0,-180,180)
39049#@gui : Secondary Gamma = float(0,-100,100)
39050#@gui : sep = separator()
39051#@gui : Tertiary Factor = float(0,-1.5,1.5)
39052#@gui : Tertiary Shift = float(0,-255,255)
39053#@gui : Tertiary Twist = float(0,-180,180)
39054#@gui : Tertiary Gamma = float(0,-100,100)
39055#@gui : sep = separator()
39056#@gui : Display Color Axes = bool(1)
39057#@gui : Stats = value(-1,-1,-1,-1)
39058#@gui : Avg Covariance = value(0,0,0,0,0,0,0,0,0,0,0,0)
39059#@gui : sep = separator()
39060#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39061#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39062#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39063#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39064#@gui : sep = separator()
39065#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/07/18</i>.</small>")
39066fx_mix_pca :
39067  repeat $! l[$>] split_opacity l[0] to_rgb
39068
39069    # Get image covariance (and remember it to speed up multiple calls of the filter).
39070    if [$14]==round(stats()[0,4],0.1) _avg={[$15][0,3]} C={[$15][3,9]} status=
39071    else
39072      +rr2d 256,256,0,2 C=${"covariance_colors. _avg"} rm.
39073      __status="{$1}{$2}{$3}{$4}"\
39074               "{$5}{$6}{$7}{$8}"\
39075               "{$9}{$10}{$11}{$12}"\
39076               "${13}"\
39077               "{"{round(stats()[0,4],0.1)}"}"\
39078               "{"$_avg,$C"}"\
39079               "{$16}{${17,18}}"
39080    fi
39081
39082    # Find value ranges for gamma correction.
39083    if "$4 || $8 || $12" +l
39084      f "begin(avg = ["$_avg"]; eig = eig(["$C"]); Pt = eig[3,9]); Pt*(I-avg)"
39085      s c repeat $! vmax$>={$>,1.1*max(abs(im),abs(iM))} done
39086      rm
39087    endl else vmax0,vmax1,vmax2=1 fi
39088
39089    # Modify image values.
39090    f "begin(
39091         do_gamma(val,vmax,gamma) = (vmax*sign(val)*(abs(val)/vmax)^gamma);
39092
39093         const gamma0 = 10^-($4%);
39094         const gamma1 = 10^-($8%);
39095         const gamma2 = 10^-($12%);
39096         const vmax0 = "$vmax0";
39097         const vmax1 = "$vmax1";
39098         const vmax2 = "$vmax2";
39099
39100         avg = ["$_avg"];
39101         eig = eig(["$C"]);
39102         for (k = 3, k<12, k+=3, eig[k]<0?copy(eig[k],eig[k,3]*=-1,3));
39103         Pt = eig[3,9];
39104         P = transpose(Pt,3);
39105         T = mul(P,diag(10^[$1,$5,$9]),3);
39106
39107         R1 = rot(eig[3,3],$3°);
39108         R2 = rot(eig[6,3],$7°);
39109         R3 = rot(eig[9,3],$11°);
39110         T = mul(R1,mul(R2,mul(R3,T,3),3),3);
39111         avg_shift = avg + $2*eig[3,3] + $6*eig[6,3] + $10*eig[9,3];
39112
39113         if ("0$_is_preview",
39114           L = [ 2,5,10]*sqrt(1e-5 + eig[0,3]);
39115           run('__cols=',vtos(round([
39116             avg - L[0]*eig[3,3],
39117             avg + L[0]*eig[3,3],
39118             avg - L[1]*eig[6,3],
39119             avg + L[1]*eig[6,3],
39120             avg - L[2]*eig[9,3],
39121             avg + L[2]*eig[9,3] ])));
39122         );
39123       );
39124       nI = Pt*(I - avg);
39125       ($4 || $8 || $12)?(
39126         nI[0] = do_gamma(nI[0],vmax0,gamma0);
39127         nI[1] = do_gamma(nI[1],vmax1,gamma1);
39128         nI[2] = do_gamma(nI[2],vmax2,gamma2);
39129       );
39130       avg_shift + T*nI"
39131    c 0,255
39132  endl a c endl done u $__status
39133
39134fx_mix_pca_preview :
39135  _is_preview=1
39136  __status=
39137  repeat $! l[$>]
39138    gui_split_preview "fx_mix_pca ${1-13},\"$14\",\"$15\",${16-18}",${-3--1}
39139    if $13
39140      rr2d ${-gui_preview_wh},0,1
39141      ($__cols) r. 3,6,1,1,-1 permute. yzcx s. x,3
39142      r[-3--1] {w#0/2},13,1,3,3 c[-3--1] 0,255
39143      frame[-3--1] 1,1,0
39144      to[0] Primary,4,2,13,1 j[0] ...,64,4
39145      to[0] Secondary,4,17,13,1 j[0] ..,64,19
39146      to[0] Tertiary,4,32,13,1 j[0] .,64,34
39147      k[0]
39148    fi
39149  endl done
39150  u $__status
39151
39152#@gui Mixer [RGB] : fx_mix_rgb, fx_mix_rgb_preview(1)+
39153#@gui : Red Factor = float(1,0,4)
39154#@gui : Red Shift = float(0,-255,255)
39155#@gui : Red Smoothness = float(0,0,10)
39156#@gui : sep = separator()
39157#@gui : Green Factor = float(1,0,4)
39158#@gui : Green Shift = float(0,-255,255)
39159#@gui : Green Smoothness = float(0,0,10)
39160#@gui : sep = separator()
39161#@gui : Blue Factor = float(1,0,4)
39162#@gui : Blue Shift = float(0,-255,255)
39163#@gui : Blue Smoothness = float(0,0,10)
39164#@gui : sep = separator()
39165#@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights")
39166#@gui : Tones Smoothness = float(2,0,10)
39167#@gui : sep = separator()
39168#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39169#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39170#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39171#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39172#@gui : sep = separator()
39173#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
39174fx_start_mix :
39175  if $1==1 +tones. 3 +[-2,-1] b[-2,-1] $2% r[-2,-1] ... *. ... mv... $!
39176  elif $1==2 +tones. 3 +[-3,-1] b[-2,-1] $2% r[-2,-1] ... *.. ... mv... $!
39177  elif $1==3 +tones. 3 +[-3,-2] b[-2,-1] $2% r[-2,-1] ... *.. ... mv... $!
39178  fi
39179fx_end_mix :
39180  if $1==1 *[-3,-1] +[-2,-1]
39181  elif $1==2 *[-2,-1] +[-2,-1]
39182  elif $1==3 *[-2,-1] +[-2,-1]
39183  fi
39184  c 0,255
39185
39186fx_mix_rgb :
39187  repeat $! l. split_opacity rv to_rgb.
39188    fx_start_mix $10,$11
39189    -. 128 s. c
39190    *... $1 +... $2 b... $3%
39191    *.. $4 +.. $5 b.. $6%
39192    *. $7 +. $8 b. $9%
39193    a[-3--1] c +. 128 c. 0,255
39194    fx_end_mix $10
39195  if $!!=3 rv a c fi endl mv. 0 done
39196
39197fx_mix_rgb_preview :
39198  gui_split_preview "fx_mix_rgb $*",${-3--1}
39199
39200#@gui Mixer [YCbCr] : fx_mix_ycbcr, fx_mix_ycbcr_preview(1)+
39201#@gui : Luminance Factor = float(1,0,4)
39202#@gui : Luminance Shift = float(0,-255,255)
39203#@gui : Luminance Smoothness = float(0,0,10)
39204#@gui : sep = separator()
39205#@gui : Blue Chroma Factor = float(1,0,4)
39206#@gui : Blue Chroma Shift = float(0,-255,255)
39207#@gui : Blue Chroma Smoothness = float(0,0,10)
39208#@gui : sep = separator()
39209#@gui : Red Chroma Factor = float(1,0,4)
39210#@gui : Red Chroma Shift = float(0,-255,255)
39211#@gui : Red Chroma Smoothness = float(0,0,10)
39212#@gui : sep = separator()
39213#@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights")
39214#@gui : Tones Smoothness = float(2,0,10)
39215#@gui : sep = separator()
39216#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39217#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39218#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39219#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39220#@gui : sep = separator()
39221#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
39222fx_mix_ycbcr :
39223  repeat $! l. split_opacity rv to_rgb.
39224    fx_start_mix $10,$11
39225    rgb2ycbcr. -. 128 s. c
39226    *... $1 +... $2 b... $3%
39227    *.. $4 +.. $5 b.. $6%
39228    *. $7 +. $8 b. $9%
39229    a[-3--1] c +. 128 c. 0,255 ycbcr2rgb.
39230    fx_end_mix $10
39231  if $!!=3 rv a c fi endl mv. 0 done
39232
39233fx_mix_ycbcr_preview :
39234  gui_split_preview "fx_mix_ycbcr $*",${-3--1}
39235
39236#@gui CLUT from After - Before Layers : fx_clut_from_ab, fx_clut_from_ab_preview
39237#@gui : Output Mode = choice("Replace Layer with CLUT","Insert New CLUT Layer","Save CLUT as .cube or .png File")
39238#@gui : Output CLUT Resolution = choice{4,16,25,36,49,64,81,100,121,144,169,225,256}_2
39239#@gui : sep = separator()
39240#@gui : Output Folder = _folder()_1-
39241#@gui : Output Filename = _text("output.cube")_1+
39242#@gui : sep = separator()
39243#@gui : Influence of Color Samples (%) = float(50,0,100)_2
39244#@gui : sep = separator()
39245#@gui : note = note{"<b>What is this filter for?</b>\n\n
39246#@gui : This filter requires at least two input layers to work properly.\n
39247#@gui : It assumes you have an input top layer <b>A</b> and a base layer <b>B</b> such that <b>A</b> and
39248#@gui : <b>B</b> both represent the same image but with only color variations
39249#@gui : (typically <b>A</b> has been obtained from <b>B</b> using the color curves tool).\n\n
39250#@gui : This filter is then able to estimate and outputs a color HaldCLUT <b>H</b> so that applying <b>H</b>
39251#@gui : on the base layer <b>B</b> gives back <b>A</b>.\n\n
39252#@gui : This is useful when you have a color transformation between two images, that you want to recover and
39253#@gui : re-apply on a bunch of other images.
39254#@gui : "}
39255#@gui : sep = separator()
39256#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      
39257#@gui : Latest Update: <i>2019/08/27</i>.</small>")
39258fx_clut_from_ab :
39259  if $!<2 error "At least two input layers are needed to run this filter." fi
39260  repeat $!-1 l[$<,-1] nm=${gui_layer_name..}
39261    i[0] {a=0$_is_preview?32:64;[a,a,a]},4 f[1] ">I(#0,round(I(#2)*(w#0-1)/255))+=[R,G,B,1]; I"
39262    l[0]
39263      s c,-3 +max. 1 /[-3,-1] ==. 0 inpaint_pde.. .,75%,1 distance. 0 *. {-1/(1+$5)} exp.
39264      f.. "f = i(#-1); f*I + (1-f)*[x,y,z]*255/(w-1)" rm.
39265      S={arg(1+$2,16,25,36,49,64,81,100,121,144,169,225,256)} if $S!=w r. $S,$S,$S,3,3 fi
39266      c 0,255
39267    endl
39268
39269    if $1==2 # Output as a file
39270      is_png={str=lowercase(['"$4"']);find(str,'.png')==size(str)-4}
39271      is_cube={str=lowercase(['"$4"']);find(str,'.cube')==size(str)-5}
39272      if !$is_png" && "!$is_cube $!is_ciube error "Filename extension must be '.cube' or '.png'." fi
39273      if $is_png r[0] {0,r=round(whd^0.5);[r,r]},1,3,-1 o[0] "$3/$4"
39274      else
39275        if {0,w>32} r3dx[0] 32 fi
39276        output_cube[0] "$3/$4"
39277      fi
39278      rm[0]
39279    else
39280      r[0] {0,r=round(whd^0.5);[r,r]},1,3,-1
39281      if !$1 rm[1] fi # Replace Layer with CLUT
39282      nm[0] "name(CLUT to '"$nm"')"
39283    fi
39284  if $_output_mode k[0] fi
39285  endl done
39286
39287fx_clut_from_ab_preview :
39288  if $!<2 gui_warning_preview "At least two input layers are needed to run this filter." return fi
39289  _is_preview,_output_mode=1 fx_clut_from_ab 0,4,0,0,$5
39290  repeat $! l[$>]
39291    r {a=round(cbrt(wh));[a,a,a]},3,-1
39292    +r3dx. 24 _fx_clut_from_ab_preview.
39293    S={arg(1+$2,16,25,36,49,64,81,100,121,144,169,225,256)} if $S!=w#0 r[0] $S,$S,$S,3,3 fi
39294    r[0] {0,r=round(whd^0.5);[r,r]},1,3,-1
39295    rr2d[0] $_preview_width,$_preview_height,2,1 r2dx. 70%
39296    sh. 100% b. 1% n. 0,255 rm.
39297    blend alpha
39298  endl done
39299
39300  file_attr={$1==2?2:1}
39301  u "{$1}{$2}{$3}_"$file_attr"{$4}_"$file_attr"{$5}"
39302
39303# Render 3D visualization of a CLUT.
39304_fx_clut_from_ab_preview :
39305  repeat $! l[$>]
39306    fact={256/w}
39307
39308    # Color data, as a point cloud.
39309    pointcloud3d +3d 0.5,0.5,0.5 *3d $fact circles3d {1.25*$fact} o3d {0.004*$fact}
39310
39311    # Add cube edges.
39312    colorcube3d[]
39313    l. s3d l.. s y,6 repeat $! sh[$>] 4,100%,0,0 /. 1.25 rm. done endl a y endl # Darken colors of edges
39314    p3d. 1
39315    l. repeat 8 o={a=$>;0.5*[a&1,(a>>1)&1,(a>>2)&1]} ++3d[0] $o +-3d[0] $o done +3d endl # Increase thickness of edges
39316
39317    # Add 3D axes, without the 'O'.
39318    axes3d 64,64,64,24,R,G,B,0
39319    +3d
39320
39321    # Render 3D snapshot.
39322    pose3d 1.2451,0.120715,-0.893986,-58.3864,-0.572953,1.28275,-0.62477,-11.6557,\
39323           0.696784,0.839071,1.08374,-333.008,0,0,217,1
39324    snapshot3d {max(512,0$_preview_width,0$_preview_height)},1.22,255,255,255
39325    autocrop frame 10,10,255
39326    to_rgba flood 0,0,0,20,1,1,255,255,255,0
39327  endl done
39328
39329#@gui Retinex : fx_retinex, fx_retinex_preview(0)+
39330#@gui : Strength (%) = float(75,0,100)
39331#@gui : Value Offset = float(16,1,256)
39332#@gui : Colorspace = choice(1,"HSI","HSV","Lab","Linear RGB","RGB","YCbCr")
39333#@gui : Min Cut (%) = float(1,0,100)
39334#@gui : Max Cut (%) = float(1,0,100)
39335#@gui : Regularization = float(5,0,32)
39336#@gui : sep = separator()
39337#@gui : Low Scale = float(15,1,512)
39338#@gui : Middle Scale = float(80,1,512)
39339#@gui : High Scale = float(250,1,512)
39340#@gui : sep = separator()
39341#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39342#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39343#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39344#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39345#@gui : sep = separator()
39346#@gui : note = note("<small><b>Note:</b> This filter implements the <i>Multiscale Color Retinex</i> algorithm,
39347#@gui : as described in:</small>")
39348#@gui : url = link{"http://www.ipol.im/pub/art/2014/107/"}
39349#@gui : sep = separator()
39350#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/13/09</i>.</small>")
39351fx_retinex :
39352  repeat $! l[$>]
39353    +retinex $2,${"arg 1+$3,hsi,hsv,lab,lrgb,rgb,ycbcr"},$4,$5,${7--1}
39354    if $6 guided. ..,$6,$6 fi
39355    j[0] .,0,0,0,0,{$1%} rm.
39356    c 0,255
39357  endl done
39358
39359fx_retinex_preview :
39360  gui_split_preview "fx_retinex $*",${-3--1}
39361
39362#@gui Retro Fade : fx_retrofade, fx_retrofade_preview
39363#@gui : Iterations = int(20,1,64)
39364#@gui : Colors = int(6,2,32)
39365#@gui : Grain = float(40,1,100)
39366#@gui : sep = separator()
39367#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39368#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39369#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39370#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39371#@gui : sep = separator()
39372#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/25/10</i>.</small>")
39373fx_retrofade :
39374  repeat $! l[$>] split_opacity l[0]
39375    +f 0
39376    repeat $1
39377      +noise[0] $3 c. 0,255 autoindex. $2,0,0
39378      +[-2,-1]
39379      progress {$>*100/$1}
39380    done
39381    k. n 0,255
39382    progress 100
39383  endl a c endl done
39384
39385fx_retrofade_preview :
39386  gui_split_preview "fx_retrofade $*",${-3--1}
39387
39388#@gui Select-Replace Color : fx_select_color, fx_select_color_preview(0)
39389#@gui : Similarity Space = choice(0,"RGB[A]","RGB","YCbCr","Red","Green","Blue","Opacity","Luminance",
39390#@gui : "Blue & Red Chrominances","Hue","Saturation")
39391#@gui : Tolerance = float(20,0,100)
39392#@gui : Smoothness = float(0,0,10)
39393#@gui : Fill Holes = int(0,0,256)
39394#@gui : Selected Color = color(255,255,255,255)
39395#@gui : Output As = choice(0,"Selected Colors","Selected Mask","Rejected Colors","Rejected Mask","Replaced Color")
39396#@gui : Replacement Color = color(255,0,0,255)
39397#@gui : sep = separator()
39398#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39399#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39400#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39401#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39402#@gui : sep = separator()
39403#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
39404_fx_select_color :
39405  if $1==1 to_rgb                          # RGB
39406  elif $1==2 to_rgb rgb2ycbcr              # YCbCr
39407  elif $1==3 channels 0                    # R
39408  elif $1==4 channels 1                    # G
39409  elif $1==5 channels 2                    # B
39410  elif $1==6 to_rgba channels 3            # Opacity
39411  elif $1==7 to_rgb rgb2ycbcr channels 0   # Luminance
39412  elif $1==8 to_rgb rgb2ycbcr channels 1,2 # B&R chrominances
39413  elif $1==9 to_rgb rgb2hsv channels 0     # Hue
39414  elif $1==10 to_rgb rgb2hsv channels 1    # Saturation
39415  fi
39416
39417fx_select_color :
39418  ($5^$6^$7^$8) _fx_select_color. $1 color={^} rm.
39419  repeat $! l[$>] to_rgba
39420    +_fx_select_color $1
39421    select_color[1] $2%,$color
39422    if $4 +area. 0,0 <=. {round($4^1.5)} inpaint.. .,0,3 rm. fi # Fill holes.
39423    b[1] $3 n[1] 0,255
39424    if $9==0 sh[0] 100% &. [1]                      # Selected colors.
39425    elif $9==1 rm[0]                                # Selected mask.
39426    elif $9==2 -[1] 255 *[1] -1 sh[0] 100% &. [1]   # Rejected colors.
39427    elif $9==3 rm[0] - 255 * -1                     # Rejected mask.
39428    else # Replaced color.
39429      /[1] 255 +*[0,1] +*[1] $11 +*[1] $12 +*[1] $13 *[1] $10 a[1,-3--1] c -[1,2] +
39430    fi
39431    k[0]
39432  endl done
39433
39434fx_select_color_preview :
39435  gui_split_preview "fx_select_color $*",${-3--1}
39436
39437#@gui Selective Desaturation : fx_selective_desaturation, fx_selective_desaturation_preview(1)
39438#@gui : Reference Color = color(255,255,255)
39439#@gui : Desaturate = choice("Reference Color","All but Reference Color")
39440#@gui : Strength = float(3,0,10)
39441#@gui : Regularization = int(0,0,20)
39442#@gui : Maximum Saturation = choice("From Input","From Reference Color","Maximum Value")
39443#@gui : sep = separator()
39444#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39445#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39446#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39447#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39448#@gui : sep = separator()
39449#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/15/07</i>.</small>")
39450fx_selective_desaturation :
39451  repeat $! l[$>] to_color split_opacity l[0]
39452    +fc $1,$2,$3
39453    -[1] [0] norm[1] /[1] {1e-6+iM}
39454    if $4 *[1] -{max(0.01,$5)} +[1] 1
39455    else >=[1] {5*$5}%
39456    fi
39457    c[1] 0,1
39458    rgb2hsl[0] s[0] c
39459    mM={[im,iM]} repeat $6 guided. [2],1,0.1 done n. $mM # Regularization step.
39460    if $7==0 *[1,-1]
39461    elif $7==1 ($1^$2^$3) rgb2hsl. *[1] {i[1]} rm[-2,-1]
39462    else rv[1,-1] rm.
39463    fi
39464    a c hsl2rgb
39465  endl a c endl done
39466
39467fx_selective_desaturation_preview :
39468  gui_split_preview "fx_selective_desaturation $*",${-3--1}
39469
39470#@gui Sepia : fx_sepia, fx_sepia_preview(1)+
39471#@gui : Brightness (%) = float(0,-100,100)
39472#@gui : Contrast (%) = float(0,-100,100)
39473#@gui : Gamma (%) = float(0,-100,100)
39474#@gui : sep = separator()
39475#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39476#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39477#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39478#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39479#@gui : sep = separator()
39480#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
39481fx_sepia :
39482  sepia adjust_colors ${1-3},0,0,0,255
39483
39484fx_sepia_preview :
39485  gui_split_preview "fx_sepia $*",${-3--1}
39486
39487#@gui Simulate Film : fx_simulate_film, fx_simulate_film_preview(1)+
39488#@gui : Category = choice{"Black & White (25)","Instant [Consumer] (54)","Instant [Pro] (68)","Fuji XTrans III (15)",
39489#@gui : "Negative [Color] (13)","Negative [New] (39)","Negative [Old] (44)","Print Films (12)","Slide [Color] (26)"}
39490
39491##### Black & White
39492#@gui : Preset = choice{1,"All [Collage]","None",
39493#@gui : "Agfa APX 100","Agfa APX 25","Fuji Neopan 1600","Fuji Neopan Acros 100","Ilford Delta 100",
39494#@gui : "Ilford Delta 3200","Ilford Delta 400","Ilford FP4 Plus 125","Ilford HP5 Plus 400","Ilford HPS 800",
39495#@gui : "Ilford Pan F Plus 50","Ilford XP2","Kodak BW 400 CN","Kodak HIE (HS Infra)","Kodak T-Max 100",
39496#@gui : "Kodak T-Max 3200","Kodak T-Max 400","Kodak Tri-X 400","Polaroid 664","Polaroid 667","Polaroid 672",
39497#@gui : "Rollei IR 400","Rollei Ortho 25","Rollei Retro 100 Tonal","Rollei Retro 80s"}_2
39498
39499##### Instant [Consumer]
39500#@gui : Preset = choice{1,"All [Collage]","None",
39501#@gui : "Polaroid PX-100UV+ Cold --","Polaroid PX-100UV+ Cold -","Polaroid PX-100UV+ Cold",
39502#@gui : "Polaroid PX-100UV+ Cold +","Polaroid PX-100UV+ Cold ++","Polaroid PX-100UV+ Cold +++",
39503#@gui : "Polaroid PX-100UV+ Warm --","Polaroid PX-100UV+ Warm -","Polaroid PX-100UV+ Warm",
39504#@gui : "Polaroid PX-100UV+ Warm +","Polaroid PX-100UV+ Warm ++","Polaroid PX-100UV+ Warm +++",
39505#@gui : "Polaroid PX-680 --","Polaroid PX-680 -","Polaroid PX-680","Polaroid PX-680 +","Polaroid PX-680 ++",
39506#@gui : "Polaroid PX-680 Cold --","Polaroid PX-680 Cold -","Polaroid PX-680 Cold","Polaroid PX-680 Cold +",
39507#@gui : "Polaroid PX-680 Cold ++","Polaroid PX-680 Cold ++a","Polaroid PX-680 Warm --","Polaroid PX-680 Warm -",
39508#@gui : "Polaroid PX-680 Warm","Polaroid PX-680 Warm +","Polaroid PX-680 Warm ++","Polaroid PX-70 --",
39509#@gui : "Polaroid PX-70 -","Polaroid PX-70","Polaroid PX-70 +","Polaroid PX-70 ++","Polaroid PX-70 +++",
39510#@gui : "Polaroid PX-70 Cold --","Polaroid PX-70 Cold -","Polaroid PX-70 Cold","Polaroid PX-70 Cold +",
39511#@gui : "Polaroid PX-70 Cold ++","Polaroid PX-70 Warm --","Polaroid PX-70 Warm -","Polaroid PX-70 Warm",
39512#@gui : "Polaroid PX-70 Warm +","Polaroid PX-70 Warm ++","Polaroid Time Zero (Expired) ---",
39513#@gui : "Polaroid Time Zero (Expired) --","Polaroid Time Zero (Expired) -","Polaroid Time Zero (Expired)",
39514#@gui : "Polaroid Time Zero (Expired) +","Polaroid Time Zero (Expired) ++","Polaroid Time Zero (Expired) Cold ---",
39515#@gui : "Polaroid Time Zero (Expired) Cold --","Polaroid Time Zero (Expired) Cold -",
39516#@gui : "Polaroid Time Zero (Expired) Cold"}_0
39517
39518##### Instant [Pro]
39519#@gui : Preset = choice{1,"All [Collage]","None",
39520#@gui : "Fuji FP-100c --","Fuji FP-100c -","Fuji FP-100c","Fuji FP-100c (alt)","Fuji FP-100c +","Fuji FP-100c ++",
39521#@gui : "Fuji FP-100c ++a","Fuji FP-100c +++",
39522#@gui : "Fuji FP-100c Cool --","Fuji FP-100c Cool -","Fuji FP-100c Cool","Fuji FP-100c Cool +","Fuji FP-100c Cool ++",
39523#@gui : "Fuji FP-100c Negative --","Fuji FP-100c Negative -","Fuji FP-100c Negative","Fuji FP-100c Negative +",
39524#@gui : "Fuji FP-100c Negative ++","Fuji FP-100c Negative ++a","Fuji FP-100c Negative +++",
39525#@gui : "Fuji FP-3000b --","Fuji FP-3000b -","Fuji FP-3000b","Fuji FP-3000b +","Fuji FP-3000b ++","Fuji FP-3000b +++",
39526#@gui : "Fuji FP-3000b HC","Fuji FP-3000b Negative --","Fuji FP-3000b Negative -","Fuji FP-3000b Negative",
39527#@gui : "Fuji FP-3000b Negative +","Fuji FP-3000b Negative ++","Fuji FP-3000b Negative +++",
39528#@gui : "Fuji FP-3000b Negative Early","Polaroid 665 --","Polaroid 665 -","Polaroid 665","Polaroid 665 +",
39529#@gui : "Polaroid 665 ++","Polaroid 665 Negative -","Polaroid 665 Negative","Polaroid 665 Negative +",
39530#@gui : "Polaroid 665 Negative HC","Polaroid 669 --","Polaroid 669 -","Polaroid 669","Polaroid 669 +",
39531#@gui : "Polaroid 669 ++","Polaroid 669 +++","Polaroid 669 Cold --","Polaroid 669 Cold -","Polaroid 669 Cold",
39532#@gui : "Polaroid 669 Cold +","Polaroid 690 --","Polaroid 690 -","Polaroid 690","Polaroid 690 +","Polaroid 690 ++",
39533#@gui : "Polaroid 690 Cold --","Polaroid 690 Cold -","Polaroid 690 Cold","Polaroid 690 Cold +","Polaroid 690 Cold ++",
39534#@gui : "Polaroid 690 Warm --","Polaroid 690 Warm -","Polaroid 690 Warm","Polaroid 690 Warm +","Polaroid 690 Warm ++"}_0
39535
39536#### Fuji XTrans III
39537#@gui : Preset = choice{1,"All [Collage]","None",
39538#@gui : "Acros","Acros+G","Acros+R","Acros+Ye","Astia","Classic Chrome","Mono","Mono+G","Mono+R","Mono+Ye",
39539#@gui : "Pro Neg Hi","Pro Neg Std","Provia","Sepia","Velvia"}_0
39540
39541##### Negative [Color]
39542#@gui : Preset = choice{1,"All [Collage]","None",
39543#@gui : "Agfa Ultra Color 100","Agfa Vista 200","Fuji Superia 200","Fuji Superia HG 1600","Fuji Superia Reala 100",
39544#@gui : "Fuji Superia X-Tra 800","Kodak Ektar 100","Kodak Elite 100 XPRO","Kodak Elite Color 200",
39545#@gui : "Kodak Elite Color 400","Kodak Portra 160 NC","Kodak Portra 160 VC","Lomography Redscale 100"}_0
39546
39547##### Negative [New]
39548#@gui : Preset = choice{1,"All [Collage]","None",
39549#@gui : "Fuji 160C -","Fuji 160C","Fuji 160C +","Fuji 160C ++",
39550#@gui : "Fuji 400H -","Fuji 400H","Fuji 400H +","Fuji 400H ++",
39551#@gui : "Fuji 800Z -","Fuji 800Z","Fuji 800Z +","Fuji 800Z ++",
39552#@gui : "Fuji Ilford HP5 -","Fuji Ilford HP5","Fuji Ilford HP5 +","Fuji Ilford HP5 ++",
39553#@gui : "Kodak Portra 160 -","Kodak Portra 160","Kodak Portra 160 +","Kodak Portra 160 ++",
39554#@gui : "Kodak Portra 400 -","Kodak Portra 400","Kodak Portra 400 +","Kodak Portra 400 ++",
39555#@gui : "Kodak Portra 800 -","Kodak Portra 800","Kodak Portra 800 +","Kodak Portra 800 ++","Kodak Portra 800 HC",
39556#@gui : "Kodak T-MAX 3200 -","Kodak T-MAX 3200","Kodak T-MAX 3200 +","Kodak T-MAX 3200 ++","Kodak T-MAX 3200 (alt)",
39557#@gui : "Kodak TRI-X 400 -","Kodak TRI-X 400","Kodak TRI-X 400 +","Kodak TRI-X 400 ++","Kodak TRI-X 400 (alt)"}_0
39558
39559##### Negative [Old]
39560#@gui : Preset = choice{1,"All [Collage]","None",
39561#@gui : "Fuji Ilford Delta 3200 -","Fuji Ilford Delta 3200","Fuji Ilford Delta 3200 +","Fuji Ilford Delta 3200 ++",
39562#@gui : "Fuji Neopan 1600 -","Fuji Neopan 1600","Fuji Neopan 1600 +","Fuji Neopan 1600 ++",
39563#@gui : "Fuji Superia 100 -","Fuji Superia 100","Fuji Superia 100 +","Fuji Superia 100 ++",
39564#@gui : "Fuji Superia 400 -","Fuji Superia 400","Fuji Superia 400 +","Fuji Superia 400 ++",
39565#@gui : "Fuji Superia 800 -","Fuji Superia 800","Fuji Superia 800 +","Fuji Superia 800 ++",
39566#@gui : "Fuji Superia 1600 -","Fuji Superia 1600","Fuji Superia 1600 +","Fuji Superia 1600 ++",
39567#@gui : "Kodak Portra 160 NC -","Kodak Portra 160 NC","Kodak Portra 160 NC +","Kodak Portra 160 NC ++",
39568#@gui : "Kodak Portra 160 VC -","Kodak Portra 160 VC","Kodak Portra 160 VC +","Kodak Portra 160 VC ++",
39569#@gui : "Kodak Portra 400 NC -","Kodak Portra 400 NC","Kodak Portra 400 NC +","Kodak Portra 400 NC ++",
39570#@gui : "Kodak Portra 400 UC -","Kodak Portra 400 UC","Kodak Portra 400 UC +","Kodak Portra 400 UC ++",
39571#@gui : "Kodak Portra 400 VC -","Kodak Portra 400 VC","Kodak Portra 400 VC +","Kodak Portra 400 VC ++"}_0
39572
39573##### Print Films
39574#@gui : Preset = choice{1,"All [Collage]","None",
39575#@gui : "Fuji 3510 (Constlclip)","Fuji 3510 (Constlmap)","Fuji 3510 (Cuspclip)",
39576#@gui : "Fuji 3513 (Constlclip)","Fuji 3513 (Constlmap)","Fuji 3513 (Cuspclip)",
39577#@gui : "Kodak 2383 (Constlclip)","Kodak 2383 (Constlmap)","Kodak 2383 (Cuspclip)",
39578#@gui : "Kodak 2393 (Constlclip)","Kodak 2393 (Constlmap)","Kodak 2393 (Cuspclip)"}_0
39579
39580#### Slide [Color]
39581#@gui : Preset = choice{1,"All [Collage]","None",
39582#@gui : "Agfa Precisa 100","Fuji Astia 100F","Fuji FP 100C","Fuji Provia 100F","Fuji Provia 400F","Fuji Provia 400X",
39583#@gui : "Fuji Sensia 100","Fuji Superia 200 XPRO","Fuji Velvia 50","Generic Fuji Astia 100","Generic Fuji Provia 100",
39584#@gui : "Generic Fuji Velvia 100","Generic Kodachrome 64","Generic Kodak Ektachrome 100 VS",
39585#@gui : "Kodak E-100 GX Ektachrome 100","Kodak Ektachrome 100 VS","Kodak Elite Chrome 200","Kodak Elite Chrome 400",
39586#@gui : "Kodak Elite ExtraColor 100","Kodak Kodachrome 200","Kodak Kodachrome 25","Kodak Kodachrome 64",
39587#@gui : "Lomography X-Pro Slide 200",
39588#@gui : "Polaroid 669","Polaroid 690","Polaroid Polachrome"}_0
39589
39590#@gui : Thumbnail Size = int(512,0,1024)_1
39591#@gui : sep = separator()
39592#@gui : Strength (%) = float(100,0,100)
39593#@gui : Brightness (%) = float(0,-100,100)
39594#@gui : Contrast (%) = float(0,-100,100)
39595#@gui : Gamma (%) = float(0,-100,100)
39596#@gui : Hue (%) = float(0,-100,100)
39597#@gui : Saturation (%) = float(0,-100,100)
39598#@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both")
39599#@gui : sep = separator()
39600#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39601#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39602#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39603#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39604#@gui : sep = separator()
39605#@gui : note = note("<small><b>Note:</b> The color LUTs proposed in this filter come from
39606#@gui : various free sources :</small>")
39607#@gui : note = note{"<small><b>*</b>
39608#@gui : <a href="https://rawpedia.rawtherapee.com/Film_Simulation">RawTherapee Film Simulation</a>.</small>"}
39609#@gui : note = note{"<small><b>*</b>
39610#@gui : <a href="https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html">Pat David Film Emulation</a>.
39611#@gui : </small>"}
39612#@gui : note = note{"<small><b>*</b>
39613#@gui : <a href="http://blog.sowerby.me/fuji-film-simulation-profiles">Fuji Film Simulation Profiles</a>.</small>"}
39614#@gui : note = note{"<small><b>*</b>
39615#@gui : <a href="http://juanmelara.com.au/print-film-emulation-luts-for-download/">Print Film LUTs For Download</a>.
39616#@gui : </small>"}
39617#@gui : sep = separator()
39618#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/02/27</i>.</small>")
39619fx_simulate_film :
39620  category=${arg\ 1+$1,bw,instant_consumer,instant_pro,fujixtransiii,negative_color,negative_new,negative_old,\
39621           print,colorslide}
39622  presets=${-_fx_cluts_$category}
39623  index={arg(1+$1,${2-10})}
39624  thumbsize,strength,brightness,contrast,gamma,hue,saturation,normalize=${11-18}
39625  if $normalize==1" || "$normalize==3 # Pre-normalization
39626    repeat $! l[$>] split_opacity balance_gamma[0] , a c endl done
39627  fi
39628  if $index>=2 # Apply CLUT
39629    path_clut=${-path_cache}
39630    name=${arg\ 1+$index-2,$presets}
39631    clut $name,{0$_is_preview" && "!isfile(['{/${path_clut}clut_$name.cimgz}'])?17:48}
39632    repeat $!-1 if $strength<100 +map_clut[$>] . j[$>] .,0,0,0,0,{$strength%} rm. else map_clut[$>] . fi done
39633    rm.
39634    adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
39635    if $normalize==2" || "$normalize==3 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi # Post-normalization
39636
39637  elif $index==1 # "None"
39638    adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
39639    if $normalize==2" || "$normalize==3 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi # Post-normalization
39640
39641  else # Collage
39642    repeat $! l[$>] if max(w,h)>$thumbsize rr2d $thumbsize,$thumbsize,0,2 fi endl done
39643    N=$! +to "Original",1%,1%,7.5%,2,0.5
39644    Np={narg($presets)} repeat $Np
39645      clut_name=${arg\ 1+$>,$presets}
39646      clut $clut_name mv. $N
39647      repeat $N
39648        if $strength<100 [$>] +map_clut. [$N] j.. .,0,0,0,0,{$strength%} rm. else +map_clut[$>] [$N] fi
39649        adjust_colors. $brightness,$contrast,$gamma,$hue,$saturation,0,255
39650        if $normalize==2" || "$normalize==3 l. split_opacity n[0] 0,255 a c endl fi # Post-normalization
39651        strcapitalize $clut_name clut_name=${}
39652        to. $clut_name,1%,1%,7.5%,2,0.5
39653      done
39654      rm[$N]
39655      progress {$>*100/($Np-1)}
39656    done
39657    k[$N--1] frame 1,1,0,0,0,255 - 128 append_tiles {s=floor(sqrt($!));w>h?[s,0]:[0,s]} + 128
39658  fi
39659
39660fx_simulate_film_preview :
39661  _is_preview=1
39662  index={arg(1+$1,${2-10})}
39663  if !$index gui_warning_preview "Preview disabled in 'Collage' mode"
39664  else gui_split_preview "fx_simulate_film $*",${19-21}
39665  fi
39666  u "{$1}{$2}_"{2*($1==0)}\
39667        "{$3}_"{2*($1==1)}\
39668        "{$4}_"{2*($1==2)}\
39669        "{$5}_"{2*($1==3)}\
39670        "{$6}_"{2*($1==4)}\
39671        "{$7}_"{2*($1==5)}\
39672        "{$8}_"{2*($1==6)}\
39673        "{$9}_"{2*($1==7)}\
39674        "{$10}_"{2*($1==8)}\
39675        "{$11}_"{1+!$index}\
39676        "{$12}{$13}{$14}{$15}{$16}{$17}{$18}{$19}{$20,$21}"
39677
39678_fx_cluts_bw :
39679  u agfa_apx_100,agfa_apx_25,fuji_neopan_1600,fuji_neopan_acros_100,ilford_delta_100,ilford_delta_3200,\
39680    ilford_delta_400,ilford_fp_4_plus_125,\
39681    ilford_hp_5_plus_400,ilford_hps_800,ilford_pan_f_plus_50,ilford_xp_2,kodak_bw_400_cn,kodak_hie_hs_infra,\
39682    kodak_t-max_100,kodak_t-max_3200,\
39683    kodak_t-max_400,kodak_tri-x_400,polaroid_664,polaroid_667,polaroid_672,rollei_ir_400,rollei_ortho_25,\
39684    rollei_retro_100_tonal,rollei_retro_80s
39685
39686_fx_cluts_instant_consumer :
39687  u polaroid_px-100uv+_cold_--,polaroid_px-100uv+_cold_-,polaroid_px-100uv+_cold,polaroid_px-100uv+_cold_+,\
39688    polaroid_px-100uv+_cold_++,polaroid_px-100uv+_cold_+++,\
39689    polaroid_px-100uv+_warm_--,polaroid_px-100uv+_warm_-,polaroid_px-100uv+_warm,polaroid_px-100uv+_warm_+,\
39690    polaroid_px-100uv+_warm_++,polaroid_px-100uv+_warm_+++,\
39691    polaroid_px-680_--,polaroid_px-680_-,polaroid_px-680,polaroid_px-680_+,polaroid_px-680_++,\
39692    polaroid_px-680_cold_--,polaroid_px-680_cold_-,polaroid_px-680_cold,polaroid_px-680_cold_+,\
39693    polaroid_px-680_cold_++,polaroid_px-680_cold_++_alt,\
39694    polaroid_px-680_warm_--,polaroid_px-680_warm_-,polaroid_px-680_warm,polaroid_px-680_warm_+,polaroid_px-680_warm_++,\
39695    polaroid_px-70_--,polaroid_px-70_-,polaroid_px-70,polaroid_px-70_+,polaroid_px-70_++,polaroid_px-70_+++,\
39696    polaroid_px-70_cold_--,polaroid_px-70_cold_-,polaroid_px-70_cold,polaroid_px-70_cold_+,polaroid_px-70_cold_++,\
39697    polaroid_px-70_warm_--,polaroid_px-70_warm_-,polaroid_px-70_warm,polaroid_px-70_warm_+,polaroid_px-70_warm_++,\
39698    polaroid_time_zero_expired_---,polaroid_time_zero_expired_--,polaroid_time_zero_expired_-,\
39699    polaroid_time_zero_expired,polaroid_time_zero_expired_+,polaroid_time_zero_expired_++,\
39700    polaroid_time_zero_expired_cold_---,polaroid_time_zero_expired_cold_--,polaroid_time_zero_expired_cold_-,\
39701    polaroid_time_zero_expired_cold
39702
39703_fx_cluts_instant_pro :
39704  u fuji_fp-100c_--,fuji_fp-100c_-,fuji_fp-100c,fuji_fp-100c_alt,fuji_fp-100c_+,fuji_fp-100c_++,fuji_fp-100c_++_alt,\
39705    fuji_fp-100c_+++,\
39706    fuji_fp-100c_cool_--,fuji_fp-100c_cool_-,fuji_fp-100c_cool,fuji_fp-100c_cool_+,fuji_fp-100c_cool_++,\
39707    fuji_fp-100c_negative_--,fuji_fp-100c_negative_-,fuji_fp-100c_negative,fuji_fp-100c_negative_+,\
39708    fuji_fp-100c_negative_++,fuji_fp-100c_negative_++_alt,fuji_fp-100c_negative_+++,\
39709    fuji_fp-3000b_--,fuji_fp-3000b_-,fuji_fp-3000b,fuji_fp-3000b_+,fuji_fp-3000b_++,fuji_fp-3000b_+++,fuji_fp-3000b_hc,\
39710    fuji_fp-3000b_negative_--,fuji_fp-3000b_negative_-,fuji_fp-3000b_negative,fuji_fp-3000b_negative_+,\
39711    fuji_fp-3000b_negative_++,fuji_fp-3000b_negative_+++,fuji_fp-3000b_negative_early,\
39712    polaroid_665_--,polaroid_665_-,polaroid_665,polaroid_665_+,polaroid_665_++,\
39713    polaroid_665_negative_-,polaroid_665_negative,polaroid_665_negative_+,polaroid_665_negative_hc,\
39714    polaroid_669_--,polaroid_669_-,polaroid_669,polaroid_669_+,polaroid_669_++,polaroid_669_+++,\
39715    polaroid_669_cold_--,polaroid_669_cold_-,polaroid_669_cold,polaroid_669_cold_+,\
39716    polaroid_690_--,polaroid_690_-,polaroid_690,polaroid_690_+,polaroid_690_++,\
39717    polaroid_690_cold_--,polaroid_690_cold_-,polaroid_690_cold,polaroid_690_cold_+,polaroid_690_cold_++,\
39718    polaroid_690_warm_--,polaroid_690_warm_-,polaroid_690_warm,polaroid_690_warm_+,polaroid_690_warm_++
39719
39720_fx_cluts_fujixtransiii :
39721  u fuji_xtrans_iii_acros,fuji_xtrans_iii_acros+g,fuji_xtrans_iii_acros+r,fuji_xtrans_iii_acros+ye,\
39722    fuji_xtrans_iii_astia,\
39723    fuji_xtrans_iii_classic_chrome,fuji_xtrans_iii_mono,fuji_xtrans_iii_mono+g,fuji_xtrans_iii_mono+r,\
39724    fuji_xtrans_iii_mono+ye,\
39725    fuji_xtrans_iii_pro_neg_hi,fuji_xtrans_iii_pro_neg_std,fuji_xtrans_iii_provia,fuji_xtrans_iii_sepia,\
39726    fuji_xtrans_iii_velvia
39727
39728_fx_cluts_negative_color :
39729  u agfa_ultra_color_100,agfa_vista_200,fuji_superia_200,fuji_superia_hg_1600,fuji_superia_reala_100,\
39730    fuji_superia_x-tra_800,kodak_ektar_100,\
39731    kodak_elite_100_xpro,kodak_elite_color_200,kodak_elite_color_400,kodak_portra_160_nc,kodak_portra_160_vc,\
39732    lomography_redscale_100
39733
39734_fx_cluts_negative_new :
39735  u fuji_160c_-,fuji_160c,fuji_160c_+,fuji_160c_++,\
39736    fuji_400h_-,fuji_400h,fuji_400h_+,fuji_400h_++,\
39737    fuji_800z_-,fuji_800z,fuji_800z_+,fuji_800z_++,\
39738    ilford_hp_5_-,ilford_hp_5,ilford_hp_5_+,ilford_hp_5_++,\
39739    kodak_portra_160_-,kodak_portra_160,kodak_portra_160_+,kodak_portra_160_++,\
39740    kodak_portra_400_-,kodak_portra_400,kodak_portra_400_+,kodak_portra_400_++,\
39741    kodak_portra_800_-,kodak_portra_800,kodak_portra_800_+,kodak_portra_800_++,kodak_portra_800_hc,\
39742    kodak_tmax_3200_-,kodak_tmax_3200,kodak_tmax_3200_+,kodak_tmax_3200_++,kodak_tmax_3200_alt,\
39743    kodak_tri-x_400_-,kodak_tri-x_400,kodak_tri-x_400_+,kodak_tri-x_400_++,kodak_tri-x_400_alt
39744
39745_fx_cluts_negative_old :
39746  u ilford_delta_3200_-,ilford_delta_3200,ilford_delta_3200_+,ilford_delta_3200_++,\
39747    fuji_neopan_1600_-,fuji_neopan_1600,fuji_neopan_1600_+,fuji_neopan_1600_++,\
39748    fuji_superia_100_-,fuji_superia_100,fuji_superia_100_+,fuji_superia_100_++,\
39749    fuji_superia_400_-,fuji_superia_400,fuji_superia_400_+,fuji_superia_400_++,\
39750    fuji_superia_800_-,fuji_superia_800,fuji_superia_800_+,fuji_superia_800_++,\
39751    fuji_superia_1600_-,fuji_superia_1600,fuji_superia_1600_+,fuji_superia_1600_++,\
39752    kodak_portra_160_nc_-,kodak_portra_160_nc,kodak_portra_160_nc_+,kodak_portra_160_nc_++,\
39753    kodak_portra_160_vc_-,kodak_portra_160_vc,kodak_portra_160_vc_+,kodak_portra_160_vc_++,\
39754    kodak_portra_400_nc_-,kodak_portra_400_nc,kodak_portra_400_nc_+,kodak_portra_400_nc_++,\
39755    kodak_portra_400_uc_-,kodak_portra_400_uc,kodak_portra_400_uc_+,kodak_portra_400_uc_++,\
39756    kodak_portra_400_vc_-,kodak_portra_400_vc,kodak_portra_400_vc_+,kodak_portra_400_vc_++
39757
39758_fx_cluts_print :
39759  u fuji_3510_constlclip,fuji_3510_constlmap,fuji_3510_cuspclip,\
39760    fuji_3513_constlclip,fuji_3513_constlmap,fuji_3513_cuspclip,\
39761    kodak_2383_constlclip,kodak_2383_constlmap,kodak_2383_cuspclip,\
39762    kodak_2393_constlclip,kodak_2393_constlmap,kodak_2393_cuspclip
39763
39764_fx_cluts_colorslide :
39765  u agfa_precisa_100,fuji_astia_100f,fuji_fp_100c,fuji_provia_100f,fuji_provia_400f,fuji_provia_400x,fuji_sensia_100,\
39766    fuji_superia_200_xpro,fuji_velvia_50,fuji_astia_100_generic,fuji_provia_100_generic,fuji_velvia_100_generic,\
39767    kodak_kodachrome_64_generic,kodak_ektachrome_100_vs_generic,kodak_e-100_gx_ektachrome_100,kodak_ektachrome_100_vs,\
39768    kodak_elite_chrome_200,\
39769    kodak_elite_chrome_400,kodak_elite_extracolor_100,kodak_kodachrome_200,kodak_kodachrome_25,kodak_kodachrome_64,\
39770    lomography_x-pro_slide_200,\
39771    polaroid_669,polaroid_690,polaroid_polachrome
39772
39773#@gui Transfer Colors [Variational] : fx_transfer_rgb, fx_transfer_rgb_preview(1)+ : *
39774#@gui : Regularization = int(8,0,32)
39775#@gui : Preserve Luminance = float(0.2,0,1)
39776#@gui : Precision = _choice(1,"Low","Normal","High","Very High")
39777#@gui : Reference Colors = choice("Bottom Layer","Top Layer")
39778#@gui : Add User-Defined Constraints (Interactive) = _bool(0)
39779#@gui : sep = separator()
39780#@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0
39781#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39782#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39783#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39784#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39785#@gui : sep = separator()
39786#@gui : note = note{"<small><b>Instructions:</b>\n
39787#@gui : - This filter transfers the colors of one layer to all the others.\n
39788#@gui : - Don't forget to set the <i>Input layers...</i> option on the left to manage your input layers.\n
39789#@gui : </small>"}
39790#@gui : sep = separator()
39791#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/04/04</i>.</small>")
39792fx_transfer_rgb :
39793  to_rgb
39794  ref={$4?0:-1}
39795  transfer_rgb[^$ref] [$ref],0.25,$1,$2,{2^(4+$3)},$5,0
39796  c 0,255
39797
39798fx_transfer_rgb_preview :
39799  if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi
39800  ref={$4?0:-1}
39801  +store[$ref] _fx_trgb_ref
39802  gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_rgb $1,$2,0,0,0 rm.",${-3--1}
39803  _fx_trgb_ref=
39804
39805  mv[$ref] $!
39806  repeat $!-1 l[$>,-1]
39807    rr2d[0] $_preview_width,$_preview_height,0,3 rr2d. {0,[w,h]/3},0,3
39808    to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0
39809    j[0] .,$6%,$7%
39810    rm.
39811  endl done
39812
39813#@gui Transfer Colors [Histogram] : fx_transfer_histogram, fx_transfer_histogram_preview(1)+ : *
39814#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
39815#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
39816#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
39817#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
39818#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
39819#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
39820#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
39821#@gui : Reference Colors = choice("Bottom Layer","Top Layer")
39822#@gui : sep = separator()
39823#@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0
39824#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39825#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39826#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39827#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39828#@gui : sep = separator()
39829#@gui : note = note{"<small><b>Note: </b>
39830#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
39831#@gui : multiple input layers.
39832#@gui : </small>"}
39833#@gui : sep = separator()
39834#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/01/13</i>.</small>")
39835fx_transfer_histogram :
39836  to_rgb
39837  ref={$2?0:-1}
39838  transfer_histogram[^$ref] [$ref],256,$1
39839  c 0,255
39840
39841fx_transfer_histogram_preview :
39842  if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi
39843  ref={$2?0:-1}
39844  +store[$ref] _fx_trgb_ref
39845  gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_histogram $1,0 rm.",${-3--1}
39846  _fx_trgb_ref=
39847
39848  mv[$ref] $!
39849  repeat $!-1 l[$>,-1]
39850    rr2d[0] $_preview_width,$_preview_height,0,3 rr2d. {0,[w,h]/3},0,3
39851    to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0
39852    j[0] .,$3%,$4%
39853    rm.
39854  endl done
39855
39856#@gui Transfer Colors [PCA] : fx_transfer_pca, fx_transfer_pca_preview(1)+ : *
39857#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
39858#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
39859#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
39860#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
39861#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
39862#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
39863#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
39864#@gui : Reference Colors = choice("Bottom Layer","Top Layer")
39865#@gui : sep = separator()
39866#@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0
39867#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39868#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39869#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39870#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39871#@gui : sep = separator()
39872#@gui : note = note{"<small><b>Note: </b>
39873#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
39874#@gui : multiple input layers.
39875#@gui : </small>"}
39876#@gui : sep = separator()
39877#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/01/13</i>.</small>")
39878fx_transfer_pca :
39879  to_rgb
39880  ref={$2?0:-1}
39881  transfer_pca[^$ref] [$ref],$1
39882  c 0,255
39883
39884fx_transfer_pca_preview :
39885  if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi
39886  ref={$2?0:-1}
39887  +store[$ref] _fx_trgb_ref
39888  gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_pca $1,0 rm.",${-3--1}
39889  _fx_trgb_ref=
39890
39891  mv[$ref] $!
39892  repeat $!-1 l[$>,-1]
39893    rr2d[0] $_preview_width,$_preview_height,0,3 rr2d. {0,[w,h]/3},0,3
39894    to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0
39895    j[0] .,$3%,$4%
39896    rm.
39897  endl done
39898
39899#@gui Tune HSV Colors : fx_tune_hsv, fx_tune_hsv_preview(1)
39900#@gui : Dark = choice(2,"Ignore","Lock","Remap")
39901#@gui : Dark Color = color(0,0,0)
39902#@gui : Target Hue (%) = float(100,0,100)
39903#@gui : Target Saturation (%) = float(100,0,100)
39904#@gui : Target Value (%) = float(100,0,100)
39905#@gui : sep = separator()
39906#@gui : Light = choice(2,"Ignore","Lock","Remap")
39907#@gui : Light Color = color(255,255,255)
39908#@gui : Target Hue (%) = float(100,0,100)
39909#@gui : Target Saturation (%) = float(100,0,100)
39910#@gui : Target Value (%) = float(100,0,100)
39911#@gui : sep = separator()
39912#@gui : Average = choice(1,"Ignore","Lock","Remap")
39913#@gui : Average Color = color(128,128,128)_0
39914#@gui : Target Hue (%) = float(0,0,100)_0
39915#@gui : Target Saturation (%) = float(50,0,100)_0
39916#@gui : Target Value (%) = float(100,0,100)_0
39917#@gui : sep = separator()
39918#@gui : Red = choice("Ignore","Lock","Remap")
39919#@gui : Red Color = color(255,0,0)_0
39920#@gui : Target Hue (%) = float(100,0,100)_0
39921#@gui : Target Saturation (%) = float(12.5,0,100)_0
39922#@gui : Target Value (%) = float(0,0,100)_0
39923#@gui : sep = separator()
39924#@gui : Yellow = choice("Ignore","Lock","Remap")
39925#@gui : Yellow Color = color(255,255,0)_0
39926#@gui : Target Hue (%) = float(100,0,100)_0
39927#@gui : Target Saturation (%) = float(12.5,0,100)_0
39928#@gui : Target Value (%) = float(0,0,100)_0
39929#@gui : sep = separator()
39930#@gui : Green = choice("Ignore","Lock","Remap")
39931#@gui : Green Color = color(0,255,0)_0
39932#@gui : Target Hue (%) = float(100,0,100)_0
39933#@gui : Target Saturation (%) = float(12.5,0,100)_0
39934#@gui : Target Value (%) = float(0,0,100)_0
39935#@gui : sep = separator()
39936#@gui : Cyan = choice("Ignore","Lock","Remap")
39937#@gui : Cyan Color = color(0,255,255)_0
39938#@gui : Target Hue (%) = float(100,0,100)_0
39939#@gui : Target Saturation (%) = float(12.5,0,100)_0
39940#@gui : Target Value (%) = float(0,0,100)_0
39941#@gui : sep = separator()
39942#@gui : Blue = choice("Ignore","Lock","Remap")
39943#@gui : Blue Color = color(0,0,255)_0
39944#@gui : Target Hue (%) = float(100,0,100)_0
39945#@gui : Target Saturation (%) = float(12.5,0,100)_0
39946#@gui : Target Value (%) = float(0,0,100)_0
39947#@gui : sep = separator()
39948#@gui : Magenta = choice("Ignore","Lock","Remap")
39949#@gui : Magenta Color = color(255,0,255)_0
39950#@gui : Target Hue (%) = float(100,0,100)_0
39951#@gui : Target Saturation (%) = float(12.5,0,100)_0
39952#@gui : Target Value (%) = float(0,0,100)_0
39953#@gui : sep = separator()
39954#@gui : Preview color mapping = choice(2,"None","Center","Top Left","Top Right","Bottom Left","Bottom Right")
39955#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
39956#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
39957#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
39958#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
39959#@gui : sep = separator()
39960#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/12/17</i>.</small>")
39961_fx_tune_hsv :
39962  mode_dark,Rdark,Gdark,Bdark,Hdark,Sdark,Vdark,\
39963  mode_light,Rlight,Glight,Blight,Hlight,Slight,Vlight,\
39964  mode_avg,Ravg,Gavg,Bavg,Havg,Savg,Vavg,\
39965  mode_red,Rred,Gred,Bred,Hred,Sred,Vred,\
39966  mode_yellow,Ryellow,Gyellow,Byellow,Hyellow,Syellow,Vyellow,\
39967  mode_green,Rgreen,Ggreen,Bgreen,Hgreen,Sgreen,Vgreen,\
39968  mode_cyan,Rcyan,Gcyan,Bcyan,Hcyan,Scyan,Vcyan,\
39969  mode_blue,Rblue,Gblue,Bblue,Hblue,Sblue,Vblue,\
39970  mode_magenta,Rmagenta,Gmagenta,Bmagenta,Hmagenta,Smagenta,Vmagenta,\
39971  preview_mapping=${1-64}
39972  all_colors=dark,light,avg,red,yellow,green,cyan,blue,magenta
39973
39974  repeat $! l[$>] split_opacity l[0]
39975    to_rgb
39976
39977    # Define list of considered colors.
39978    colors,sep=
39979    repeat narg($all_colors) color=${arg\ 1+$>,$all_colors} if ${mode_$color} colors.=$sep$color sep=, fi done
39980
39981    # Find darkest, lightest and average colors.
39982    +srgb2lab channels. 0 darklight={"[I(#-2,xm,ym),I(#-2,xM,yM)]"} rm.
39983    dark={[$darklight][0,3]} light={[$darklight][3,3]}
39984    +r. 1,1,1,3,2 avg={^} rm.
39985
39986    # Find the nearest existing colors to "pure" colors.
39987    eval. ">
39988      begin(
39989        red = yellow = green = cyan = blue = magenta = [0,0,0];
39990        best_red = best_yellow = best_green = best_cyan = best_blue = best_magenta = inf;
39991        test_color(color,tR,tG,tB) = (
39992          val = norm([tR,tG,tB]-[R,G,B]);
39993          val<best_#color?(best_#color = val; color# = I(#-2));
39994        );
39995      );
39996      test_color(red,255,0,0);
39997      test_color(yellow,255,255,0);
39998      test_color(green,0,255,0);
39999      test_color(cyan,0,255,255);
40000      test_color(blue,0,0,255);
40001      test_color(magenta,255,0,255);
40002      end(
40003        run(' red=',vtos(red),
40004            ' yellow=',vtos(yellow),
40005            ' green=',vtos(green),
40006            ' cyan=',vtos(cyan),
40007            ' blue=',vtos(blue),
40008            ' magenta=',vtos(magenta))
40009      ); I"
40010
40011    # Compute color mapping.
40012    if narg($colors)
40013      l[]
40014
40015        # Define color correspondences.
40016        repeat narg($colors)
40017          color=${arg\ 1+$>,$colors}
40018          if ${mode_$color}==2 (${$color},${R$color},${G$color},${B$color},${H$color},${S$color},${V$color})
40019          else (${$color},${$color},0,0,0)
40020          fi
40021        done
40022        a y permute yzcx
40023        s c,-3 srgb2rgb[0,1] rgb2hsv[0,1] /. 100
40024        f.. "
40025          Hs = i(#0,x,0,0,0); Ss = i(#0,x,0,0,1); Vs = i(#0,x,0,0,2);
40026          Hd = i0; Sd = i1; Vd = i2;
40027          DeltaH = Hd - Hs;
40028          alphaH = i(#2,x,0,0,0);
40029          H = abs(DeltaH)<abs(DeltaH - 360)?
40030            lerp(Hs,Hd,alphaH):
40031            lerp(Hs,Hd - 360,alphaH)%360;
40032          S = lerp(Ss,Sd,i(#2,x,0,0,1));
40033          V = lerp(Vs,Vd,i(#2,x,0,0,2));
40034          [ H,S,V ]"
40035        rm.
40036        hsv2rgb rgb2srgb
40037        repeat w color=${arg\ 1+$>,$colors} t_$color={I[$>]} done
40038        -. .. a c
40039
40040        # Create CLUT.
40041        if $_is_preview 33,33,33,4 else 63,63,63,4 fi
40042        f.. "I(#-1,round([i0,i1,i2]*(w#-1-1)/255))+=[i3,i4,i5,1];"
40043        s. c,-3 +max. 1 /[-3,-1] eq. 0
40044        inpaint_pde.. . rm[-3,-1]
40045        2,2,2,3,"[x,y,z]*255" ri. ..,3 +[-2,-1]
40046        nm clut
40047      endl
40048
40049      # Apply CLUT.
40050      if $clut map_clut.. . rm. c 0,255 fi
40051    fi
40052
40053    if $preview_mapping # Generate view of color mapping
40054      __px,__py,__ps={s=min(w,h);p=$preview_mapping;p==1?[0.5,0.5,80]:\
40055                                                    p==2?[0,0,48]:\
40056                                                    p==3?[1,0,48]:\
40057                                                    p==4?[0,1,64]:[1,1,64]}
40058      l[]
40059        repeat narg($all_colors) color=${arg\ 1+$>,$all_colors} (${$color}) done a y permute. yzcx
40060        s x r {s=round(max($__ps,16));[s,s]}
40061        repeat narg($all_colors) l[$>]
40062          color=${arg\ 1+$>,$all_colors}
40063          mode=${mode_$color}
40064          if $mode==2
40065            polygon 3,0,100%,100%,100%,100%,0,1,${t_$color}
40066            line 0,100%,100%,0,0.5,0xAAAAAAAA,255 line 0,100%,100%,0,0.5,0x55555555,0
40067          fi
40068          to {`uppercase(['$color'][0])`},0.5~,0.5~,50%,2,0.5
40069          to_rgba
40070          if $mode frame 1,1,0,0,0,255 frame 2,2,255 else frame 3,3,0,0,0,64 fi
40071        endl done
40072        append_tiles 3,3
40073        store __preview_mapping
40074      endl
40075    fi
40076
40077  endl a c endl done
40078
40079fx_tune_hsv :
40080  _fx_tune_hsv ${1-63},0
40081
40082fx_tune_hsv_preview :
40083  preview_mapping=$64
40084  repeat $! l[$>]
40085    gui_split_preview "_is_preview=1 _fx_tune_hsv $*",${-3--1}
40086    if $preview_mapping
40087      $__preview_mapping
40088      if narg($_preview_width) rr2d[0] $_preview_width,$_preview_height,0,2 fi
40089      s. c,-3 to_colormode.. {-3,s}
40090      j... ..,$__px~,$__py~,0,0,1,.,255 rm[-2,-1]
40091    fi
40092  endl done
40093
40094  d,l,a,r,y,g,c,b,m={[$1==2?2:0,$8==2?2:0,$15==2?2:0,$22==2?2:0,$29==2?2:0,$36==2?2:0,$43==2?2:0,$50==2?2:0,$57==2?2:0]}
40095  u "{$1}{$2,$3,$4}_"$d"{$5}_"$d"{$6}_"$d"{$7}_"$d\
40096    "{$8}{$9,$10,$11}_"$l"{$12}_"$l"{$13}_"$l"{$14}_"$l\
40097    "{$15}{$16,$17,$18}_"$a"{$19}_"$a"{$20}_"$a"{$21}_"$a\
40098    "{$22}{$23,$24,$25}_"$r"{$26}_"$r"{$27}_"$r"{$28}_"$r\
40099    "{$29}{$30,$31,$32}_"$y"{$33}_"$y"{$34}_"$y"{$35}_"$y\
40100    "{$36}{$37,$38,$39}_"$g"{$40}_"$g"{$41}_"$g"{$42}_"$g\
40101    "{$43}{$44,$45,$46}_"$c"{$47}_"$c"{$48}_"$c"{$49}_"$c\
40102    "{$50}{$51,$52,$53}_"$b"{$54}_"$b"{$55}_"$b"{$56}_"$b\
40103    "{$57}{$58,$59,$60}_"$m"{$61}_"$m"{$62}_"$m"{$63}_"$m\
40104    "{$64}{$65}{$66,$67}"
40105
40106#@gui User-Defined : fx_custom_transform, fx_custom_transform
40107#@gui : Red - Green - Blue - Alpha = text{"i"}
40108#@gui : Red - Green - Blue = text{"i + 90*(x/w)*cos(i/10)"}
40109#@gui : Red = text{"i"}
40110#@gui : Green = text{"i"}
40111#@gui : Blue = text{"i"}
40112#@gui : Alpha = text{"i"}
40113#@gui : Value Normalization = choice("None","RGB","RGBA")
40114#@gui : sep = separator()
40115#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40116fx_custom_transform :
40117  to_rgba repeat $!
40118    f. "$1"
40119    s. c a[-4--2] c f.. "$2"
40120    s.. c f[-4] "$3" f... "$4" f.. "$5" f. "$6"
40121    if $7==0 a[-4--1] c c. 0,255
40122    elif $7==1 a[-4--2] c n.. 0,255 c. 0,255 a[-2,-1] c
40123    else a[-4--1] c n. 0,255
40124    fi
40125  mv. 0 done
40126
40127#@gui ____<b>Contours</b>
40128#------------------------
40129
40130#@gui Convolve : fx_convolve, fx_convolve_preview(0)
40131#@gui : Kernel = choice("Custom","Average 3x3","Average 5x5","Average 7x7","Average 9x9","Prewitt-X","Prewitt-Y",
40132#@gui : "Sobel-X","Sobel-Y","Rotinv-X","Rotinv-Y","Laplacian","Robert Cross 1","Robert Cross 2","Impulses 5x5",
40133#@gui : "Impulses 7x7","Impulses 9x9")
40134#@gui : Boundary = choice(1,"Dirichlet","Neumann")
40135#@gui : sep = separator()
40136#@gui : note = note("<small><b>Note:</b> If parameter <i>Kernel</i> is set to <i>Custom</i>, it uses the custom
40137#@gui : convolution kernel defined below. Use commas and semicolons as separators for res. matrix columns and rows.
40138#@gui : </small>")
40139#@gui : Custom Kernel = text("0,1,0;1,-4,1;0,1,0")
40140#@gui : sep = separator()
40141#@gui : note = note("<small><b>Note:</b> Kernel multiplier is useful only when parameter <i>Value range</i> is set
40142#@gui : to <i>Cut</i>.</small>")
40143#@gui : Value Range = choice(1,"Cut","Normalize")
40144#@gui : Kernel Multiplier = float(1,0,50)
40145#@gui : sep = separator()
40146#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
40147#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
40148#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
40149#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
40150#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
40151#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
40152#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
40153#@gui : sep = separator()
40154#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40155#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40156#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40157#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40158#@gui : sep = separator()
40159#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/06/06</i>.</small>")
40160fx_convolve : skip "${3=1}"
40161  ac "_fx_convolve $1,$2,\"$3\",${4--5}",$-4
40162
40163_fx_convolve :
40164  if $1 _fx_convolve$1[] else ($3) fi
40165  if !$4 *. $5 fi
40166  convolve[0--2] .,$2
40167  if $4 n 0,255 else c 0,255 fi
40168  rm.
40169
40170_fx_convolve1 : 3,3 f 1 normalize_sum  # Average 3x3
40171_fx_convolve2 : 5,5 f 1 normalize_sum  # Average 5x5
40172_fx_convolve3 : 7,7 f 1 normalize_sum  # Average 7x7
40173_fx_convolve4 : 9,9 f 1 normalize_sum  # Average 9x9
40174_fx_convolve5 : (1,0,-1;1,0,-1;1,0,-1) # Prewitt-X
40175_fx_convolve6 : (1,1,1;0,0,0;-1,-1,-1) # Prewitt-Y
40176_fx_convolve7 : (1,0,-1;2,0,-2;1,0,-1) # Sobel-X
40177_fx_convolve8 : (1,2,1;0,0,0;-1,-2,-1) # Sobel-Y
40178_fx_convolve9 : a={0.25*(2-sqrt(2))} b={0.5*(sqrt(2)-1)} ($a,0,-$a;$b,0,-$b;$a,0,-$a)  # Rotinv-X
40179_fx_convolve10 : a={0.25*(2-sqrt(2))} b={0.5*(sqrt(2)-1)} ($a,$b,$a;0,0,0;-$a,-$b,-$a) # Rotinv-Y
40180_fx_convolve11 : (0,1,0;1,-4,1;0,1,0)   # Laplacian
40181_fx_convolve12 : (1,0;0,-1)             # Robert Cross1
40182_fx_convolve13 : (0,1;-1,0)             # Robert Cross2
40183_fx_convolve14 : 3,3 f 1 r 7,7,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 5x5
40184_fx_convolve15 : 3,3 f 1 r 9,9,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 7x7
40185_fx_convolve16 : 3,3 f 1 r 11,11,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 9x9
40186
40187fx_convolve_preview : skip "${3=1}"
40188  gui_split_preview "fx_convolve $1,$2,\"$3\",${4--1}",${-3--1}
40189
40190#@gui Curvature : fx_curvature, fx_curvature_preview(0)
40191#@gui : Smoothness = float(2,0,10)
40192#@gui : Min Threshold = float(0,0,100)
40193#@gui : Max Threshold = float(100,0,100)
40194#@gui : Absolute Value = bool(0)
40195#@gui : Negative Colors = bool(0)
40196#@gui : sep = separator()
40197#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40198#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40199#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40200#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40201#@gui : sep = separator()
40202#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40203fx_curvature :
40204  repeat $! l[$>] split_opacity l[0]
40205    b $1 iee
40206    if $4 abs fi
40207    c $2%,$3%
40208    if $5 negate fi
40209    n 0,255
40210  endl a c endl done
40211
40212fx_curvature_preview :
40213  gui_split_preview "fx_curvature ${^0}",${-3--1}
40214
40215#@gui Difference of Gaussians : fx_dog, fx_dog_preview(1)
40216#@gui : 1st Variance = float(1.4,0,5)
40217#@gui : 2nd Variance = float(1.5,0,5)
40218#@gui : Threshold = float(0,0,49)
40219#@gui : Negative Colors = bool(0)
40220#@gui : Monochrome = bool(1)
40221#@gui : sep = separator()
40222#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40223#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40224#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40225#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40226#@gui : sep = separator()
40227#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40228fx_dog :
40229  dog $1%,$2%
40230  if $5 norm fi
40231  c $3%,{100-$3}%
40232  if $4 negate fi
40233  n 0,255
40234
40235fx_dog_preview :
40236  gui_split_preview "fx_dog ${^0}",${-3--1}
40237
40238#@gui Distance Transform : fx_distance, fx_distance_preview(0)
40239#@gui : Value = int(128,0,255)
40240#@gui : Metric = choice(2,"Chebyshev","Manhattan","Euclidean","Squared-Euclidean")
40241#@gui : Normalization = choice(2,"Cut","Normalize","Modulo")
40242#@gui : Modulo Value = int(32,1,255)
40243#@gui : sep = separator()
40244#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40245#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40246#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40247#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40248#@gui : sep = separator()
40249#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/07/04</i>.</small>")
40250fx_distance :
40251  repeat $! l[$>] split_opacity l[0]
40252    distance $1,$2
40253    if $3==0 c 0,255
40254    elif $3==1 n 0,255
40255    else % $4 n 0,255
40256    fi
40257  endl a c endl done
40258
40259fx_distance_preview :
40260  gui_split_preview "fx_distance ${^0}",${-3--1}
40261
40262#@gui Edges : fx_edges, fx_edges_preview(0)
40263#@gui : Smoothness = float(0,0,10)
40264#@gui : Threshold = float(15,0,50)
40265#@gui : Negative Colors = bool(0)
40266#@gui : sep = separator()
40267#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40268#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40269#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40270#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40271#@gui : sep = separator()
40272#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40273fx_edges :
40274  to_rgb b $1% edges $2%
40275  if $3 negate fi
40276  n 0,255
40277
40278fx_edges_preview :
40279  gui_split_preview "fx_edges ${^0}",${-3--1}
40280
40281#@gui Edges Offsets : fx_edge_offsets, fx_edge_offsets_preview(0)
40282#@gui : Smoothness = float(0,0,10)
40283#@gui : Threshold = float(15,0,50)
40284#@gui : Scale = int(4,0,32)
40285#@gui : Thickness = int(1,0,16)
40286#@gui : Negative Colors = bool(0)
40287#@gui : sep = separator()
40288#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40289#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40290#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40291#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40292#@gui : sep = separator()
40293#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40294fx_edge_offsets :
40295  repeat $!
40296    os={s}
40297    b. $1% gradient_norm. >=. $2% skeleton. 0 distance. 1 round. 1 %. $3 >=. {max(1,$3-$4)}
40298    if !$5 negate. fi
40299    n. 0,255 to_colormode. $os
40300  mv. 0 done
40301
40302fx_edge_offsets_preview :
40303  gui_split_preview "fx_edge_offsets ${^0}",${-3--1}
40304
40305#@gui Extract Foreground [Interactive] : fx_extract_foreground, gui_no_preview
40306#@gui : Feathering = _float(0,0,4)
40307#@gui : Dilation = int(0,-32,32)
40308#@gui : Output Mode = choice{3,"RGBA Image (Full-Transparency / 1 Layer)","RGBA Image (Updatable / 1 Layer)",
40309#@gui : "RGB Image + Binary Mask (2 Layers)","RGBA Foreground + Background (2 Layers)"}
40310#@gui : View Resolution = _choice{1,"Small (Faster)","Medium","High (Slower)","Very High (Even Slower)"}
40311#@gui : sep = separator()
40312#@gui : note = note{"<small><b>Description:</b>\n
40313#@gui : This filter allows to quickly extract foreground objects from background in opaque RGB images.
40314#@gui : Click on the <i>Apply</i> or <i>OK</i> buttons below to open the interactive window and start adding
40315#@gui : foreground and background control points. When you're done, exit the interactive window: your extracted
40316#@gui : foreground will be transferred back to the host software.\n\n
40317#@gui : If you are not satisfied with the result, click on <i>Apply</i> once again to modify your control points
40318#@gui : defined previously. To remove all control points, click on the <i>Reset</i> button above.
40319#@gui : </small>"}
40320#@gui : Last Image Size = value(0,0)
40321#@gui : Control Points = value(-1)
40322#@gui : sep = separator()
40323#@gui : note = note{"<small><b>Interactions:</b>\n
40324#@gui : Use the following actions in the interactive window to build your extraction mask :\n\n
40325#@gui : - <b>Left mouse button</b> or key <b>F</b> create a new <b>foreground</b> control point
40326#@gui : (or move an existing one).\n
40327#@gui : - <b>Right mouse button</b> or key <b>B</b> create a new <b>background</b> control point
40328#@gui : (or move an existing one).\n
40329#@gui : - <b>Mouse wheel</b>, or keys <b>CTRL+arrows UP/DOWN</b> zoom view in/out.\n
40330#@gui : - Key <b>SPACE</b> updates the extraction mask.\n
40331#@gui : - Key <b>TAB</b> toggles background view modes.\n
40332#@gui : - Key <b>M</b> toggles marker view modes.\n
40333#@gui : - Key <b>BACKSPACE</b> deletes the last control point added.\n
40334#@gui : - Key <b>PAGE UP</b> increases background opacity.\n
40335#@gui : - Key <b>PAGE DOWN</b> decreases background opacity.\n
40336#@gui : - Keys <b>CTRL+D</b> increase window size.\n
40337#@gui : - Keys <b>CTRL+C</b> decrease window size.\n
40338#@gui : - Keys <b>CTRL+R</b> reset window size.\n
40339#@gui : - Keys <b>ESC</b>, <b>Q</b> or <b>ENTER</b> exit the interactive window.
40340#@gui : </small>"}
40341#@gui : sep = separator()
40342#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/29/09</i>.</small>")
40343fx_extract_foreground :
40344  if !$! return fi
40345  resolution={arg(1+$3,512,1024,2048,0)}
40346  repeat $! l[$<]
40347    nm=${-gui_layer_name}
40348    nm "[G"{`39`}"MIC] Interactive Foreground Extraction"
40349    if [$6][0]==-1" || "[$5]!=[w,h] _gui_control_points= else _gui_control_points=$6 fi
40350    status=${x_segment\ $resolution}
40351    sh 3 b. $1% if $2>0 dilate. {1+2*$2} elif $2<0 erode. {1-2*$2} fi
40352    rm.
40353    if $3==1 sh 3 max. 1 rm.
40354    elif $3==2 s c,-3 r. 100%,100%,1,4 rv nm[0] name(Mask) nm[1] name($nm)
40355    elif $3==3
40356      .
40357      sh.. 0,2 +channels... 3,3 >=. 3 *[-2,-1] rm.
40358      sh. 0,2 +channels.. 3,3 <=. {255-3} *[-2,-1] rm.
40359      sh. 3 *. -1 +. 255 rm.
40360      gui_autocrop_layers[0]
40361      pos0=${gui_layer_pos[0]} pos1=${gui_layer_pos[1]}
40362      nm[0] name($nm" [foreground]"),pos($pos0)
40363      nm[1] name($nm" [background]"),pos($pos1)
40364    fi
40365  endl done
40366  if narg($status)>=4 u \{$1\}\{$2\}\{$3\}\{$4\}\{{w},{h}\}\{$status\} else u "" fi
40367
40368#@gui Gradient Norm : fx_gradient_norm, fx_gradient_norm_preview(0)
40369#@gui : Smoothness = float(0,0,10)
40370#@gui : Linearity = float(0.5,0,1.5)
40371#@gui : Min Threshold = float(0,0,100)
40372#@gui : Max Threshold = float(100,0,100)
40373#@gui : Negative Colors = bool(0)
40374#@gui : sep = separator()
40375#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40376#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40377#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40378#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40379#@gui : sep = separator()
40380#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40381fx_gradient_norm :
40382  b $1 gradient_norm ^ $2
40383  c $3%,$4%
40384  if $5 negate fi
40385  n 0,255
40386
40387fx_gradient_norm_preview :
40388  gui_split_preview "fx_gradient_norm ${^0}",${-3--1}
40389
40390#@gui Gradient RGB : fx_gradient2rgb, fx_gradient2rgb_preview(0)
40391#@gui : Smoothness = float(0,0,10)
40392#@gui : Min Threshold = float(0,0,100)
40393#@gui : Max Threshold = float(100,0,100)
40394#@gui : Orientation Only = bool(0)
40395#@gui : Negative Colors = bool(0)
40396#@gui : sep = separator()
40397#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40398#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40399#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40400#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40401#@gui : sep = separator()
40402#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40403fx_gradient2rgb :
40404  b $1 gradient2rgb $4
40405  c $2%,$3%
40406  if $5 negate fi
40407  n 0,255
40408
40409fx_gradient2rgb_preview :
40410  gui_split_preview "fx_gradient2rgb ${^0}",${-3--1}
40411
40412#@gui Isophotes : fx_isophotes, fx_isophotes_preview(0)
40413#@gui : Levels = int(8,1,256)
40414#@gui : Smoothness = float(0,0,5)
40415#@gui : Filling = choice(1,"Transparent","Colors")
40416#@gui : sep = separator()
40417#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40418#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40419#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40420#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40421#@gui : sep = separator()
40422#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40423fx_isophotes :
40424  if $3
40425    topographic_map $1,$2
40426  else
40427    b $2 isophotes $1
40428  fi
40429
40430fx_isophotes_preview :
40431  gui_split_preview "fx_isophotes ${^0}",${-3--1}
40432
40433#@gui Laplacian : fx_laplacian, fx_laplacian_preview(0)
40434#@gui : Smoothness = float(0,0,10)
40435#@gui : Min Threshold = float(0,0,100)
40436#@gui : Max Threshold = float(100,0,100)
40437#@gui : Absolute Value = bool(0)
40438#@gui : Negative Colors = bool(0)
40439#@gui : sep = separator()
40440#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40441#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40442#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40443#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40444#@gui : sep = separator()
40445#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40446fx_laplacian :
40447  b $1 laplacian
40448  if $4 abs fi
40449  c $2%,$3%
40450  if $5 negate fi
40451  n 0,255
40452
40453fx_laplacian_preview :
40454  gui_split_preview "fx_laplacian ${^0}",${-3--1}
40455
40456#@gui Local Orientation : fx_local_orientation, fx_local_orientation_preview(1)
40457#@gui : Smoothness = float(0,0,5)
40458#@gui : Min Threshold = float(0,0,100)
40459#@gui : Max Threshold = float(100,0,100)
40460#@gui : Negative Colors = bool(0)
40461#@gui : sep = separator()
40462#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
40463#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
40464#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
40465#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
40466#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
40467#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
40468#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
40469#@gui : sep = separator()
40470#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40471#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40472#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40473#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40474#@gui : sep = separator()
40475#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40476_fx_local_orientation :
40477  repeat $! l[$>] split_opacity l[0]
40478    b $1% gradient_orientation 2 complex2polar rm[0--1:2]
40479    c $2%,$3%
40480    if $4 negate fi
40481    n 0,255
40482  endl a c endl done
40483
40484fx_local_orientation :
40485  ac "_fx_local_orientation $1,$2,$3,$4",$5,2
40486
40487fx_local_orientation_preview :
40488  gui_split_preview "fx_local_orientation ${^0}",${-3--1}
40489
40490#@gui Morphological Filter : fx_morphological, fx_morphological_preview(0)
40491#@gui : Action = choice{"Erosion","Dilation","Opening","Closing","Original - Erosion","Dilation - Original",
40492#@gui : "Original - Opening","Closing - Original","Original - (Opening + Closing)/2","Closing - Opening"}
40493#@gui : Kernel = choice(0,"Square","Octagonal","Circular","Custom")
40494#@gui : Size = int(5,2,60)
40495#@gui : note = note("<small>Parameter <i>Size</i> is inactive for <i>Custom</i> kernel.</small>")
40496#@gui : Custom Kernel = text("1,0,1; 0,1,0; 1,0,1")
40497#@gui : Negative = bool()
40498#@gui : Process Transparency = bool(0)
40499#@gui : sep = separator()
40500#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
40501#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
40502#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
40503#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
40504#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
40505#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
40506#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
40507#@gui : Value Action = choice("None","Cut","Stretch")
40508#@gui : sep = separator()
40509#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40510#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40511#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40512#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40513#@gui : sep = separator()
40514#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/22/06</i>.</small>")
40515fx_morphological :
40516  ac "_fx_morphological ${1-3},\"$4\",${5-6}",$7,$8
40517
40518fx_morphological_preview :
40519  gui_split_preview "fx_morphological ${1-3},\"$4\",${5--1}",${-3--1}
40520  if $2==3
40521    ('"$4"') f. "(i>=_'0' && i<=_'9') || i==_',' || i==_';'?i:-1"
40522    discard. -1 ({t}) rr2d. {0,max(24,w/6)},{0,max(24,h/6)},0,1 >. 0 *. 255
40523    to_rgba. frame. 1,1,0,0,0,0,255 frame. 1,1,255 frame. 1,1,0,0,0,0,255
40524    j[^-1] .,2,2,0,0,0.75 rm.
40525  else
40526  fi
40527
40528_fx_morphological :
40529  ('"$4"') f. "(i>=_'0' && i<=_'9') || i==_',' || i==_';'?i:-1" discard. -1 ckernel={t} rm.
40530  if $2==0 m "my_erode: erode $""1" m "my_dilate: dilate $""1"
40531  elif $2==1 m "my_erode: erode_oct $""1" m "my_dilate: dilate_oct $""1"
40532  elif $2==2 m "my_erode: erode_circ $""1" m "my_dilate: dilate_circ $""1"
40533  else
40534    m "my_erode : ("$ckernel") erode[^-1] . rm."
40535    m "my_dilate : ("$ckernel") dilate[^-1] . rm."
40536  fi
40537  # Erosion
40538  if $1==0 m "my_action : my_erode $3"
40539  # Dilation
40540  elif $1==1 m "my_action : my_dilate $3"
40541  # Opening
40542  elif $1==2 m "my_action : my_erode $3 my_dilate $3"
40543  # Closing
40544  elif $1==3 m "my_action : my_dilate $3 my_erode $3"
40545  # Original - Erosion
40546  elif $1==4 m "my_action : +my_erode $3 -"
40547  # Dilation - Original
40548  elif $1==5 m "my_action : +my_dilate $3 rv -"
40549  # Original - Opening
40550  elif $1==6 m "my_action : +my_erode $3 my_dilate. $3 -"
40551  # Closing - Original
40552  elif $1==7 m "my_action : +my_dilate $3 my_erode. $3 rv -"
40553  # Original - (Opening + Closing)/2
40554  elif $1==8 m "my_action : +my_erode $3 my_dilate. $3 +my_dilate.. $3 my_erode. $3 +[-2,-1] /. 2 -"
40555  # Closing - Opening
40556  else m "my_action : +my_erode $3 my_dilate. $3 my_dilate.. $3 my_erode.. $3 -"
40557  fi
40558  repeat $! l[$>]
40559    if !$6 split_opacity fi
40560    my_action[0]
40561    a c
40562  endl done
40563  if $5 repeat $! l[$>] split_opacity negate[0] a c endl done fi
40564  um my_erode,my_dilate,my_action
40565
40566#@gui Segmentation : fx_segment_watershed, fx_segment_watershed_preview(0)
40567#@gui : Edge Threshold = float(2,0,15)
40568#@gui : Smoothness = float(1,0,5)
40569#@gui : sep = separator()
40570#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
40571#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
40572#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
40573#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
40574#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
40575#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
40576#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
40577#@gui : Value Action = choice("None","Cut","Normalize")
40578#@gui : sep = separator()
40579#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40580#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40581#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40582#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40583#@gui : sep = separator()
40584#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40585fx_segment_watershed : skip ${4=1}
40586  ac "b $2 segment_watershed $1",$3,$4
40587
40588fx_segment_watershed_preview :
40589  gui_split_preview "fx_segment_watershed ${^0}",${-3--1}
40590
40591#@gui Skeleton : fx_skeleton, fx_skeleton_preview(1)
40592#@gui : Method = choice{"Distance (Fast)","Thinning (Slow)"}
40593#@gui : Smoothness = float(0,0,10)
40594#@gui : sep = separator()
40595#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40596#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40597#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40598#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40599#@gui : sep = separator()
40600#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/07/04</i>.</small>")
40601fx_skeleton :
40602  remove_opacity
40603  b $2% >= 50%
40604  if $1 thinning 1
40605  else
40606    distance 0 sharpen 1e10 >= 100%
40607    repeat $! +erode[$>] 2 -[$>,-1] done
40608  fi
40609  * 255
40610
40611fx_skeleton_preview :
40612  gui_split_preview "fx_skeleton ${^0}",${-3--1}
40613
40614#@gui Super-Pixels : fx_superpixels, fx_superpixels_preview(0)
40615#@gui : Size = int(16,4,64)
40616#@gui : Regularity = float(10,0,128)
40617#@gui : Iterations = int(5,1,16)
40618#@gui : Colors = choice(1,"Random","Average")
40619#@gui : Border Opacity = float(1,0,1)
40620#@gui : Border Color = color(0,0,0,255)
40621#@gui : sep = separator()
40622#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40623#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40624#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40625#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40626#@gui : sep = separator()
40627#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/11/16</i>.</small>")
40628fx_superpixels :
40629  repeat $! l[$>]
40630    +srgb2lab slic. ${1-3}
40631    if $4 +blend shapeaverage else +map. 2,2 fi
40632    if $5 f[1] "i!=j(1,0) || i!=j(0,1)" [0],[0],1,4 fc. ${6-9} to_rgba.. j.. .,0,0,0,0,$5,... k..
40633    else k.
40634    fi
40635  endl done
40636
40637fx_superpixels_preview :
40638  gui_split_preview "fx_superpixels ${^0}",${-3--1}
40639
40640#@gui Thin Edges : fx_thin_edges, fx_thin_edges_preview(0)
40641#@gui : Smoothness = float(0,0,10)
40642#@gui : Threshold = float(15,0,50)
40643#@gui : Negative Colors = bool(0)
40644#@gui : sep = separator()
40645#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40646#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40647#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40648#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40649#@gui : sep = separator()
40650#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40651fx_thin_edges :
40652  b $1% gradient_norm >= $2% thinning 1
40653  if !$3 negate fi
40654  n 0,255
40655
40656fx_thin_edges_preview :
40657  gui_split_preview "fx_thin_edges ${^0}",${-3--1}
40658
40659
40660#@gui ____<b>Deformations</b>
40661#----------------------------
40662
40663#@gui Cartesian Transform : fx_custom_deformation, fx_custom_deformation(1)
40664#@gui : X-Warping = text{"(w+h)/20 * cos(y*20/h)"}
40665#@gui : Y-Warping = text{"(w+h)/20 * sin(x*20/w)"}
40666#@gui : Relative Warping = bool(1)
40667#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
40668#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
40669#@gui : sep = separator()
40670#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40671fx_custom_deformation :
40672  if !$5 to_a fi
40673  repeat $!
40674    +norm. . f.. "$1" f. "$2"
40675    a[-2,-1] c warp.. .,$3,$4,$5,1 rm.
40676  mv. 0 done
40677
40678#@gui Circle Transform : fx_circle_transform, fx_circle_transform_preview(1)
40679#@gui : Center (%) = point(50,50,0,1)
40680#@gui : Radius = point(75,50,0,1)
40681#@gui : X-Scale = float(-2,-16,16)
40682#@gui : Y-Scale = float(-2,-16,16)
40683#@gui : Symmetry = choice("None","Inside","Outside")
40684#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
40685#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
40686#@gui : Preview Reference Circle = bool(1)
40687#@gui : sep = separator()
40688#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/08/01</i>.</small>")
40689fx_circle_transform :
40690  repeat $! l[$>] to_rgba
40691    r={dx=($3-$1)*(w-1)%;dy=($4-$2)*(h-1)%;norm(dx,dy)}
40692    if $7==0 cond="i(X,Y,z,c,$8,$9)"
40693    elif $7==1 cond="if(N<"$r",i(X,Y,z,c,$8,$9),i)"
40694    else cond="if(N>"$r",i(X,Y,z,c,$8,$9),i)"
40695    fi
40696    f "U = x - w*$1%;
40697       V = y - h*$2%;
40698       N = sqrt(U*U + V*V);
40699       Nr = N - "$r";
40700       X = x + $5*Nr*U/N;
40701       Y = y + $6*Nr*V/N;
40702       "$cond
40703  endl done
40704
40705fx_circle_transform_preview :
40706  fx_circle_transform $*
40707  if $10
40708    rr2d ${-gui_preview_wh},0,1
40709    repeat $! l[$>]
40710      x0,y0={[$1,$2]*([w,h]-1)%}
40711      r={dx=($3-$1)*(w-1)%;dy=($4-$2)*(h-1)%;norm(dx,dy)}
40712      circle $x0,$y0,{$r-1},1,0xFFFFFFFF,0,0,0,255
40713      circle $x0,$y0,{$r+1},1,0xFFFFFFFF,0,0,0,255
40714      circle $x0,$y0,$r,1,0xFFFFFFFF,0,255,0,255
40715    endl done
40716  fi
40717
40718#@gui Conformal Maps : fx_conformal_maps, fx_conformal_maps_preview(1)
40719#@gui : Mapping = choice{8,"Custom Formula","z","(z-1)/(z+1)","cos(z)","sin(z)","tan(z)","exp(z)","log(z)",
40720#@gui : "Dipole: 1/(4*z^2-1)","Star: -5*(z^3/3-z/4)/2"}
40721#@gui : Exponent (Real) = float(1,-16,16)
40722#@gui : Exponent (Imaginary) = float(0,-16,16)
40723#@gui : Custom Formula = text{1,"((1.1 + i*z/6)/(1.04 - i*z/6))^6.2"}
40724#@gui : sep = separator()
40725#@gui : Zoom = float(0,-4,4)
40726#@gui : Angle = float(0,-180,180)
40727#@gui : Aspect Ratio = float(0,-1,1)
40728#@gui : X-Shift = float(0,-5,5)
40729#@gui : Y-Shift = float(0,-5,5)
40730#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
40731#@gui : Anti-Aliasing = int(0,0,3)
40732#@gui : sep = separator()
40733#@gui : Specify Different Output Size = _bool(0)
40734#@gui : Output Width = _text("1024")
40735#@gui : Output Height = _text("1024")
40736#@gui : sep = separator()
40737#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/15/02</i>.</small>")
40738fx_conformal_maps :
40739  to_a
40740  expr0="$4"
40741  expr1="z"
40742  expr2="(z+1)/(z-1)"
40743  expr3="cos(z)"
40744  expr4="sin(z)"
40745  expr5="tan(z)"
40746  expr6="exp(z)"
40747  expr7="log(z)"
40748  expr8="1/(4*z^2-1)"
40749  expr9="-5*(z^3/3-z/4)/2"
40750
40751  ('${expr$1}')
40752  replace_str. "*","**"
40753  replace_str. "/","//"
40754  replace_str. "^","^^"
40755  replace_str. "exp(","cexp("
40756  replace_str. "log(","clog("
40757  replace_str. "cos(","ccos("
40758  replace_str. "sin(","csin("
40759  replace_str. "tan(","ctan("
40760  expr={t}
40761  rm.
40762
40763  repeat $! l[$>]
40764    wh={$12?[$13,$14]:[w,h]}
40765    {(1+$11)*[$wh]},1,100%
40766    f. "begin(
40767          ccos(z) = (iz = [ -z[1],z[0] ]; (cexp(iz) + cexp(-iz)/2));
40768          csin(z) = (iz = [ -z[1],z[0] ]; (cexp(iz) - cexp(-iz)/2));
40769          ctan(z) = csin(z)//ccos(z);
40770          boundary = $10;
40771          interpolation = 1;
40772          const f = max(w,h);
40773          const f0 = max(w#0,h#0);
40774          i = [0,1];
40775        );
40776        z = (2*[ x,y ] - [ w,h ])/f;
40777        z = rot(-$6°)*z;
40778        z-= [ $8, $9 ];
40779        z/=[ 10^($5 + $7), 10^$5 ];
40780        z = ("$expr");
40781        if ($1, z = z^^[$2,$3]);
40782        z = rot($6°)*z;
40783        z = 0.5*(f0*z + [w#0,h#0]);
40784        I(#0,z)"
40785      r. $wh,1,100%,2
40786    rm..
40787  endl done
40788
40789fx_conformal_maps_preview :
40790  fx_conformal_maps ${1-3},"$4",${5-11},0,0,0
40791
40792#@gui Crease : fx_crease,fx_crease(0)
40793#@gui : Amplitude = float(30,0,300)
40794#@gui : Frequency (%) = float(10,0,100)
40795#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
40796#@gui : sep = separator()
40797#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/22</i>.</small>")
40798fx_crease :
40799  repeat $! l[$>]
40800    if !$3 to_a fi
40801    2,2,1,2,{"const w1 = w#-1-1; const h1 = h#-1 - 1; [ 0,w1,0,w1,0,0,h1,h1 ];"}
40802    r. {0,D=$2*[w,h]%;[max(D[0],1),max(D[1],1)]},1,2,3 noise. $1,1
40803    r. ..,..,1,2,3 warp.. .,0,1,$3 rm.
40804  endl done
40805
40806#@gui Distort Lens : fx_distort_lens, fx_distort_lens(1)
40807#@gui : Amplitude = float(0.1,-1,1)
40808#@gui : Aspect Ratio = float(0,-2,2)
40809#@gui : Zoom = float(0,-4,4)
40810#@gui : Center (%) = point(50,50,0,1)
40811#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
40812#@gui : sep = separator()
40813#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/18/02</i>.</small>")
40814fx_distort_lens :
40815  if !$6 to_a fi
40816  undistort ${1-3},$4%,$5%,$6
40817
40818#@gui Drop Water : fx_drop_water, fx_drop_water_preview(1)
40819#@gui : note = note("<small><b>Shape geometry:</b></small>")
40820#@gui : Shapes = choice("Procedural","Opaque Regions on Top Layer")
40821#@gui : Density = float(20,0,100)
40822#@gui : Radius = float(2,0,5)
40823#@gui : Variability = float(80,0,100)
40824#@gui : Random Seed = int(0,0,16384)
40825#@gui : note = note("<small>Parameters <i>Density</i>, <i>Radius</i>, <i>Variability</i> and <i>Random seed</i>
40826#@gui : are used only in <i>Procedural shapes</i> mode.</small>")
40827#@gui : sep = separator()
40828#@gui : note = note("<small><b>Light parameters:</b></small>")
40829#@gui : Refraction = float(3,0,20)
40830#@gui : Light Angle = float(35,0,360)
40831#@gui : Specular Size = float(10,0,100)
40832#@gui : Specular Intensity = float(1,0,1)
40833#@gui : Specular Centering = float(0.5,0,1)
40834#@gui : sep = separator()
40835#@gui : note = note("<small><b>Shadow parameters:</b></small>")
40836#@gui : Shadow Size = float(0.25,0,3)
40837#@gui : Shadow Intensity = float(0.5,0,1)
40838#@gui : Shadow Smoothness = float(0.75,0,3)
40839#@gui : Diffuse Shadow = float(0.05,0,3)
40840#@gui : sep = separator()
40841#@gui : Smoothness = float(0.15,0,3)
40842#@gui : Output as Separate Layers = _bool(1)
40843#@gui : sep = separator()
40844#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/21/07</i>.</small>")
40845fx_drop_water :
40846  N={$!-$1}
40847  if $N<=0 error "At least two layers are required in this mode." fi
40848
40849  repeat $N l[{$!-$>-1}] nm0={n} nm=${-gui_layer_name}
40850    nm img
40851
40852    # Create binary shapes (i.e. opacity map).
40853    srand $5
40854    if $1 # Shape from top layer.
40855      pass[0] 0 to_a. channels. 100% >=. 50%
40856      r. [0],[0],1,1,0,0,0.5,0.5
40857    else # Procedural shape.
40858      100%,100%
40859      rmin={max(0.1,$3*(1-$4%))} rmax={max(0.1,$3)}
40860      repeat 10
40861        100%,100%
40862        random3d {max(1,$2)} *3d. {-2,w},{-2,h},0
40863        j3d.. .,0,0,0,1,1,0,0 rm.
40864        b. {$rmin+($rmax-$rmin)*$>/9}%,0,1
40865        j.. .,0,0,0,0,0.5 rm.
40866      done
40867      >=. 10%
40868    fi
40869    nm. shape
40870
40871    # Create elevation map.
40872    +b[shape] 1% n. 0,30
40873    nm. elevation
40874
40875    # Warp image.
40876    g[elevation] xy a[-2,-1] c nm. grad
40877    +*[grad] {grad,$6*max(w,h)/100} *. [shape] b. $15%
40878    +warp[img] .,1,1,1 rm.. nm. refraction
40879
40880    # Compute specular spots.
40881    +*[grad] -1 100%,100%,1,1,1 a[-2,-1] c orientation.  # 3D normal map.
40882    a={$7*pi/180} ca={-cos($a)} sa={-sin($a)}
40883    mix_channels. ({(1-$10)*$ca},{(1-$10)*$sa},1) c. {100-$8}%,100% n. 0,1
40884    *. [shape] nm. spots
40885
40886    # Compute ambiant light (gradient).
40887    mix_channels[grad] ($ca,$sa)
40888    n[grad] 0,1 *[grad] [shape]
40889
40890    # Drop shadow.
40891    +shift[shape] {-$11*$ca}%,{-$11*$sa}%,0,0,1
40892    -. [shape] >=. 1 b. $13% n. 0,1
40893    nm. shadow
40894    b[shape] $14% n. 0,1  # Add diffuse shadow around each drop.
40895
40896    # Prepare layers for output.
40897    nm[img] name($nm)
40898
40899    *[shadow] 255 channels[shadow] -1,0 mv[shadow] 1
40900    nm[shadow] name($nm" [shadow]"),mode(alpha),opacity({$12*100})
40901
40902    to_a[refraction] sh[refraction] 100% +b[shape] $15% *[-2,-1] rm.
40903    mv[refraction] 2
40904    nm[refraction] name($nm" [refraction]"),mode(alpha)
40905
40906    channels[spots] -1,0 sh[spots] 0 f. 1 rm. *[spots] 255
40907    nm[spots] name($nm" [specular spots]"),mode(alpha),opacity({$9*100})
40908
40909    rv[shape,grad] a[grad,shape] c *[grad] 255 b[grad] $15%
40910    nm[grad] name($nm" [gradient]"),mode(grainmerge)
40911
40912    rv
40913    if !$16 gui_merge_layers nm $nm0 fi
40914  endl done
40915  if $1 rm[0] fi
40916
40917fx_drop_water_preview :
40918  N={$!-$1}
40919  if $N<=0 gui_warning_preview "At least two layers are required in this mode." return fi
40920  if $1
40921    repeat $N l[{$!-$>-1}]
40922      pass[0] 0 mv. 0
40923      fx_drop_water $* gui_merge_layers
40924    endl done
40925    rm[0]
40926  else
40927    repeat $! l[$>]
40928      fx_drop_water $* gui_merge_layers
40929    endl done
40930  fi
40931
40932#@gui Equirectangular to Nadir-Zenith : fx_equirectangular2nadirzenith, fx_equirectangular2nadirzenith(1)
40933#@gui : Mode = choice{"to Nadir / Zenith","to Equirectangular"}
40934#@gui : sep = separator()
40935#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/29/12</i>.</small>")
40936fx_equirectangular2nadirzenith :
40937  if $1 nadirzenith2equirectangular else equirectangular2nadirzenith fi
40938
40939#@gui Euclidean - Polar : fx_euclidean2polar, fx_euclidean2polar(1)
40940#@gui : Center (%) = point(50,50,0,1)
40941#@gui : Stretch Factor = float(1,0.1,10)
40942#@gui : Boundary = choice(1,"Transparent","Nearest","Periodic","Mirror")
40943#@gui : Inverse Transform = bool(0)
40944#@gui : sep = separator()
40945#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40946fx_euclidean2polar :
40947  if !$4 to_a fi
40948  if $5 polar2euclidean $1%,$2%,$3,$4 else euclidean2polar $1%,$2%,$3,$4 fi
40949
40950#@gui Fish-Eye : fisheye, fisheye(1)
40951#@gui : Center (%) = point(50,50,0,1,255)
40952#@gui : Radius = float(70,0,100)
40953#@gui : Amplitude = float(1,0,2)
40954#@gui : sep = separator()
40955#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40956
40957#@gui Flower : fx_flower, fx_flower_preview(1)
40958#@gui : Center (%) = point(50,50,0,1)
40959#@gui : Amplitude / Angle = point(75,50,0,1)
40960#@gui : Petals = int(6,2,20)
40961#@gui : Offset (%) = float(0,0,100)
40962#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
40963#@gui : sep = separator()
40964#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40965fx_flower :
40966  if !$7 to_a fi
40967  amplitude,angle={dx=$3-$1;dy=$4-$2;[norm(dx,dy),-atan2(dy,dx)*180/pi]}
40968  flower $amplitude,$5,$6%,$angle,$1%,$2%,$7
40969
40970fx_flower_preview :
40971  fx_flower $*
40972  line $1%,$2%,$3%,$4%,1,0xF0F0F0F0,0
40973  line $1%,$2%,$3%,$4%,1,0x0F0F0F0F,255
40974
40975#@gui Kaleidoscope [Blended] : fx_rotoidoscope, fx_rotoidoscope(1)
40976#@gui : Center (%) = point(50,50)
40977#@gui : Angular Tiles = int(10,1,72)
40978#@gui : Smoothness = float(0.5,0,5)
40979#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
40980#@gui : sep = separator()
40981#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40982fx_rotoidoscope :
40983  if !$5 to_a fi
40984  rotoidoscope $1%,$2%,$3,$4%,$5
40985
40986#@gui Kaleidoscope [Polar] : fx_kaleidoscope, fx_kaleidoscope(1)
40987#@gui : Center (%) = point(50,50)
40988#@gui : X-Offset (%) = float(0,0,100)
40989#@gui : Y-Offset (%) = float(0,0,100)
40990#@gui : Radius Cut = float(100,0,100)
40991#@gui : Angle Cut = float(10,0,100)
40992#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
40993#@gui : sep = separator()
40994#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
40995fx_kaleidoscope :
40996  if !$7 to_a fi
40997  shift $3%,$4%,0,0,2 kaleidoscope $1%,$2%,$5,$6,$7
40998
40999#@gui Kaleidoscope [Symmetry] : fx_symmetrizoscope, fx_symmetrizoscope(1)
41000#@gui : Iterations = int(4,1,32)
41001#@gui : Angle = float(0,0,360)
41002#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
41003#@gui : Symmetry Sides = choice("Backward","Forward","Swap")
41004#@gui : sep = separator()
41005#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/07/01</i>.</small>")
41006fx_symmetrizoscope :
41007  if !$3 to_a fi
41008  repeat $1
41009    ang={$2+180*$>/max(1,$1-1)}
41010    symmetrize 50%,50%,$ang,$3,0,{if($4!=2,$4,$>%2)}
41011  done
41012
41013#@gui Morph [Interactive] : fx_morph_interactive, fx_morph_interactive_preview
41014#@gui : Number of Frames = int(16,3,1024)
41015#@gui : Preview Precision = choice{2,"Coarsest (faster)","Coarse","Normal","Fine","Finest (slower)"}
41016#@gui : Keypoints = value(-1)
41017#@gui : sep = separator()
41018#@gui : note = note{"<b>Instructions:</b>"}
41019#@gui : note = note{"
41020#@gui : Use mouse buttons to add/move/remove correspondence keypoints over the interactive window that will appear,
41021#@gui : in order to create the morphing.\n\n
41022#@gui : <span color="#EE5500"><b>Source/target window:</b></span>\n\n
41023#@gui : - <b>Left mouse button</b>: Add new keypoint on current image and move it on the other one.\n
41024#@gui : - <b>Right mouse button</b>: Add/move keypoint on current image.\n
41025#@gui : - Key <b>DELETE</b> or <b>middle mouse button</b>: Delete keypoint.\n
41026#@gui : - Key <b>SPACE</b> or <b>mouse wheel</b>: Toggle source/target.\n\n
41027#@gui : <span color="#EE5500"><b>In-between window:</b></span>\n\n
41028#@gui : - <b>Mouse wheel</b>: Change morphing time, from 0 to 1.\n
41029#@gui : - <b>Left mouse button</b>: Reset morphing time to 0.5.\n\n
41030#@gui : <span color="#EE5500"><b>Both windows:</b></span>\n\n
41031#@gui : - Key <b>TAB</b>: Change keypoint radius.\n
41032#@gui : - Key <b>ENTER</b>: Play/stop in-between animation.\n
41033#@gui : - Key <b>R</b>: Reset keypoints.\n
41034#@gui : - Key <b>K</b>: Show/hide keypoints.\n
41035#@gui : - Keys <b>ESC</b> or <b>Q</b>: Process fullres and exit.
41036#@gui : "}
41037#@gui : sep = separator()
41038#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/04/16</i>.</small>")
41039fx_morph_interactive :
41040  if [$3][0]!=-1 __x_morph_keypoints=$3 fi
41041  rv
41042  x_morph $1,$2
41043  repeat $! gui_set_layer_name[$>] "Morphing ""#"$> gui_set_layer_pos[$>] 0,0 done
41044  rv
41045  u "{$1}{$2}{"$__x_morph_keypoints"}"
41046
41047fx_morph_interactive_preview :
41048  if $!<2 gui_warning_preview "This filter requires at least two input layers!" return fi
41049  rr2d ${-max_wh},0,3 + n 0,255
41050  if [$3][0]!=-1 gui_warning_preview "No preview available\n\nKeypoints from previous\nrun have been saved"
41051  else gui_warning_preview "No preview available"
41052  fi
41053
41054#@gui Perspective : fx_warp_perspective, fx_warp_perspective(1)
41055#@gui : X-Angle = float(1.73,-4,4)
41056#@gui : Y-Angle = float(0,-4,4)
41057#@gui : Zoom = float(1,0.1,4)
41058#@gui : Center (%) = point(50,50,0,1,255)
41059#@gui : X-Offset = float(0,0,100)
41060#@gui : Y-Offset = float(0,0,100)
41061#@gui : Boundary = choice(2,"Transparent","Nearest","Periodic","Mirror")
41062#@gui : sep = separator()
41063#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41064fx_warp_perspective :
41065  if !$8 to_a fi
41066  shift $6%,$7%,0,0,2 warp_perspective $1,$2,$3,$4,$5,$8
41067
41068#@gui Polar Transform : fx_transform_polar, fx_transform_polar(1)
41069#@gui : Preset = choice("Custom Transform","Inverse Radius","Swap Radius / Angle")
41070#@gui : Center (%) = point(50,50,0,1)
41071#@gui : Radius = text{"r + R/10*cos(a*5)"}
41072#@gui : Angle = text{"a"}
41073#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
41074#@gui : sep = separator()
41075#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41076fx_transform_polar :
41077  if !$6 to_a fi
41078  if $1==0
41079    transform_polar "$4","$5",$2%,$3%,$6
41080  elif $1==1
41081    transform_polar R-r,a,$2%,$3%,$6
41082  else
41083    transform_polar a*R/(2*pi),r*2*pi/R,$2%,$3%,$6
41084  fi
41085
41086#@gui Quadrangle : fx_quadrangle, fx_quadrangle_preview(1)
41087#@gui : Top-Left Vertex (%) = point(5,5,0,1,255,0,0)
41088#@gui : Top-Right Vertex (%) = point(95,25,0,1,0,255,0)
41089#@gui : Bottom-Right Vertex (%) = point(60,95,0,1,64,128,255)
41090#@gui : Bottom-Left Vertex (%) = point(40,95,0,1,255,255,0)
41091#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
41092#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
41093#@gui : Preview Type = choice(1,"Input","Output","Both")
41094#@gui : sep = separator()
41095#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/10/11</i>.</small>")
41096fx_quadrangle :
41097  at_quadrangle $1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,${9-10}
41098
41099fx_quadrangle_preview :
41100  repeat $! l[$>]
41101    if !$10 to_a fi
41102    if $11 +fx_quadrangle $* rr2d. {0,[w,h]},2,3 fi
41103    polygon[{$11==1?1:0}] 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,0.25,255
41104    if $11>=2
41105      circle[0] $1%,$2%,4,1,0 circle[0] $1%,$2%,3,1,255,0,0
41106      circle[0] $3%,$4%,4,1,0 circle[0] $3%,$4%,3,1,0,255,0
41107      circle[0] $5%,$6%,4,1,0 circle[0] $5%,$6%,3,1,64,128,255
41108      circle[0] $7%,$8%,4,1,0 circle[0] $7%,$8%,3,1,255,255,0
41109    elif $11>0
41110      rm[0]
41111    fi
41112    if $!==2
41113      drgba to[0] Quadrangle to[1] Result frame 1,1,0
41114      +a x a[0,1] y rr2d ${-gui_preview_wh},0,3
41115      k[{max(w#0,h#0)>max(w#1,h#1)?0:1}]
41116    fi
41117  endl done
41118
41119#@gui Raindrops : raindrops, raindrops(0)
41120#@gui : Amplitude = float(80,0,300)
41121#@gui : Density = float(0.1,0,1)
41122#@gui : Wavelength = float(1,0,2)
41123#@gui : Merging Steps = int(0,0,20)
41124#@gui : sep = separator()
41125#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/28/11</i>.</small>")
41126
41127#@gui Random : deform, deform(0)
41128#@gui : Amplitude = float(10,0,100)
41129#@gui : sep = separator()
41130#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41131
41132#@gui Ripple : ripple, ripple(0)
41133#@gui : Amplitude = float(10,0,100)
41134#@gui : Bandwidth = float(20,1,300)
41135#@gui : Shape = choice(2,"Bloc","Triangle","Sine","Sine+","Random")
41136#@gui : Angle = float(0,0,360)
41137#@gui : Offset = float(0,0,500)
41138#@gui : sep = separator()
41139#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/23/08</i>.</small>")
41140
41141#@gui Reflection : fx_reflect, fx_reflect(1)
41142#@gui : Height = float(50,0,100)
41143#@gui : Attenuation = float(1,0.1,4)
41144#@gui : Color = color(110,160,190,64)
41145#@gui : Waves Amplitude = float(0,0,100)
41146#@gui : Waves Smoothness = float(1.5,0,4)
41147#@gui : X-Angle = float(0,-10,10)
41148#@gui : Y-Angle = float(-3.30,-10,10)
41149#@gui : Focale = float(7,0,10)
41150#@gui : Zoom = float(1.5,1,5)
41151#@gui : sep = separator()
41152#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41153fx_reflect :
41154  repeat $!
41155    to_rgba. +rows. {100-$1}%,100% mirror. y water. $7,$8
41156    s. c
41157    f[-4] "(i*(255-$6) + $6*$3)/255"
41158    f... "(i*(255-$6) + $6*$4)/255"
41159    f.. "(i*(255-$6) + $6*$5)/255" a[-4--1] c
41160    *. '(h^$2-y^$2)/h^$2' a[-2,-1] y
41161    100%,100%,100%,1,$11*$12*(x/w-0.5)
41162    100%,100%,100%,1,$11*$12*(y/h-0.5)
41163    100%,100%,100%,1,"$10*(x/w-0.5) + $9*(y/h-0.5) + $11"
41164    /... . +... 0.5 *... {-3,w}
41165    /[-2,-1] +. 0.5 *. {h}
41166    a[-2,-1] c warp.. .,0,1,0 rm.
41167  mv. 0 done
41168  autocrop 0,0,0,0
41169
41170#@gui Seamcarve : fx_seamcarve, fx_seamcarve_preview(1)
41171#@gui : Width (%) = float(85,0,200)
41172#@gui : Height (%) = float(100,0,200)
41173#@gui : Maximal Seams per Iteration (%) = float(15,0,100)
41174#@gui : Use Top Layer as a Priority Mask = bool(0)
41175#@gui : Antialiasing = bool(1)
41176#@gui : sep = separator()
41177#@gui : note = note{"<small><b>Note:</b>
41178#@gui : You can define a transparent top layer that will help the seam-carving algorithm to preserve or force
41179#@gui : removing image structures:\n
41180#@gui : \n  - Draw areas in <i>red</i> to force removing them.
41181#@gui : \n  - Draw areas in <i>green</i> to preserve them.
41182#@gui : \n  - Don't forget also to set the <i>Input layers...</i> parameter to input both layers to the filter.
41183#@gui : </small>"}
41184#@gui : sep = separator()
41185#@gui : note = note("<small>Authors: <i>Garagecoder</i> and <i>David Tschumperlé</i>.
41186#@gui :       Latest Update: <i>2014/02/06</i>.</small>")
41187fx_seamcarve :
41188  if $4
41189    if $!<2 error "Priority mask (top layer) is missing!" fi
41190    _fx_seamcarve
41191  fi
41192  seamcarve $1%,$2%,$4,$5,$3%
41193  if $4 repeat $! channels[$>] 0,{$>,s-2} done fi
41194  c 0,255
41195
41196fx_seamcarve_preview :
41197  if $4
41198    if $!<2 to_rgb to "Priority mask (top layer) is missing!",5,5,18,2 return fi
41199    _fx_seamcarve
41200  fi
41201  repeat $! l[$>]
41202    w={w} h={h}
41203    seamcarve $1%,$2%,$4,$5,{max($3,10)}%
41204    if $4 channels 0,{s-2} fi
41205    to_rgba r $w,$h,1,100%,0,0,0.5,0.5
41206  endl done
41207  c 0,255
41208
41209_fx_seamcarve :
41210  mv[0] $!
41211  l.
41212    s c k[0,1]
41213    >[1] [0] !=[0] 0 -[0] [1] *[0] -1 + * 256
41214  endl
41215  repeat $!-1 a[$>] .,c done rm.
41216
41217#@gui Sphere : fx_map_sphere, fx_map_sphere_preview(1)
41218#@gui : Width = _int(512,1,4096)
41219#@gui : Height = _int(512,1,4096)
41220#@gui : Radius = float(90,0,400)
41221#@gui : Dilation = float(0.5,0,1)
41222#@gui : Angle = float(0,-50,50)
41223#@gui : Border Smoothness = float(0,0,200)
41224#@gui : Border Width = float(20,0,100)
41225#@gui : Orientation = choice("0 deg.","90 deg.","180 deg.","270 deg.")
41226#@gui : Background = choice("Transparent","Mean Color")
41227#@gui : Fading = float(0,0,100)
41228#@gui : Fading Shape = float(0.5,0,3)
41229#@gui : sep = separator()
41230#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/07/11</i>.</small>")
41231fx_map_sphere :
41232  rotate {$8*90}
41233  if $6
41234    repeat $!
41235      shift. {round(w/2)},0,0,0,2 +columns. {(1-$7/100)*w/2},{(1+$7/100)*w/2}
41236      100% gaussian. {0.1*w},{h},0 100% 100% a[-3--1] c r. ..,..,1,3
41237      smooth.. .,$6,5,0 rm.
41238      j.. .,{(1-$7/100)*{-2,w}/2} rm. shift. -{round(w/2)},0,0,0,2
41239    mv. 0 done
41240  fi
41241  shift $5%,0,0,0,2 to_rgba
41242  if $9
41243    repeat $!
41244      +rows[$>] 0 r. 1,1,1,4,2 RGBA$>={^}
41245      r. [$>],[$>],1,4 -[$>,-1]
41246    done
41247  fi
41248  map_sphere $1,$2,$3,$4,$10,$11
41249  if $9
41250    repeat $!
41251      (${RGBA$>}) y. c r. [$>],[$>],1,4 +[$>,-1]
41252    done
41253  fi
41254
41255fx_map_sphere_preview :
41256  fx_map_sphere {w},{h},${3--1}
41257
41258#@gui Spherize : fx_spherize, fx_spherize_preview(1)
41259#@gui : Radius (%) = float(50,0,300)
41260#@gui : Strength = float(1,-10,10)
41261#@gui : Smoothness (%) = float(0,0,4)
41262#@gui : Center (%) = point(50,50,0,1,255,255,255,170,10)
41263#@gui : Ratio = float(0,-2,2)
41264#@gui : Angle = float(0,-90,90)
41265#@gui : Interpolation = choice(2,"Nearest Neighbor","Linear","Cubic")
41266#@gui : Preview Grid = bool(0)
41267#@gui : sep = separator()
41268#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/10/03</i>.</small>")
41269fx_spherize :
41270  ratio={10^$6}
41271  spherize $1%,$2,$3%,$4%,$5%,$ratio,$7,$8
41272  cut 0,255
41273
41274fx_spherize_preview :
41275  cx,cy=${4,5}
41276  if $9 grid 5%,5%,50%,50%,0.6,255 fi
41277  fx_spherize ${1-3},$cx,$cy,${6--1}
41278
41279#@gui Square to Circle : fx_square_circle, fx_square_circle
41280#@gui : Mode = choice(0,"Square to Circle","Circle to Square")
41281#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
41282#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
41283#@gui : sep = separator()
41284#@gui : X-Factor (%) = float(0,-100,100)
41285#@gui : Y-Factor (%) = float(0,-100,100)
41286#@gui : X-Offset (%) = float(0,-300,300)
41287#@gui : Y-Offset (%) = float(0,-300,300)
41288#@gui : sep = separator()
41289#@gui : note = note("<small>This filter implements the mapping functions described in this page,
41290#@gui : by <i>C. Fong</i>:</small>")
41291#@gui : url = link("http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html")
41292#@gui : sep = separator()
41293#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/10/30</i>.</small>")
41294fx_square_circle :
41295  mode,interp,boundary,factx,facty,offx,offy=${1-7}
41296  if !$boundary to_a fi
41297  base="const interpolation = "$interp";
41298        const boundary = "$boundary";
41299        const offx = "$offx"%;
41300        const offy = "$offy"%;
41301        const factx = 10^-("$factx"%);
41302        const facty = 10^-("$facty"%);
41303        const w2 = int(w/2);
41304        const h2 = int(h/2);"
41305  if !$mode # Square to circle
41306    f $base"
41307       const tst = 2*sqrt(2);
41308       U = (2*x/(w-1) - 1)*factx + offx;
41309       V = (2*y/(h-1) - 1)*facty + offy;
41310       U2 = U^2;
41311       V2 = V^2;
41312       U2mV2 = U2 - V2;
41313       X = 0.5*(sqrt(max(0,2 + tst*U + U2mV2)) - sqrt(max(0,2 - tst*U + U2mV2)));
41314       Y = 0.5*(sqrt(max(0,2 + tst*V - U2mV2)) - sqrt(max(0,2 - tst*V - U2mV2)));
41315       (X+=1)*=w2 - 0.5;
41316       (Y+=1)*=h2 - 0.5;
41317       I(X,Y)"
41318  else # Circle to square
41319    f $base"
41320       X = (2*x/(w-1) - 1)*factx + offx;
41321       Y = (2*y/(h-1) - 1)*facty + offy;
41322       U = X*sqrt(abs(1 - 0.5*Y^2));
41323       V = Y*sqrt(abs(1 - 0.5*X^2));
41324       (U+=1)*=w2 - 0.5;
41325       (V+=1)*=h2 - 0.5;
41326       I(U,V)"
41327  fi
41328
41329#@gui Stereographic Projection : fx_project_stereographic, fx_project_stereographic_preview(1)
41330#@gui : Transform = choice("Direct","Inverse")
41331#@gui : Center (%) = point(50,50,0,1,255,255,255,170)
41332#@gui : Radius / Angle = point(50,75,0,1,255,0,255,170)
41333#@gui : Horizon Leveling (deg) = float(0,-10,10)
41334#@gui : Left / Right Blur (%) = float(0,0,20)
41335#@gui : Dilation = float(0,-2,2)
41336#@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axis")
41337#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
41338#@gui : Last Center = value(50,50)
41339#@gui : sep = separator()
41340#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/07/04</i>.</small>")
41341fx_project_stereographic :
41342  is_inverse,centerx,centery,radangx,radangy,rechor,lrblur,dilation,mirror,boundary,ocenterx,ocentery=${1-11}
41343
41344  if $centerx!=$ocenterx" || "$centery!=$ocentery
41345    deltax,deltay={[$radangx,$radangy]-[$ocenterx,$ocentery]}
41346    radangx,radangy={[$centerx,$centery]+[$deltax,$deltay]}
41347  fi
41348  status=\{$is_inverse\}\{$centerx,$centery\}\{$radangx,$radangy\}\{$rechor\}\{$lrblur\}\
41349         \{$dilation\}\{$mirror\}\{$boundary\}\{$centerx,$centery\}
41350
41351  # So that preview line does not hide left/right frontier:
41352  nradangx,nradangy={[$centerx,$centery]+rot(-90°)*([$radangx,$radangy]-[$centerx,$centery])}
41353  init="const boundary = "$is_inverse?$boundary:2";
41354        const interpolation = 1;
41355        const dilation = 2^"$dilation";
41356        const centerx = "$centerx"%*(W-1);
41357        const centery = "$centery"%*(H-1);
41358        const radangx = "$nradangx"%*(W-1) - centerx;
41359        const radangy = "$nradangy"%*(H-1) - centery;
41360        const R = sqrt(radangx^2 + radangy^2);
41361        const theta0 = atan2(radangy,radangx);
41362        const pi2 = 2*pi;"
41363  m "_fx_project_stereographic_mirror : if !$""1 mirror y elif $""1==1 mirror xy elif $""1==3 mirror x fi"
41364  repeat $! l[$>]
41365    if !$boundary to_a fi
41366    if $rechor rotate $rechor,1,3 fi
41367    if $lrblur
41368      100%,1,1,1,!x||x==w-1 shift {round(w/2)},0,0,0,2 b. x,$lrblur% n. 0,1 +b.. x,$lrblur%
41369      r.. .,.,1,1 j... .,0,0,0,0,1,.. k[0] shift {-round(w/2)},0,0,0,2
41370    fi
41371    if $is_inverse
41372      100%,50%,1,100%,"*
41373        const W = w#0;
41374        const H = h#0;
41375        "$init"
41376        theta = theta0 + x*pi2/w;
41377        phi = (y/h - 0.5)*pi;
41378        z = R*sin(phi);
41379        rho = ((R + z)/(R - z))^(0.5/dilation)*R;
41380        X = centerx + rho*cos(theta);
41381        Y = centery + rho*sin(theta);
41382        I(#0,X,Y)"
41383      _fx_project_stereographic_mirror $mirror
41384    else
41385      _fx_project_stereographic_mirror $mirror
41386      {u=0$_is_preview?min(w,h):max(w,h);[u,u,1,s]},"*
41387        const W = w;
41388        const H = h;
41389        "$init"
41390        X = x - centerx;
41391        Y = y - centery;
41392        theta = atan2(Y,X);
41393        beta = ((X^2 + Y^2)/R^2)^dilation;
41394        z = R*(beta - 1)/(beta + 1);
41395        phi = asin(z/R);
41396        theta = ((theta - theta0)*w#0/pi2)%w#0;
41397        phi = (h#0*(phi/pi + 0.5))%h#0;
41398        I(#0,theta,phi)"
41399    fi
41400  k. endl done um _fx_project_stereographic
41401
41402  if $_is_preview
41403    line $centerx%,$centery%,$radangx%,$radangy%,0.75,0xF0F0F0F0,255,255,255,255
41404    line $centerx%,$centery%,$radangx%,$radangy%,0.75,0x0F0F0F0F,0,0,0,255
41405  fi
41406  u $status
41407
41408fx_project_stereographic_preview :
41409  _is_preview=1
41410  fx_project_stereographic $"*"
41411
41412#@gui Symmetrize : fx_symmetrize, fx_symmetrize_preview(1)
41413#@gui : Point 1 = point(50,50,0,1,0,255,0,170,10)
41414#@gui : Point 2 = point(50,75,-1,1,255,255,0,170,10)
41415#@gui : Angle = float(0,-180,180)
41416#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
41417#@gui : Type = choice("Symmetry","Antisymmetry")
41418#@gui : Swap Sides = bool(0)
41419#@gui : sep = separator()
41420#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/06/11</i>.</small>")
41421fx_symmetrize :
41422  if !$6 to_a fi
41423  angle={isnan($3)?$5:atan2($4-$2,$3-$1)*180/pi}
41424  symmetrize $1%,$2%,$angle,${6-8}
41425
41426fx_symmetrize_preview :
41427  fx_symmetrize $*
41428  rr2d ${-gui_preview_wh},0,1
41429  u,v={angle=isnan($3)?$5*pi/180:atan2($4-$2,$3-$1);[cos(angle),sin(angle)]}
41430  repeat $! l[$>]
41431    x0,y0,x1,y1={V=[$u,$v];([${1,2},${1,2}]+10000*[V,-V])*([w,h,w,h]-1)%}
41432    line $x0,$y0,$x1,$y1,1,0x0F0F0F0F,0,0,0,255
41433    line $x0,$y0,$x1,$y1,1,0xF0F0F0F0,255
41434  endl done
41435
41436#@gui Textured Glass : fx_textured_glass, fx_textured_glass_preview(0)
41437#@gui : X-Amplitude = float(40,0,400)
41438#@gui : Y-Amplitude = float(40,0,400)
41439#@gui : X-Smoothness = float(1,0,5)
41440#@gui : Y-Smoothness = float(1,0,5)
41441#@gui : Edge Attenuation = float(0,0,1)
41442#@gui : Edge Influence = float(2,0,10)
41443#@gui : Noise Scale = int(0,0,16)
41444#@gui : sep = separator()
41445#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41446#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41447#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41448#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41449#@gui : sep = separator()
41450#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/21/11</i>.</small>")
41451fx_textured_glass :
41452  repeat $! l[$>]
41453    100%,100%,1,1
41454    if $7 plasma. 1,1,$7 else rand. 0,1 fi
41455    g. xy
41456    if $5
41457      +gradient_norm... +. 1 b. $6 ^. -$5
41458      *... . *[-2,-1]
41459    fi
41460    blur_xy[-2,-1] $3,$4
41461    *.. {-2,$1/max(abs(im),abs(iM))}
41462    *. {$2/max(abs(im),abs(iM))}
41463    a[-2,-1] c
41464    warp.. .,1,1 rm.
41465  endl done
41466
41467fx_textured_glass_preview :
41468  gui_split_preview "fx_textured_glass $*",${-3--1}
41469
41470#@gui Twirl : fx_twirl, fx_twirl(1)
41471#@gui : Amplitude = float(1,-5,5)
41472#@gui : Center (%) = point(50,50,0,1)
41473#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
41474#@gui : sep = separator()
41475#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41476fx_twirl :
41477  if !$4 to_a fi
41478  twirl $1,$2%,$3%,$4
41479
41480#@gui Warp [Interactive] : fx_warp_interactive, fx_warp_interactive_preview(1)
41481#@gui : Preview Precision = choice{1,"Coarsest (faster)","Coarse","Normal","Fine","Finest (slower)"}
41482#@gui : sep = separator()
41483#@gui : note = note{"<small><b>Pre-defined keypoints</b></small>"}
41484#@gui : Regular Grid = int(2,2,10)
41485#@gui : Contours = int(0,0,32)
41486#@gui : Keypoints = value(-1)
41487#@gui : sep = separator()
41488#@gui : note = note{"<b>Instructions:</b>"}
41489#@gui : note = note{"
41490#@gui : Use mouse to add/move/delete keypoints over the interactive window that will appear,
41491#@gui : in order to create the deformation map.\n\n
41492#@gui : - <b>Left mouse button</b>: Add and move keypoint.\n
41493#@gui : - <b>Right mouse button</b>: Delete keypoint.\n
41494#@gui : - Key <b>SPACE</b> or <b>middle mouse button</b>: Show/hide keypoints.\n
41495#@gui : - Key <b>TAB</b>: Change keypoint radius.\n
41496#@gui : - Key <b>SHIFT</b>: Toggle to original image.\n
41497#@gui : - Key <b>R</b>: Reset keypoints.\n
41498#@gui : - Keys <b>ESC</b>, <b>ENTER</b> or <b>Q</b>: Process fullres and exit.
41499#@gui : "}
41500#@gui : sep = separator()
41501#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/04/08</i>.</small>")
41502fx_warp_interactive :
41503  if [$4][0]!=-1 __x_warp_keypoints=$4 fi
41504  x_warp $2,$2,$3,$1
41505  u "{$1}{$2}{$3}{"$__x_warp_keypoints"}"
41506
41507fx_warp_interactive_preview :
41508  if [$4][0]!=-1 __x_warp_keypoints=$4 fi
41509  repeat $! l[$>]
41510    rr2d $_preview_width,$_preview_height,0,3 to_color drgba
41511    if narg($__x_warp_keypoints) ($__x_warp_keypoints) r. 1,{w/4},1,4,-1
41512    else
41513
41514      # Keypoints located on regular grid.
41515      nbp,nbq=$2,$2
41516      1,{$nbp*$nbq},1,4,"const nbp = "$nbp"; const nbq = "$nbq";
41517        p = y%nbp;
41518        q = int(y/nbp);
41519        x = p*100/(nbp - 1);
41520        y = q*100/(nbq - 1);
41521        [ x,y,x,y ]"
41522
41523      # Keypoints located on contours.
41524      nbc=$3
41525      if $nbc>0
41526        +b[0] 0.5 gradient_norm. sqrt. {round([w,h]/4)} gaussian. 20%
41527        1,$nbc,1,4,">
41528          begin(ref(crop(#-1),gauss));
41529          st = stats(#-2);
41530          iM = st[1];
41531          xM = st[8];
41532          yM = st[9];
41533          img = vector(#w#-1*h#-1,-iM);
41534          draw(#-2,img,xM - w#-1/2,yM - h#-1/2,0,0,w#-1,h#-1,1,1,-1,gauss);
41535          nxyM = [ xM,yM ]*100/([w#-2,h#-2]-1);
41536          [ nxyM,nxyM ]"
41537        rm[-3,-2]
41538        a[-2,-1] y
41539      fi
41540    fi
41541
41542    +_x_warp_rbf. {0,round([w,h]/4)} *. 4 r. [0],[0],1,100%,3 +. '[x,y]' warp[0] .,0,1,3 rm.
41543
41544    eval. "*
41545      begin(
41546        col1 = [ 64,200,255 ];
41547        const radius1 = 3;
41548        const radius2 = radius1 + 2;
41549        fact = ([ w#0,h#0 ] - 1)%
41550      );
41551      X = (I)[0,2]*fact;
41552      ellipse(#0,X,radius2,radius2,0,1,0);
41553      ellipse(#0,X,radius1,radius1,0,1,col1); I"
41554    rm.
41555
41556    if narg($__x_warp_keypoints)
41557      0 t. "Keypoints from previous\nrun have been saved",0,0,24,1,255
41558      frame. 5,5,0 +dilate_circ. 5 a[-2,-1] c blend alpha
41559    fi
41560  endl done
41561
41562#@gui Water : water, water(0)
41563#@gui : Amplitude = float(30,0,300)
41564#@gui : Smoothness = float(1.5,0,4)
41565#@gui : Angle = float(45,0,180)
41566#@gui : sep = separator()
41567#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/07/10</i>.</small>")
41568
41569#@gui Wave : wave, wave(1)
41570#@gui : Amplitude = float(10,0,30)
41571#@gui : Frequency = float(0.4,0,2)
41572#@gui : Center (%) = point(50,50,0,1,255,255,255,170,10)
41573#@gui : sep = separator()
41574#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41575
41576#@gui Wind : fx_wind, fx_wind_preview(0)
41577#@gui : Amplitude = int(20,0,500)
41578#@gui : Angle = float(0,0,360)
41579#@gui : Attenuation = float(0.7,0,1)
41580#@gui : Threshold = float(20,0,100)
41581#@gui : Mode = choice(1,"Darker","Brighter")
41582#@gui : sep = separator()
41583#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41584#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41585#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41586#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41587#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41588#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41589#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41590#@gui : Value Action = choice("None","Cut","Normalize")
41591#@gui : sep = separator()
41592#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41593#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41594#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41595#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41596#@gui : sep = separator()
41597#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/13/07</i>.</small>")
41598fx_wind :
41599  if !$5 negate fi
41600  ac "wind ${1-4}",$6,$7
41601  if !$5 negate fi
41602
41603fx_wind_preview :
41604  gui_split_preview "fx_wind $*",${-3--1}
41605
41606#@gui Zoom : fx_zoom, fx_zoom(1)
41607#@gui : Factor = float(2,0.01,10)
41608#@gui : Center (%) = point(50,50,0,1,255)
41609#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
41610#@gui : sep = separator()
41611#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41612fx_zoom :
41613  if !$4" && "$1<1 to_a fi
41614  zoom $1,{$2%},{$3%},0,$4
41615
41616
41617#@gui ____<b>Degradations</b>
41618#-----------------------------
41619
41620#@gui Add Grain : fx_simulate_grain, fx_simulate_grain_preview(0)
41621#@gui : Preset = choice{"Orwo NP20-GDR","Kodak TMAX 400","Kodak TMAX 3200","Kodak TRI-X 1600","Unknown"}
41622#@gui : Blend Mode = choice(1,"Alpha","Grain Merge","Hard Light","Overlay","Soft Light","Grain Only")
41623#@gui : Opacity = float(0.2,0,1)
41624#@gui : Scale = float(100,30,200)
41625#@gui : Colored Grain = bool()
41626#@gui : sep = separator()
41627#@gui : Brightness (%) = float(0,-100,100)
41628#@gui : Contrast (%) = float(0,-100,100)
41629#@gui : Gamma (%) = float(0,-100,100)
41630#@gui : Hue (%) = float(0,-100,100)
41631#@gui : Saturation (%) = float(0,-100,100)
41632#@gui : sep = separator()
41633#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41634#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41635#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41636#@gui : Preview Grain Alone = bool()
41637#@gui : sep = separator()
41638#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/02/08</i>.</small>")
41639fx_simulate_grain :
41640  __fx_simulate_grain ${arg\ {1+$1},${-_fx_simulate_grain}},${2-10},0,0
41641
41642_fx_simulate_grain :
41643  u orwo_np20,kodak_tmax400,kodak_tmax3200,kodak_trix1600,unknown
41644
41645fx_simulate_grain_preview :
41646  gui_split_preview "_fx_simulate_grain_preview $*",$-2
41647
41648_fx_simulate_grain_preview :
41649  __fx_simulate_grain ${arg\ {1+$1},${-_fx_simulate_grain}},${2-12}
41650
41651__fx_simulate_grain :
41652  bm0=alpha bm1=grainmerge bm2=hardlight bm3=overlay bm4=softlight bm5=alpha
41653  if isfile(['{/${-path_cache}grain_$1.cimgz}']) i ${-path_cache}grain_$1.cimgz
41654  else i https://gmic.eu/data_film_presets/grain_$1.cimgz o. ${-path_cache}grain_$1.cimgz
41655  fi
41656
41657  repeat $!-1 l[$>,-1] split_opacity[0]
41658    +syntexturize. {0,max(10,100*w/$4)},{0,max(10,100*h/$4)}
41659    if $5 +syntexturize.. {w},{h} +syntexturize... {w},{h} a[-3--1] c fi
41660    r. {0,w},{0,h},1,100%,5 c. 0,255
41661    adjust_colors. ${6-10}
41662    if $12 k[0,-1] rv
41663    else blend[0,-1] ${bm$2},{if($2<=4,$3,1)}
41664    fi
41665  a[^-1] c endl done rm.
41666
41667#@gui Blur [Angular] : fx_blur_angular, fx_blur_angular_preview(1)
41668#@gui : Amplitude (%) = float(2,0,20)
41669#@gui : Center (%) = point(50,50,0,1)
41670#@gui : Sharpness = float(0,0,500)
41671#@gui : Preview Guides = bool(1)
41672#@gui : sep = separator()
41673#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41674#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41675#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41676#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41677#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41678#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41679#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41680#@gui : Value Action = choice("None","Cut","Normalize")
41681#@gui : sep = separator()
41682#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/16/01</i>.</small>")
41683fx_blur_angular :
41684  ac "blur_angular $1%,$2%,$3% sharpen $4",$6,$7
41685
41686fx_blur_angular_preview :
41687  fx_blur_angular $*
41688  if $5
41689    line 0,$3%,100%,$3%,0.5,0xF0F0F0F0,255 line 0,$3%,100%,$3%,0.5,0x0F0F0F0F,0
41690    line $2%,0,$2%,100%,0.5,0xF0F0F0F0,255 line $2%,0,$2%,100%,0.5,0x0F0F0F0F,0
41691  fi
41692
41693#@gui Blur [Bloom] : fx_blur_bloom, fx_blur_bloom_preview(0)
41694#@gui : Amplitude = float(1,0,10)
41695#@gui : Ratio = float(2,0,5)
41696#@gui : Iterations = int(5,0,100)
41697#@gui : Operator = choice("Add","Max","Min")
41698#@gui : Kernel = choice(1,"Deriche","Gaussian","Box","Triangle","Quadratic")
41699#@gui : Normalize Scales = bool(0)
41700#@gui : Anisotropy = float(0,0,1)
41701#@gui : Angle = float(0,-180,180)
41702#@gui : note = note("Parameter <i>Angle</i> is only active when <i>Anisotropy</i>&gt;0")
41703#@gui : sep = separator()
41704#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41705#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41706#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41707#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41708#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41709#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41710#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41711#@gui : sep = separator()
41712#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41713#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41714#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41715#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41716#@gui : sep = separator()
41717#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/03/02</i>.</small>")
41718fx_blur_bloom :
41719  op=${"arg 1+$4,+,max,min"}
41720  if !$7 ac "blur_bloom ${1-3},"$op",${5-6},xy",$9
41721  else
41722    wh={[w,h]}
41723    rotate $8,2,1
41724    ac "blur_bloom ${1-3},"$op",${5-6},x blur_bloom {$1*(1-$7)},${2-3},"$op",${5-6},y",$9
41725    rotate {-$8},2,1
41726    r $wh,1,100%,0,0,0.5,0.5 c 0,255
41727  fi
41728
41729fx_blur_bloom_preview :
41730  gui_split_preview "fx_blur_bloom $*",${-3--1}
41731
41732#@gui Blur [Depth-of-Field] : fx_blur_dof, fx_blur_dof_preview(1)
41733#@gui : Blur Amplitude = float(3,0,20)
41734#@gui : Blur Precision = int(16,2,64)
41735#@gui : Depth-of-Field Type = choice{"Gaussian","User-Defined (Bottom Layer)"}
41736#@gui : Invert Blur = bool(0)
41737#@gui : sep = separator()
41738#@gui : note = note("<small><b>Gaussian depth-of-field:</b></small>")
41739#@gui : Center (%) = point(50,50,0,0,255)
41740#@gui : First Radius = float(30,0,200)
41741#@gui : Second Radius = float(30,0,200)
41742#@gui : Angle = float(0,0,180)
41743#@gui : Sharpness = float(1,0,8)
41744#@gui : Preview Guides = bool(1)
41745#@gui : sep = separator()
41746#@gui : note = note("<small><b>User-defined depth-of-field:</b></small>")
41747#@gui : Gamma = float(0,-2,2)
41748#@gui : note = note("<small>You can specify your own depth-of-field image, as a <b>bottom layer</b> image
41749#@gui : whose luminance encodes the depth for each pixel.
41750#@gui : Don't forget to modify the <b>Input layers</b> combo-box to make this layer active for the filter.</small>")
41751#@gui : sep = separator()
41752#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/25/02</i>.</small>")
41753fx_blur_dof :
41754  _$0 ${1-10},0,$12
41755
41756fx_blur_dof_preview :
41757  _fx_blur_dof $*
41758
41759_fx_blur_dof :
41760  if !$3  # Gaussian DOF.
41761    repeat $! l[$>] if $11 drgba fi split_opacity l[0]
41762      rmax={(w*w+h*h)^0.5} R={$7*$rmax/100} r={$8*$rmax/100}
41763      t={$9*pi/180} u={cos($t)} v={sin($t)}
41764      l1={($rmax/(1e-8+$R))^2} l2={($rmax/(1e-8+$r))^2}
41765      a={$l1*($u)^2+$l2*($v)^2} b={$u*$v*($l1-$l2)} c={$l1*($v)^2+$l2*($u)^2}
41766      100%,100%,1,1,'X=(x-$5*w/100)/max(w,h);Y=(y-$6*h/100)/max(w,h);f=$a*X*X+2*$b*X*Y+$c*Y*Y;exp(-f^$10/2.5)'
41767      -[1] 1 *[1] -$1 ms={im} Ms={iM}
41768
41769      if $11 # With preview of guides.
41770        +isoline3d[1] {0.1*$1} col3d. 255,255,0
41771        +isoline3d[1] {0.5*$1} col3d. 255,128,0
41772        +3d[-2--1]
41773        __fx_dof_blur[0,1] $2,$ms,$Ms,$4
41774        [0],[0],1,3 j3d. ..,0,0,0,1,1,0,0 rm..
41775        circle. $5%,$6%,3,1,255,255,255
41776        +compose_channels. + !=. 0 dilate. 3
41777        j[0] ..,0,0,0,0,0.5,.,1 rm[-2,-1]
41778      else __fx_dof_blur[0,1] $2,$ms,$Ms,$4 # Without preview.
41779      fi
41780    endl if $11 k[0] fi a c endl done
41781  elif $!>1 # User-defined DOF (as bottom layer).
41782    luminance. n. 0,1 ^. {10^$12}
41783    repeat $!-1 +r. {$>,w},{$>,h},1,1,3 l[$>,-1] split_opacity[0]
41784      __fx_dof_blur[0,-1] $2,0,$1,$4
41785    a c endl done rm.
41786  else drgba to "Depth-of-field (bottom layer) is missing !",2,2,13,2,1,255
41787  fi
41788
41789# Render DOF blur (generic)
41790# [0] = Input image
41791# [1] = continuous DOF field (with same size as [0]).
41792# $1 = nb quantization levels.
41793# $2 = minimal blur level.
41794# $3 = maximal blur level.
41795# $4 = invert blur.
41796__fx_dof_blur :
41797  n[1] 0,{$1-1} round[1]
41798  [0],[0],1,{0,s+1}
41799  s=0
41800  repeat $1
41801    +==[1] {if($4,$<,$>)} b. 2%
41802    j.. [0],0,0,0,0,-1,.,1
41803    j.. .,0,0,0,100%,-1
41804    rm.
41805     ns={$2+($3-$2)*($>+1)/($1-1)}
41806     b[0] {sqrt($ns^2-$s^2)}%
41807     s=$ns
41808  done
41809  s. c,{-s+1} /[-2,-1] rm[0,1]
41810
41811#@gui Blur [Gaussian] : fx_gaussian_blur, fx_gaussian_blur_preview(0)
41812#@gui : XY-Amplitude = float(3,0,20)
41813#@gui : X-Amplitude = float(0,0,20)
41814#@gui : Y-Amplitude = float(0,0,20)
41815#@gui : Boundary = choice(1,"Black","Nearest")
41816#@gui : sep = separator()
41817#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41818#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41819#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41820#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41821#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41822#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41823#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41824#@gui : Value Action = choice("None","Cut","Normalize")
41825#@gui : sep = separator()
41826#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41827#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41828#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41829#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41830#@gui : sep = separator()
41831#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41832_fx_gaussian_blur :
41833  b $1,$4
41834  if $2>0 repeat $! l. s y b $2,$4 a y endl mv. 0 done fi
41835  if $3>0 repeat $! l. s x b $3,$4 a x endl mv. 0 done fi
41836
41837fx_gaussian_blur :
41838  ac "_fx_gaussian_blur $1,$2,$3,$4",$5,$6
41839
41840fx_gaussian_blur_preview :
41841  gui_split_preview "fx_gaussian_blur $*",${-3--1}
41842
41843#@gui Blur [Glow] : fx_glow, fx_glow_preview(0)
41844#@gui : Amplitude = float(6,0,20)
41845#@gui : sep = separator()
41846#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41847#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41848#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41849#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41850#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41851#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41852#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41853#@gui : Value Action = choice("None","Cut","Normalize")
41854#@gui : sep = separator()
41855#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41856#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41857#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41858#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41859#@gui : sep = separator()
41860#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41861fx_glow :
41862  ac "glow $1",$2,$3
41863
41864fx_glow_preview :
41865  gui_split_preview "fx_glow $*",${-3--1}
41866
41867#@gui Blur [Linear] : fx_blur_linear, fx_blur_linear_preview(1)
41868#@gui : Tangent Radius = float(10,0,100)
41869#@gui : Orthogonal Radius = float(0.5,0,100)
41870#@gui : Angle = float(0,0,180)
41871#@gui : Sharpness = float(0,0,500)
41872#@gui : Boundary = choice(1,"Black","Nearest")
41873#@gui : sep = separator()
41874#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41875#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41876#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41877#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41878#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41879#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41880#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41881#@gui : Value Action = choice("None","Cut","Normalize")
41882#@gui : sep = separator()
41883#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41884#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41885#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41886#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41887#@gui : sep = separator()
41888#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41889fx_blur_linear :
41890  ac "blur_linear $1,{$2*$1/100},$3,$5 sharpen $4",$6,$7
41891
41892fx_blur_linear_preview :
41893  gui_split_preview "fx_blur_linear $*",${-3--1}
41894
41895#@gui Blur [Multidirectional] : fx_blur_multidirectional, fx_blur_multidirectional_preview(0)
41896#@gui : Number of Orientations = int(5,1,16)
41897#@gui : Reference Angle (deg.) = float(0,0,360)
41898#@gui : Angle Range (deg.) = float(360,0,360)
41899#@gui : sep = separator()
41900#@gui : Smoothness = float(150,0,1024)
41901#@gui : Kernel type = choice(0,"Mono-Directional","Bi-Directional")
41902#@gui : Boundary conditions = choice(1,"Dirichlet","Neumann","Periodic","Mirror")
41903#@gui : Sharpness = float(0,0,1000)
41904#@gui : Blend Mode = choice{2,"Min","Max","Average","Edges-0.5 (beware: memory-consuming!)",
41905#@gui : "Edges-1 (beware: memory-consuming!)","Edges-2 (beware: memory-consuming!)",
41906#@gui : "Median (beware: memory-consuming!)"}
41907#@gui : Boost Contrast = float(2,0,32)
41908#@gui : sep = separator()
41909#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41910#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41911#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41912#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41913#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41914#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41915#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41916#@gui : sep = separator()
41917#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41918#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41919#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41920#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41921#@gui : sep = separator()
41922#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/09/11</i>.</small>")
41923_fx_blur_multidirectional :
41924  nb_orientations,\
41925  angle_ref,\
41926  angle_range,\
41927  smoothness,\
41928  kernel,\
41929  boundary_conditions,\
41930  sharpness,\
41931  blend_mode,\
41932  contrast=${1-9}
41933  repeat $! l[$>]
41934    # If no median blend, blend step by step in an accumulator to reduce memory usage.
41935    if $blend_mode<3 +f {$blend_mode?0:inf} fi
41936
41937    repeat $nb_orientations
41938      angle={$angle_ref+$angle_range*($>/$nb_orientations-0.5)}
41939
41940      # Kernel definition.
41941      $smoothness,1 gaussian. 20%,0.1
41942      if !$kernel f. "x>w/2?i:0" fi
41943      rotate. $angle,1 # /. {is}
41944
41945      # Blur and blend.
41946      +convolve_fft[0] .,$boundary_conditions rm.. n. 0,255 sharpen. $sharpness
41947      if $blend_mode<3 ${arg\ 1+$blend_mode,min,max,+}[1,-1] fi
41948    done
41949    rm[0]
41950
41951    if $blend_mode==6 blend_median
41952    elif $blend_mode>2 blend_edges {arg($blend_mode-2,0.5,1,2)}
41953    fi
41954
41955    n 0,255 ac "normalize_local "$contrast,hsl_l
41956  endl done
41957
41958fx_blur_multidirectional :
41959  ac "_fx_blur_multidirectional ${1-9}",$10
41960
41961fx_blur_multidirectional_preview :
41962  gui_split_preview "fx_blur_multidirectional $*",${-3--1}
41963
41964#@gui Blur [Radial] : fx_blur_radial, fx_blur_radial_preview(1)
41965#@gui : Amplitude = float(3,0,20)
41966#@gui : Center (%) = point(50,50,0,1)
41967#@gui : Sharpness = float(0,0,500)
41968#@gui : Preview Guides = bool(1)
41969#@gui : sep = separator()
41970#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41971#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41972#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41973#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41974#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41975#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41976#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41977#@gui : Value Action = choice("None","Cut","Normalize")
41978#@gui : sep = separator()
41979#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/16/01</i>.</small>")
41980fx_blur_radial :
41981  ac "blur_radial $1%,$2%,$3% sharpen $4",$6,$7
41982
41983fx_blur_radial_preview :
41984  fx_blur_radial $*
41985  if $5
41986    line 0,$3%,100%,$3%,0.5,0xF0F0F0F0,255 line 0,$3%,100%,$3%,0.5,0x0F0F0F0F,0
41987    line $2%,0,$2%,100%,0.5,0xF0F0F0F0,255 line $2%,0,$2%,100%,0.5,0x0F0F0F0F,0
41988  fi
41989
41990#@gui Chromatic Aberrations : fx_chromatic_aberrations, fx_chromatic_aberrations_preview(0)
41991#@gui : Primary Color = color(255,0,0)
41992#@gui : X-Shift = float(2,-16,16)
41993#@gui : Y-Shift = float(2,-16,16)
41994#@gui : sep = separator()
41995#@gui : Secondary Color = color(0,255,0)
41996#@gui : X-Shift (px) = float(0,-16,16)
41997#@gui : Y-Shift (px) = float(0,-16,16)
41998#@gui : sep = separator()
41999#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42000#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42001#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42002#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42003#@gui : sep = separator()
42004#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/05/07</i>.</small>")
42005fx_chromatic_aberrations :
42006
42007  # Compute orthonormal color basis.
42008  l[]
42009    (${1-3}) (${6-8}) y c orientation[0]
42010    +*[0,1] +*[0] {is} -[1,3] rm[2] orientation[1]
42011    _cross3d {0,^},{^} y x a y
42012    M={^} transpose Minv={^} rm
42013  endl
42014
42015  # Shift images.
42016  repeat $! l[$>] to_color split_opacity l[0]
42017    mix_rgb $M
42018    s c
42019    100%,100%,1,2 fc. {($9-$4)/2},{($10-$5)/2} warp[0] .,1,2,1 rm.
42020    100%,100%,1,2 fc. {($4-$9)/2},{($5-$10)/2} warp[1] .,1,2,1 rm.
42021    100%,100%,1,2 fc. {(-$4-$9)/2},{(-$5-$10)/2} warp[2] .,1,2,1 rm.
42022    a c
42023    mix_rgb $Minv
42024  endl a c endl done
42025  c 0,255
42026
42027fx_chromatic_aberrations_preview :
42028  gui_split_preview "fx_chromatic_aberrations $*",${-3--1}
42029
42030#@gui Dirty : fx_dirty, fx_dirty_preview(0)
42031#@gui : Amplitude = float(30,0,100)
42032#@gui : Monochrome = bool(1)
42033#@gui : sep = separator()
42034#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42035#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42036#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42037#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42038#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42039#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42040#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42041#@gui : Value Action = choice("None","Cut","Normalize")
42042#@gui : sep = separator()
42043#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42044#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42045#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42046#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42047#@gui : sep = separator()
42048#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/24/11</i>.</small>")
42049fx_dirty :
42050  ac "_fx_dirty ${1-2}",$3,$4
42051
42052fx_dirty_preview :
42053  gui_split_preview "fx_dirty ${1--2}",${-3--1}
42054
42055_fx_dirty :
42056  repeat $! l[$>]
42057    dct 100%,100%,1,{if($2,1,s)} noise. $1,2
42058    ==. 0 point. 0,0,0,1,1
42059    * idct c 0,255
42060  endl done
42061
42062#@gui Flip & Rotate Blocs : fx_flip_blocs,fx_flip_blocs_preview(0)
42063#@gui : X-Size (px) = int(4,1,128)
42064#@gui : Y-Size (px) = int(4,1,128)
42065#@gui : Flip = choice(3,"None","X-axis","Y-axis","XY-axes")
42066#@gui : Rotate = choice(1,"-90 deg.","0 deg.","90 deg.")
42067#@gui : sep = separator()
42068#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42069#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42070#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42071#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42072#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42073#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42074#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42075#@gui : sep = separator()
42076#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42077#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42078#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42079#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42080#@gui : sep = separator()
42081#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/01/09</i>.</small>")
42082fx_flip_blocs :
42083  repeat $! l[$>]
42084    if $5 ac "_fx_flip_blocs ${1-4}",$5
42085    else _fx_flip_blocs ${1-4}
42086    fi
42087  endl done
42088
42089_fx_flip_blocs :
42090  if ($3%2)" && "$1>1 s x,-$1 mirror x a x fi
42091  if ($3>1)" && "$2>1 s y,-$2 mirror y a y fi
42092  if $4!=1" && "$1>1" && "$2>1
42093    s y,-$2 N=$!
42094    s x,-$1 M={$!/$N}
42095    ap "rotate {($4-1)*90}"
42096    append_tiles $M,$N
42097  fi
42098
42099fx_flip_blocs_preview :
42100  gui_split_preview "fx_flip_blocs $*",${-3--1}
42101
42102#@gui JPEG Artefacts : fx_jpeg_artefacts, fx_jpeg_artefacts_preview(0)
42103#@gui : note = note("<small>This filter simulates the JPEG compression artifacts,
42104#@gui : using DCT quantization on 8x8 blocs.</small>")
42105#@gui : Quality (%) = int(50,1,100)
42106#@gui : sep = separator()
42107#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42108#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42109#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42110#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42111#@gui : sep = separator()
42112#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/05/07</i>.</small>")
42113fx_jpeg_artefacts :
42114
42115  # Input all 8x8 DCT and iDCT kernels.
42116  # (Generated with: l[] 8,8,64,1,"y*w+x==z" s z +ap idct a[-64--1] z ap[^-1] dct a[0--2] z nm idct,dct endl)
42117  base642img[] \
42118"MSBmbG9hdCBsaXR0bGVfZW5kaWFuCjggOCA2NCAxICMyMjIyCnic5Vs9q51FEC60kKtWMYpaCirYKCoI767iR6UoSangtdM/EAM2Fgab2FhaWdgIUXO"\
42119"qVO8sXEHBVDFia6kgCBaCQSyu+5w8c3nv5p55Bg+3uSmGs19nP2Z2Zt752P39/bJ/G8PHF5+qD1y4v77+yNWyeulegPWy9bbW+9pJ7z9XH6s/f3KxdL"\
42120"BeRr2hjDb0nfR+4OZyxwlw8UbHS4f1nehtBTg66f3kAyOU261OfDTiA/ehEV91ga8T208+cDlg5JNKPiknvR8ykfKwLuRlo7y0k96PegS8MxsB80RAn"\
42121"ROBRYC9R0AZtxF45o1w+cXz9fHvztTnP7y7nv7z5dLBern1NvTht37/75P17Qt/lOsPPQqwXrbeth6D/6Hv3F2fl2/++WXqMPfyegzmwZz439e9755f"\
42122"HwbMvTxjHqwFQBlt6MMYjMV/8F/MgbkwJ+bGGlgLa2Jt7AF7wRjMg/+hD3vG3nmGxr0Y5/S+euPGe/XVJ/ZKB+vlBkAZbeh77ctX6re/f1E6WC83AMp"\
42123"oQ9+Zd++oV6+8WTpYLzcAymhD30+fPlee/e2zqcPcywZAGW3oO6J+aPw437jeuJ9xv+N5xvMCD/d1vABPL3QcdVjjpbcV4rb+2PGK8jsdxx3QjrZCvJ"\
42124"evOl2A3/c7jTqs8dvbJtKkgK7A/eU+rsMEGoDOpJehjDb0YQzG4j+kpWEuzIm5sQbWwpqkc8NesCfsDXvEXrFn3oGGs+BMOBvOiLPizPgP+fzgW3Gsu"\
42125"87wb4mxTj53fixjfeDxo+qb2suC/2+Z1+ub9uX1TefyOundSG/HlxFfjfRupHclvo34dno30tvpNZNeRnob6V1I75n0nknvQnr7fZl4XybSu5Lexvs2"\
42126"8b45vSvp7fe18L4W0ruS3n7fC+97JV808oWRLyr5ppCvGvnKyFeVfFfIl418aeTLSr4t5GsjX8/ka+f76Yj6ofHjfON6437G/Y7nGc/rMtZlJfVBoz5"\
42127"weXrAS9QHjfrAKI+N8tj1gVEfuDyfKc+N+sCoD2bqg4n6oPia1AeuTwr1SaU+KNQHE/VRoT5yfVBd9lCfVeoz1wfVdZPbSJvAv6ECsAjc5vq/oOZX+1"\
42128"Pncx3nusp1zkJ3lL1+p37gXcad7GXb490CoIw212MYi//4XcNcmNPvNdbCmn4vo341v9qfOt8HH71V/7rzSulgvYx6Qxlt3vfg36cBxnZDGW3+v6F+a"\
42129"Pw4n1pv7Ffzq/2p9YCb6x0nwMXZjpcOa7z3trUcAW6f6TgFLv07BDjvbRNwDEAZbf5dgrH4zx5lEebCnJgba2AtrOlyK+pX86v9qfO5LeQ20lgfeHzk"\
42130"+RK0L30ut8ybrav51f7UOsRHIz6cHkZ6NOLTiM9Cesykh5EehfQw0mMiPQrpWUnPRnoU0qOqfjW/2p86H/nA5YDzbCWfFPKR85mRz5zvyxH1Q+PH+dR"\
42131"6Y7+aX+1PrUd52yhvG+Vlo7w0ylujvDXKW6O8nSlvC+VtobwulNcT5W2lvHV5Xynvi+pX86v9qfMp+x6+ogiUfU0fVAQlAuWfgF8rAuUfUPb9Kdrnl/"\
42132"p32E7/VuswXaJ93vvKKrCvV7Qhdm/aCQf2H77bd29+m69tMpTR5vYfxuI/+O8q8E9g7VP0L2BP2Bv2eIn+hVP85o/8A9QxZW9h37ucQd+1fkee7veow"\
42133"9TLBYAy2q5Rr0T29dmF/U55d2C/uz4a6ofGK//EuJ9xv+N5xvMu7XvgfHew74Ez4BV4Bo6BW+B1h/a529erhX29WtjXmBPf9+g732ncYU1XfN9jLQDK"\
42134"aEMfxmAs/rNL2w5zrRb+idXCP4E9YC/YE/aGPWKvO/QvLP0DXO+Qf0DZ9+Rz5ycb68q+Hnh85HkL2m3B/xvt+E378rryD+wG9j1wRXqXnYV9zvuAvsl"\
42135"5dLSvSa9KelfS20jvifQupHcjvQvvy7z87+oI/4TLFtLbdhb+Bd4H3Nd5N/APYL29hb3s37P+nYA+8lUhX03kKyPfzcq+9m+Qswv7fcH3dkT90Hjlnx"\
42136"j3M+53PM94XmXfU55OlKeF+qBQH0xL+xo0WQ32NeV5oTx3fVDcN+Q+HuqDRn1g1Afz0j+BuVeDf4L6aKY+MuoDoz6YlX8gESO3LWHbGP2xrp+InzfGS"\
42137"w58kxjLeEpN9G8bvz/W9RPx823j7dvG7491/UT8fB07Zbx0rZsYT/VvZ9W/bfz+WNdPxMu3jbNvG68/1vUT8XMjvmxBr0J8lkT/tvH7Y10/ET/fNt6+"\
42138"bfz+WNdPxM+N8tS/Pz3uZQt5GvVvG78/1vUT9nccP9f2t8ohCO3zhP8h9A8o/0PC/g7t+4T9HfoXlH2e8D+E/gHlf0jY36F9n7C/Q/+Css8T/ofQP6D"\
42139"8Dwn7O7TvE/Z36F9Q9nnC/xD6B5T/IWF/h/Z9wv4O/QvKPk/4H0L/gPI/JOzv0L5P2N+hf0HZ5wn/Q+gfUP6HhP0d2vcJ+zv0Lyj7POF/CP0Dyv+QsL"\
42140"9D+z5hf4f+BWWfJ/wPoX9A+R8S8XMVo1f5Ayp+r/IHwvnV/tT5EvFzFb9X+QMqfq/yB8L5E/kD4fkS8XMVv1f5Ayp+r/IHVL6Ayh8I10vEz1X8XuUPq"\
42141"Pi9yh8I50/kD4TnS8TPVbxe5Q+o+L3KH1B5Aip/IFwnET9X8XuVP6Di9yp/IJw/kT8Qni8RP1fxe5U/oOL3Kn9A5Quo/IFwvUT8XMXvVf6Ait+r/IFw"\
42142"/kT+QHg+lZ+fyL8P7XvlP1D2u/IPJN4fhO8LVH5+Iv8+tO9V/n3i/UHoH0i8PwjfF6j8/ET+ffg+QPkPEu8Pwvz+xPuD8H2Bys9P5N+H7wNU/n3i/UG"\
42143"Y3594fxC+L1D5+Yn8+9C+V/4DZb8r/0Di/UH4vkDl5yfy78P3ASr/PvH+IMzvT7w/CN8XqPz8RP59+D5A+Q8S7w/C/P7E+4PwfYHKz0/k34f2vcq/T7"\
42144"w/CP0DifcH4fuC/wBiGjbi"
42145
42146  base642img[] \
42147"MSBmbG9hdCBsaXR0bGVfZW5kaWFuCjggOCA2NCAxICMyOTE3CnicjVsxjybFEV0JB4iDyHdYhhAJIznBAiSS6ZbBkb3YS4glHOI/4ENyQuDTOsAJIRG"\
42148"BEyP7di+6cKo5yZY4J95DmxKChIREYMln5ODo1/OqXd9KU1XZzX37Tc+rqqlX9aq+o6Ojevr+T+rvlh/VH9x6uh716189d79c/un9cue1pwo+O//pu/"\
42149"Xhw9/WF/5+Mv62vnet/vzHn5Qb37xe8D189vs//Lr+4i8/G/d5853H6r+/d7d89sGrBffE9/DZP/734jjjN7e+Lp989efy4Jnny6NHj4o9H9e/NOfj3"\
42150"zgP98T3cP3u4x+V+3ffKufffr7g7/AsOA/3xDU+++F/bpRXvvxwwT3wnHgWnIdrfA+fPfnFswvw4lx87/y1p6Rft/48rT9Pe+O5+4Jnw/PjjO9/8zqu"\
42151"W7dH65hbee8a/m5gw/kPPnhV8D181u3RTt55rOHewI1n+9czz0u/bv1+rX+nvX3rawFePf+sn9+vxZ4PvMANTH/79vO1X0u3R+v2aDcf/0jwPdwbeF/"\
42152"68sO1X0t/VunPI5/efUuAF7iB99oXz679Wro9pNtDbvf7Af8bm73HucDf7VDUH8BfNn8LcONvuh2KxgP+7mTztwA3nrvboWg84B5vb/4W4Ab+boei8Q"\
42153"D89nzgPzPnA//Nzd8DN/B3OywaD8D/6ebvgRv36XZYNB6A//bm7xW4gb/bYdF4AP4723mtx72YeBjPA/w3NrwN55t4GPbAc3624R0+MfEw7IF7Pdjwt"\
42154"v68YuJh2AP49XxgM/Ewzgf+8w1v63Fv42HYA/hf2fAK/s/Ew7AH7v3khlf6fVYTD8MexC/EX4i/8fxK/EL8lfgb8VfiF+KvxN+IvxK/EH8h/kb8lfiF"\
42155"+Avxz/OJfyX+QvxC/JX4V+IvxC/EX4h/Jf6F+IX4C+Nf9DzGvzD+GuN/4mX8C+O/Mf4nXsa/MP4b418UL+NfGP+N8T/PZ/zP8xn/Ey/jf2X8C+N/4mX"\
42156"8r4x/YfyvipfxvzL+xfgb+a8w/1Xmn2L8jfxXmP8q85/1N/JfYf6rzH/V+Bv5rzD/VeY/62/kv8L8N883/kb+W5j/KvOf9Tfy38L8V5j/ivE38t/C/F"\
42157"eY/0b+Z74ddiD/CflHyH9N/U3+a+Q/If819Tf5r5H/hPzX1N/kPyH/Cflvnk/+m+eT/5r6m/wn5L+V/Cfqb/KfkP9W8p+ov8l/K/lvPQr4X/n7+Ap/3"\
42158"yN/R/yLd+k6+eain4/rv/bzX+7PhvMt3xG/EP/I1X/sn93p9v9vtzHsiGvaX2j/ybe0f6P9x5m4F95fvKO0n8B+/+w2OiP/efyv+Rp4Lwx/H5O/I/7F"\
42159"PfE94H2ixx/eseusRz7u8Qe8mu8Y/7MeQvwDr+ZbfK9f126P2u1R+f41zbd8/2Y9hvcPeIEbeG9v70/B9/AZ3p+I/5W/7xn+7niKxkPEv8D/8ebvBc+"\
42160"Pez/BfHyxxb8w/xXmv8L8N84CfubfAtzAj3jSeMDfMf8W5t/K/DvsAvw3N38P3MCPfKjxEPG/8vfxFf5We0T8C/zkn9LjfjHxMOwB/OS/Sv7TeBj2AP"\
42161"4bG95Rd5t4GPbAc5N/K/lX42HYA/jPNryV/KnxMOwR8b/y9/EV/ib+kH+JfyH+lfgL8QvxF+IX4q/E34i/EH8j/kr8jfgL8Tfir8TfiH8hfiH+Qvwt4"\
42162"n/l73uGvy82eww7RPzL+F8UL+N/YfwXxn9RvIz/wvivjP+Jl/FfGP+V8T/xMv4L478y/idexv/C+C8R/xt/I/9N/mb+C/nX+Bv5b2X+E+a/1fhb2P9M"\
42163"PoRfjL8HjzD/NeY/629h/zX5GBiNv0e/w/zXmP8k4n/l7+Mr/E3+k4h/yX9F/U3+W8h/i6132P8K+9/xXpD/qvqb/FfJfwf1Fvvvxv572Jj8V9Xf5L9"\
42164"C/lsi/rd8YftvxNPZ1n9PviN+If5RY0f8HfG/5Uvar9B+o3/P6g979UvE/9pvAS/jZ9YT7L9F8x3jf9ZDiP+IvyP+B17Nl3x/Zj2F9yerP+zVLxH/a/"\
42165"+NeGH+EOYP7b+F+a8w/xXmv2GXiL8j/sezMH+uzJ/C/DnsktUf9uqXiP+Bn/xx0H+rPfB35L9C/tN4GPaI+Dvi/3PTv5M/NR6GPbL6w179EvE/8a8nV"\
42166"/pv4q/EvxC/vg+F+CXi74j/z03/zvqpaD19nfpjRn/Yq18i/tf+W/Ey/teT//ffwvq3sP4trH+HHSL+jvif8b8qXsb/yviXrP6wV79E/G/8Xdg/TD5h"\
42167"/63+FvY/kw+RFyL+jvjf+Hth/zT5FHkhqz/s1S8R/9t6wfbf5D/t94r6m/xXyH9LxN8R/9t6if1zYf88+ves/rBXv0T8H+nnmf5b9WrbfyNXnG/1n6v"\
42168"/R/ODqP6wej31m4P6I+L/SD/P9t94JupXc56B9yfS/6P5QVR/nJr6g/rdnOdo/+fxf6SfZ/tvvBPULwv1yxEPkf4fzQ+i+uPU1B/Ubyv122b4b5f/I/"\
42169"08039Tvz7ov9Uekf4fzQ+i+uOU9cdD1h8mHoY9Iv6P9PNM/835xUH/Tfw6/9jV/6P5QVR/EH8h/nZq6g/khYj/I/08238rXsb/wvgvkf4fzQ+i+uPU1"\
42170"B+c31XO75rWPx7/R/p5tv9m/lutno28EOn/0fwgqj+Mv4Xz26nnq/7j8X+kn2f6b51X2P6b/LdE+n80P4jqDzuv4fz+oP5I8P/kO+IX4l8Nfo//3fl9"\
42171"gv8n3xF/I34x+D3+d/cXEvwvmu8Y/7MeMvHv8b87v0/wf9N8x/if9ZCJf4//3f2FBP8L89/C/FeY/8TkP4//3fl9gv8b819h/qvMf83kP4//3f2FBP9"\
42172"P/Z78p/GwGP7z+N+d3yf4v5H/KvlP46EY/vP4391fSPD/1O9Z/4jWw6b+8fjfnd8n+L+x/qmsf5rWw6b+8fjf3V9I8L+w/l1Y/xbWv2LqX4//3fl9gv"\
42173"8b69/C+rey/m2m/vX4391fSPC/+ntl/zP50PQ/Hv+78/sE/6u/hf3P5EPT/3j87+4vJPh/1jvsf4X972r6X4//3fl9gv9nvcP+t7H/FdP/evzv7i8k9"\
42174"H93fp7Q/6feTf3jgH8T+r+7P5DQ/6febeuHe6wfEvq/Oz9P6P+Tf6l/zXmI0b88/d/dH0jo/031zgtTPxyzfkjo/+78PKH/T/6l/tmof1ajf3r6v7s/"\
42175"kND/G/XPWT9Q/2yqfwb6vzs/T+j/lfp3o/49+dfo357+7+4PJPT/Rv37oH5QeyT0f3d+ntD/K+cfjfOPyb9m/uHp/+7+QEL/b5x/HNQPxB/2/9H8PKH"\
42176"/T/7l/Ktx/lXN/MvT/939gYT+3zj/mvUD519N51+B/u/OzxP6/+Rfzj+nHm7mn57+7+4PJPR/9bdcmPqB+S+c/0fz84T+P+cdnH8f8G9C/3f3BxL6/5"\
42177"x32PqB/Bf2/xF/Wr7b2f9z5/eJ/T+3fsjoD97+QmL/z+XPy3j/z53fJ/b/3Pohqz/s7S8k9v9c/ryM9//c+X1i/8+tH7L6w97+QmL/z+XPy3j/z53fJ"\
42178"/b/3Pohoz94+wuJ/T+XPy/j/T93fp/Y/3Prh4z+4O0vJPb/XP68jPf/3Pl9Yv/PrR+y+sPe/kJi/8/lT+Pvvf0/d36f2P9z64es/rC3v5DY/3P509Y7"\
42179"O/t/7vw+sf/n1g8Z/cHbX4j4P5qfZ/vvPf0+0v+j/YGo/ojmF4nf/7nz82z/vaffR/p/tD8Q1R/R/CLx+z93fp7tv/f0+0j/j/YHovojml8kfv/nzs+"\
42180"z/feefh/p/9H+QFR/RPOLxO//3Pl5tv/e0+8j/T/aH4jqj2h+kfj9nzs/z/bfe/p9pP9H+wNR/RHNLxK//3Pn59n+e0+/j/T/aH8gqj+i+UXi93/u/D"\
42181"zbf+/p95H+H+0PRPVHNL/4DgTd2gQ="
42182  nm[-2,-1] dct,idct
42183
42184  # Input DCT quantization matrix.
42185  (16,11,10,16,24,40,51,61;12,12,14,19,26,58,60,55;14,13,16,24,40,57,69,56;14,17,22,29,51,87,80,62;\
42186   18,22,37,56,68,109,103,77;24,35,55,64,81,104,113,92;49,64,78,87,103,121,120,101;72,92,95,98,112,100,103,99)
42187  f. "const S = $1<50?5000/$1:200-2*$1; max(1,round((S*i+50)/100,1,-1))"
42188  nm. Q
42189
42190  # Process images.
42191  repeat $!-3 l[$>,-3--1]
42192
42193    # Decompose image into luma/chroma
42194    l[0] w,h={[w,h]} r {w+(-w%16)},{h+(-h%16)},1,100%,0,3 rgb2ycbcr s c r[-2,-1] 50%,50%,1,1,2 round. endl
42195
42196    repeat 3
42197      # Compute DCT of image with 8x8 blocs.
42198      [$>]
42199      f[$>] "begin(boundary = 2; ref(vector64(),res));
42200        if (!(x%8) && !(y%8),
42201          ref(crop(x,y,8,8),src);
42202          repeat (8,l, repeat (8,k, off = k + 8*l; res[off] = sum(src*crop(#"$dct",0,0,off,8,8,1))));
42203          draw(#-1,res,x,y,0,0,8,8);
42204        ); i"
42205      round.
42206
42207      # Compute DCT quantization.
42208      +r[Q] {$>,w},100%,1,1,0,2 /.. . round.. *[-2,-1]
42209
42210      # Compute iDCT of 8x8 blocs.
42211      f. "begin(boundary = 2; ref(vector64(),res));
42212        if (!(x%8) && !(y%8),
42213          ref(crop(x,y,8,8),src);
42214          repeat (8,l, repeat (8,k, off = k + 8*l; res[off] = sum(src*crop(#"$idct",0,0,off,8,8,1))));
42215          draw(#"$>",res,x,y,0,0,8,8);
42216        ); i"
42217      rm.
42218      round[$>]
42219    done
42220    l[^3--1] r ${-max_wh},1,1,1 a c ycbcr2rgb round. r $w,$h,1,3,0 endl
42221
42222  endl done
42223  rm[dct,idct,Q]
42224
42225fx_jpeg_artefacts_preview :
42226  gui_split_preview "fx_jpeg_artefacts $*",${-3--1}
42227
42228#@gui Lomo : fx_lomo, fx_lomo_preview(1)
42229#@gui : Vignette Size = float(20,0,100)
42230#@gui : sep = separator()
42231#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42232#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42233#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42234#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42235#@gui : sep = separator()
42236#@gui : note = note("<small>Authors: <i>Jérome Boulanger</i> and <i>David Tschumperlé</i>.
42237#@gui :       Latest Update: <i>2012/06/06</i>.</small>")
42238fx_lomo :
42239  remove_opacity repeat $! l[$>] to_rgb
42240    +gaussian {100-$1}%,{100-$1}% n. 0,1 *
42241    s c
42242    f[0] '255*atan((i-128)/128)'
42243    f[1] '255*tan((i-128)/128)'
42244    f[2] '255*atan((i-128)/255)'
42245    a c
42246    sharpen 1
42247    normalize 0,255
42248  endl done
42249
42250fx_lomo_preview :
42251  gui_split_preview "fx_lomo $*",${-3--1}
42252
42253#@gui Mess with Bits : fx_mess_with_bits, fx_mess_with_bits_preview
42254#@gui : note = note("<small><b>Input processing:</b></small>")
42255#@gui : Pre-Normalize = bool(1)
42256#@gui : Smoothness (%) = float(15,0,100)
42257#@gui : Multiplier = int(1,1,256)
42258#@gui : sep = separator()
42259#@gui : note = note("<small><b>Output processing:</b></small>")
42260#@gui : Reversing = choice{1,"None","Reverse bits","Reverse bytes"}
42261#@gui : Bit Masking (Start) = int(0,0,15)
42262#@gui : Bit Masking (End) = int(15,0,15)
42263#@gui : Opacity (%) = float(100,0,100)
42264#@gui : sep = separator()
42265#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42266#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42267#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42268#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42269#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42270#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42271#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42272#@gui : sep = separator()
42273#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42274#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42275#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42276#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42277#@gui : sep = separator()
42278#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/01/16</i>.</small>")
42279fx_mess_with_bits :
42280  ac "_fx_mess_with_bits ${1-7}",$8
42281
42282_fx_mess_with_bits :
42283  repeat $! +l[$>]
42284    b {($2/100)^2*100}% if $1 n 0,255 fi * $3 round
42285    # -> Here, images are 'ushort'-valued.
42286    if $4==1 f "for (k = res = 0, k<15, ++k, res|=((i>>k)&1)<<(15-k))"
42287    elif $4==2 f "res = ((i>>8)&255) | ((i&255)<<8)"
42288    fi
42289    f "begin( mask = (65535>>(15-max($5,$6))) & (65535<<min($5,$6))); i & mask"
42290    n 0,255
42291  endl j[$>] .,0,0,0,0,{$7%} rm. done
42292
42293fx_mess_with_bits_preview :
42294  gui_split_preview "fx_mess_with_bits $*",${-3--1}
42295
42296#@gui Noise [Additive] : fx_noise, fx_noise_preview(0)
42297#@gui : Amplitude = float(10,0,200)
42298#@gui : Noise Type = choice("Gaussian","Uniform","Salt and Pepper","Poisson")
42299#@gui : sep = separator()
42300#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42301#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42302#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42303#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42304#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42305#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42306#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42307#@gui : Value Action = choice(1,"None","Cut","Normalize")
42308#@gui : sep = separator()
42309#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42310#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42311#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42312#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42313#@gui : sep = separator()
42314#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42315fx_noise :
42316  ac "_fx_noise $1,$2",$3,$4
42317
42318_fx_noise :
42319  repeat $! l[$>] split_opacity l[0] noise $1,$2 endl a c endl done
42320
42321fx_noise_preview :
42322  gui_split_preview "fx_noise $*",${-3--1}
42323
42324#@gui Noise [Perlin] : fx_noise_perlin, fx_noise_perlin_preview(1)
42325#@gui : Random Seed = int(0,0,65536)
42326#@gui : sep = separator()
42327#@gui : note = note("<small><b>1st scale:</b></small>")
42328#@gui : Amplitude = float(100,0,512)
42329#@gui : Scale (%) = float(8,0,100)
42330#@gui : X/Y-Ratio = float(0,-4,4)
42331#@gui : sep = separator()
42332#@gui : note = note("<small><b>2nd scale:</b></small>")
42333#@gui : Amplitude = float(0,0,512)
42334#@gui : Scale (%) = float(4,0,100)
42335#@gui : X/Y-Ratio = float(0,-4,4)
42336#@gui : sep = separator()
42337#@gui : note = note("<small><b>3rd scale:</b></small>")
42338#@gui : Amplitude = float(0,0,512)
42339#@gui : Scale (%) = float(2,0,100)
42340#@gui : X/Y-Ratio = float(0,-4,4)
42341#@gui : sep = separator()
42342#@gui : note = note("<small><b>4th scale:</b></small>")
42343#@gui : Amplitude = float(0,0,512)
42344#@gui : Scale (%) = float(1,0,100)
42345#@gui : X/Y-Ratio = float(0,-4,4)
42346#@gui : sep = separator()
42347#@gui : Channel(s) = choice(2,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42348#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42349#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42350#@gui : "YCbCr [Green chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42351#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42352#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42353#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]")
42354#@gui : sep = separator()
42355#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42356#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42357#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42358#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42359#@gui : sep = separator()
42360#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/01/24</i>.</small>")
42361_fx_noise_perlin :
42362  seedx,seedy={[$1>>8,$1&255]}
42363  repeat $! l[$>]
42364    if $2 .,.,1,. noise_perlin. {r=2^$4;s=max(w,h)*$3%;[max(1,s/r),max(1,s*r)]},1,$seedx,$seedy *. $2 + fi
42365    if $5 .,.,1,. noise_perlin. {r=2^$7;s=max(w,h)*$6%;[max(1,s/r),max(1,s*r)]},1,$seedx,$seedy *. $5 + fi
42366    if $8 .,.,1,. noise_perlin. {r=2^$10;s=max(w,h)*$9%;[max(1,s/r),max(1,s*r)]},1,$seedx,$seedy *. $8 + fi
42367    if $11 .,.,1,. noise_perlin. {r=2^$13;s=max(w,h)*$12%;[max(1,s/r),max(1,s*r)]},1,$seedx,$seedy *. $11 + fi
42368  endl done
42369
42370fx_noise_perlin :
42371  ac "_fx_noise_perlin $*",$-4,1
42372
42373fx_noise_perlin_preview :
42374  gui_split_preview "fx_noise_perlin $*",${-3--1}
42375
42376#@gui Noise [Spread] : fx_spread, fx_spread_preview(0)
42377#@gui : X-Variations = float(4,0,20)
42378#@gui : Y-Variations = float(4,0,20)
42379#@gui : sep = separator()
42380#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42381#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42382#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42383#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42384#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42385#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42386#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42387#@gui : Value Action = choice("None","Cut","Normalize")
42388#@gui : sep = separator()
42389#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42390#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42391#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42392#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42393#@gui : sep = separator()
42394#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42395fx_spread :
42396  ac "spread $1,$2",$3,$4
42397
42398fx_spread_preview :
42399  gui_split_preview "fx_spread $*",${-3--1}
42400
42401#@gui Old-Movie Stripes : fx_stripes_y, fx_stripes_y_preview(1)
42402#@gui : Frequency = float(10,0,100)
42403#@gui : sep = separator()
42404#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42405#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42406#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42407#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42408#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42409#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42410#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42411#@gui : Value Action = choice("None","Cut","Normalize")
42412#@gui : sep = separator()
42413#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42414#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42415#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42416#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42417#@gui : sep = separator()
42418#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42419fx_stripes_y :
42420  ac "stripes_y $1",$2,$3
42421
42422fx_stripes_y_preview :
42423  gui_split_preview "fx_stripes_y $*",${-3--1}
42424
42425#@gui Oldschool 8bits : fx_8bits, fx_8bits_preview(0)
42426#@gui : Scale = float(25,1,100)
42427#@gui : Dithering = float(800,0,10000)
42428#@gui : Levels = int(16,2,256)
42429#@gui : sep = separator()
42430#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42431#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42432#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42433#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42434#@gui : sep = separator()
42435#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/02/11</i>.</small>")
42436fx_8bits :
42437  remove_opacity repeat $! l[$>]
42438    w={w} h={h}
42439    r $1%,$1%,1,100%,2
42440    +luminance sharpen. $2 otsu. 256 blend[-2,-1] shapeaverage0
42441    l. s c quantize $3,1,1 a c endl
42442    r. $w,$h,1,100%,1
42443  endl done
42444
42445fx_8bits_preview :
42446  gui_split_preview "fx_8bits $*",${-3--1}
42447
42448#@gui Pixel Sort : fx_pixelsort, fx_pixelsort_preview(1)+
42449#@gui : note = note{"<small><b>Sorting parameters:</b></small>"}
42450#@gui : Order = choice(1,"Decreasing","Increasing")
42451#@gui : Axis = choice("X-axis","Y-axis","X-axis Then Y-axis","Y-axis Then X-axis")
42452#@gui : Sorting Criterion = choice("Red","Green","Blue","Intensity","Luminance","Lightness","Hue",
42453#@gui : "Saturation","Minimum","Maximum","Random")
42454#@gui : sep = separator()
42455#@gui : note = note{"<small><b>Masking parameters:</b></small>"}
42456#@gui : Mask By = choice(1,"Bottom Layer","Criterion","Contours","Random")
42457#@gui : Lower Mask Threshold (%) = float(0,0,100)
42458#@gui : Higher Mask Threshold (%) = float(100,0,100)
42459#@gui : Mask Smoothness (%) = float(0,0,5)
42460#@gui : Invert Mask = bool(0)
42461#@gui : Preview Mask = bool(0)
42462#@gui : sep = separator()
42463#@gui : note = note{"<small><b>Note:</b>
42464#@gui : This filter implements one version of the algorithm described here :
42465#@gui : </small>"}
42466#@gui : url = link("http://satyarth.me/articles/pixel-sorting/")
42467#@gui : sep = separator()
42468#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/05/09</i>.</small>")
42469fx_pixelsort :
42470  _fx_pixelsort ${1-8},0
42471
42472_fx_pixelsort :
42473  repeat $!-($4==0) l[$>]
42474    if $3==0 +to_rgb channels. 0             # Red
42475    elif $3==1 +to_rgb channels. 1           # Green
42476    elif $3==2 +to_rgb channels. 2           # Blue
42477    elif $3==3 +compose_channels +           # Intensity
42478    elif $3==4 +luminance                    # Luminance
42479    elif $3==5 +to_rgb rgb2hsl. channels. 2  # Lightness
42480    elif $3==6 +to_rgb rgb2hsl. channels. 0  # Hue
42481    elif $3==7 +to_rgb rgb2hsl. channels. 1  # Saturation
42482    elif $3==8 +compose_channels min         # Minimum
42483    elif $3==9 +compose_channels max         # Maximum
42484    else 100%,100%,1,1 rand. 0,100           # Random
42485    fi
42486    if $4==0 pass. 0 norm.
42487    elif $4==1 .
42488    elif $4==2 +gradient_norm[0]
42489    else +rand. 0,100
42490    fi
42491    b. $7% ir. $5%,{$6+0.01}%
42492    if $8 ==. 0 fi
42493    if $9 k. * 255
42494    else pixelsort[0] {`$1?_'+':_'-'`},{`$2==0?'x':$2==1?'y':$2==2?'xy':'yx'`},[1],[2] k[0]
42495    fi
42496  endl done
42497
42498fx_pixelsort_preview :
42499  _fx_pixelsort $*
42500
42501#@gui Rain & Snow : fx_rain, fx_rain_preview(0)
42502#@gui : Angle = float(65,-180,180)
42503#@gui : Speed = float(10,0,50)
42504#@gui : Density (%) = float(50,0,100)
42505#@gui : Radius = float(0.1,0,3)
42506#@gui : Gamma = float(1,0,2)
42507#@gui : Opacity = float(1,0,1)
42508#@gui : sep = separator()
42509#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42510#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42511#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42512#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42513#@gui : sep = separator()
42514#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/29/06</i>.</small>")
42515fx_rain :
42516  repeat $! l[$<] nm=${-gui_layer_name}
42517    100%,100% l.
42518      noise 300 c 0,255 b 1,0
42519      c {100-$3}%,100%
42520      +>= 40% blend shapeaverage0
42521      blur_linear $2,$4,$1
42522      max n 0,255 apply_gamma $5
42523      nm name($nm),mode(screen),opacity({$6*100})
42524    endl
42525    rv
42526  endl done
42527
42528fx_rain_preview :
42529  gui_split_preview "repeat $! l[$>] fx_rain $* rv blend screen,$6 endl done",${-3--1}
42530
42531#@gui Random Shade Stripes : fx_shade_stripes, fx_shade_stripes_preview(1)
42532#@gui : Frequency = float(30,1,100)
42533#@gui : Orientation = choice(1,"Horizontal","Vertical")
42534#@gui : Darkness = float(0.8,0,3)
42535#@gui : Lightness = float(1.3,0,3)
42536#@gui : sep = separator()
42537#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42538#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42539#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42540#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42541#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42542#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42543#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42544#@gui : Value Action = choice("None","Cut","Normalize")
42545#@gui : sep = separator()
42546#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42547#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42548#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42549#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42550#@gui : sep = separator()
42551#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42552fx_shade_stripes :
42553  ac "shade_stripes $1,$2,$3,$4",$5,$6
42554
42555fx_shade_stripes_preview :
42556  gui_split_preview "fx_shade_stripes $*",${-3--1}
42557
42558#@gui Rebuild From Similar Blocs : fx_rebuild_from_similar_blocs, fx_rebuild_from_similar_blocs(1)
42559#@gui : Bloc Size (%) = float(5,2,50)
42560#@gui : sep = separator()
42561#@gui : Regularization factor = float(10,0,20)
42562#@gui : Luminance factor = float(0.75,0,3)
42563#@gui : Norm type = choice(1,"L1","L2")
42564#@gui : sep = separator()
42565#@gui : note = note{"This filter subdivides the image into blocs, and replace each bloc by the most similar bloc
42566#@gui : found in the other blocs."}
42567#@gui : sep = separator()
42568#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/09/17</i>.</small>")
42569fx_rebuild_from_similar_blocs :
42570  repeat $! l[$>] split_opacity l[0] to_rgb
42571    w0,h0,S={[w,h,round(min(w,h)*$1%)]} # S: Bloc size
42572
42573    # Split image into regular blocs.
42574    r {ceil([w,h]/$S)*$S},1,100%,0,3,0.5,0.5
42575    M,N={[w,h]/$S}
42576
42577    s yx,-$S a z # Original RGB blocs
42578#    +mirror x +mirror y a z # + all xy mirrors
42579#    repeat 3 +rotate. 90 done a z # + all 90° rotations
42580    +l b xy,$2% rgb2ycbcr sh. 0 *. $3 rm. endl # Blocs to compare, in YCbCr space
42581
42582    # For each bloc, find most similar bloc.
42583    $M,$N,1,1,-1
42584    f. ":
42585      i<0?(
42586        ind = x + y*w;
42587        ref(crop(#1,0,0,ind,w#1,h#1,1),S);
42588        kmin = 0;
42589        dmin = inf;
42590        repeat (wh,k, k!=ind?(
42591          d = norm"{1+$4}"(S - crop(#1,0,0,k,w#1,h#1,1));
42592          d<dmin?(dmin = d; kmin = k);
42593        ));
42594        xt = kmin%w;
42595        yt = int(kmin/w);
42596        i(xt,yt) = ind;
42597        kmin;
42598      ):i"
42599
42600    # Reconstruct image.
42601    {[$M,$N]*$S},1,{0,s}
42602    eval.. "draw(#-1,crop(#0,0,0,i,w#0,h#0,1),x*w#0,y*h#0,0,0,w#0,h#0)"
42603    k. r $w0,$h0,1,100%,0,0,0.5,0.5
42604  endl a c endl done
42605
42606#@gui Scanlines : fx_scanlines, fx_scanlines_preview(0)
42607#@gui : Amplitude = float(60,0,255)
42608#@gui : Bandwidth = float(2,1,300)
42609#@gui : Shape = choice(0,"Bloc","Triangle","Sine","Sine+","Random")
42610#@gui : Angle = float(0,0,360)
42611#@gui : Offset = float(0,0,500)
42612#@gui : sep = separator()
42613#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42614#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42615#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42616#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42617#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42618#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42619#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42620#@gui : Value Action = choice("None","Cut","Normalize")
42621#@gui : sep = separator()
42622#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42623#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42624#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42625#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42626#@gui : sep = separator()
42627#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/19/11</i>.</small>")
42628fx_scanlines :
42629  ac "scanlines ${1-5}",$6,$7
42630
42631fx_scanlines_preview :
42632  gui_split_preview "fx_scanlines $*",${-3--1}
42633
42634#@gui Self Glitching : fx_self_glitching, fx_self_glitching_preview(1)
42635#@gui : Multiplier = float(0,-5,5)
42636#@gui : Bias = float(0,-255,255)
42637#@gui : Negate = bool(0)
42638#@gui : Operator = choice("Add","Mul","And","Or","Xor","Pow","Reverse Pow","Mod","Reverse Mod")
42639#@gui : Shift Point = point(50,50,0,1)
42640#@gui : Boundary = choice(3,"Zero","Nearest","Periodic","Mirror")
42641#@gui : sep = separator()
42642#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42643#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42644#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42645#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42646#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42647#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42648#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42649#@gui : sep = separator()
42650#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42651#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42652#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42653#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42654#@gui : sep = separator()
42655#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/08/19</i>.</small>")
42656fx_self_glitching :
42657  ac "_fx_self_glitching ${1-7}",$8
42658
42659_fx_self_glitching :
42660  f "begin(
42661       shift = ([w,h]-1)*(50-[$5,$6])%;
42662       const sign = $3?-1:1;
42663       const boundary = $7;
42664     );
42665     val = sign*((2^$1)*j(shift) + $2);
42666     ($4==0?(val + i):
42667      $4==1?(val * i):
42668      $4==2?(val & i):
42669      $4==3?(val | i):
42670      $4==4?xor(val,i):
42671      $4==5?(val^i):
42672      $4==6?(i^val):
42673      $4==7?(val%i):
42674      (i%val)
42675     )%256;
42676  "
42677
42678fx_self_glitching_preview :
42679  gui_split_preview "fx_self_glitching $*",${-3--1}
42680
42681#@gui Streak : fx_streak,fx_streak(1)
42682#@gui : Mask Color = color(255,0,0,255)
42683#@gui : Step (%) = float(0,0,30)
42684#@gui : Angle = float(0,0,360)
42685#@gui : Propagation = choice(3,"Backward","Forward","Bidirectional [Sharp]","Bidirectional [Smooth]")
42686#@gui : sep = separator()
42687#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/12/22</i>.</small>")
42688fx_streak :
42689  repeat $! l[$>]
42690    to_rgba
42691    if !$4 R,G,B,A=0 else R,G,B,A=${1-4} fi +select_color 0,$R,$G,$B,$A
42692    if $7==3 srgb2rgb.. fi
42693    f.. "
42694      const step = max(1,$5%*min(w,h));
42695      const angle = $6*pi/180;
42696      const dx = step*cos(angle);
42697      const dy = step*sin(angle);
42698      if (!i(#-1),I,
42699        ixf = xf = x; iyf = yf = y; lf = 0;
42700        if ($7>=1, while (i(#-1,ixf=round(xf),iyf=round(yf)), ++lf; xf-=dx; yf-=dy)); # Forward
42701        ixb = xb = x; iyb = yb = y; lb = 0;
42702        if ($7!=1, while (i(#-1,ixb=round(xb),iyb=round(yb)), ++lb; xb+=dx; yb+=dy)); # Backward
42703        $7==0?I(ixb,iyb):
42704        $7==1?I(ixf,iyf):
42705        $7==2?(lf<lb?I(ixf,iyf):I(ixb,iyb)):
42706        (lb*lb*I(ixf,iyf) + lf*lf*I(ixb,iyb))/(lb^2+lf^2);
42707      )"
42708    if $7==3 rgb2srgb.. fi
42709    rm.
42710  endl done
42711
42712#@gui Visible Watermark : fx_watermark_visible, fx_watermark_visible(0)
42713#@gui : Text = text{"\\251 G'MIC"}
42714#@gui : Opacity = float(0.4,0.1,0.9)
42715#@gui : Size = int(50,13,128)
42716#@gui : Angle = float(25,0,360)
42717#@gui : Smoothness = float(0.5,0,5)
42718#@gui : Lightness = choice(1,"Darker","Brighter")
42719#@gui : sep = separator()
42720#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42721fx_watermark_visible : skip "${1= }"
42722  watermark_visible "$1",$2,$3,$4,$6,$5
42723
42724#@gui Warp by Intensity : fx_warp_by_intensity, fx_warp_by_intensity_preview(0)
42725#@gui : X-Factor = float(0.04,-6,6)
42726#@gui : Y-Factor = float(0.04,-6,6)
42727#@gui : sep = separator()
42728#@gui : X-Offset = float(128,0,255)
42729#@gui : Y-Offset = float(128,0,255)
42730#@gui : sep = separator()
42731#@gui : Correlated Channels = bool(0)
42732#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
42733#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42734#@gui : sep = separator()
42735#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42736#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42737#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42738#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42739#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42740#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42741#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42742#@gui : sep = separator()
42743#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42744#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42745#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42746#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42747#@gui : sep = separator()
42748#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/02/09</i>.</small>")
42749fx_warp_by_intensity :
42750  if !$7 to_a fi
42751  ac "_fx_warp_by_intensity ${1-7}",$8
42752
42753_fx_warp_by_intensity :
42754  if $5 f "ni = norm2(R,G,B); J((ni-$3)*$1,(ni-$4)*$2,0,$6,$7)"
42755  else f "j((i-$3)*$1,(i-$4)*$2,0,0,$6,$7)"
42756  fi
42757
42758fx_warp_by_intensity_preview :
42759  gui_split_preview "fx_warp_by_intensity $*",${-3--1}
42760
42761#@gui ____<b>Details</b>
42762#------------------------
42763
42764#@gui Details Equalizer : fx_equalize_details, fx_equalize_details_preview(0)
42765#@gui : Base Scale = float(5,0,15)
42766#@gui : Detail Scale = float(0.5,0,5)
42767#@gui : note = note("<small><b>Coarse scale:</b></small>")
42768#@gui : Threshold = float(0,0,10)
42769#@gui : Smoothness = float(0,0,10)
42770#@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion")
42771#@gui : Gain = float(0,-4,4)
42772#@gui : sep = separator()
42773#@gui : note = note("<small><b>Medium scale:</b></small>")
42774#@gui : Threshold = float(0,0,10)
42775#@gui : Smoothness = float(0,0,10)
42776#@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion")
42777#@gui : Gain = float(0,-4,4)
42778#@gui : sep = separator()
42779#@gui : note = note("<small><b>Small scale:</b></small>")
42780#@gui : Threshold = float(0,0,10)
42781#@gui : Smoothness = float(0,0,10)
42782#@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion")
42783#@gui : Gain = float(0,-4,4)
42784#@gui : sep = separator()
42785#@gui : note = note("<small><b>Fine scale:</b></small>")
42786#@gui : Threshold = float(0,0,10)
42787#@gui : Smoothness = float(0,0,10)
42788#@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion")
42789#@gui : Gain = float(0,-4,4)
42790#@gui : sep = separator()
42791#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42792#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42793#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42794#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42795#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42796#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42797#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42798#@gui : Value Action = choice("None","Cut","Normalize")
42799#@gui : sep = separator()
42800#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
42801#@gui : "Sixteen Threads"), Spatial Overlap = int(32,0,256)
42802#@gui : sep = separator()
42803#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42804#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42805#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42806#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42807#@gui : sep = separator()
42808#@gui : note = note("<small>Author: <i>Jérome Boulanger</i> and <i>David Tschumperlé</i>.
42809#@gui :       Latest Update: <i>2015/11/11</i>.</small>")
42810_fx_equalize_details :
42811  repeat $! l[$>]
42812    split_details 5,{max(0.1,$1)},{max(0.1,$2)}
42813    __fx_equalize_details[1] ${3-6},8
42814    __fx_equalize_details[2] ${7-10},4
42815    __fx_equalize_details[3] ${11-14},2
42816    __fx_equalize_details[4] ${15-18},1
42817    + c 0,255
42818  endl done
42819
42820__fx_equalize_details :
42821  threshold $1,1
42822  if $3==0 b {$2*$5/2}
42823  elif $3==1
42824    if $2>0
42825      m={im} M={iM} n. 0,255
42826      repeat int($2/5) bilateral 15,{5*$5} done
42827      bilateral 15,{($2%5)*$5}
42828      *. {($M-$m)/255} +. $m
42829    fi
42830  else smooth {$2*50},0.2,0.8,$5,$5 fi
42831  * {10^$4}
42832
42833fx_equalize_details :
42834  ac "gui_parallel_overlap \"_fx_equalize_details ${1-18}\",$21,$22",$19,$20
42835
42836fx_equalize_details_preview :
42837  gui_split_preview "fx_equalize_details $*",${-3--1}
42838
42839#@gui Equalize Local Histograms : fx_equalize_local_histograms, fx_equalize_local_histograms_preview(0)
42840#@gui : Strength (%) = float(75,0,100)
42841#@gui : Mode = choice(2,"Raw","Hard","Soft")
42842#@gui : Radius = int(4,1,16)
42843#@gui : Sigma = float(100,0,256)
42844#@gui : Regularization = float(8,0,32)
42845#@gui : Reduce Halos = bool(1)
42846#@gui : sep = separator()
42847#@gui : Channel(s) = choice(16,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42848#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42849#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42850#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42851#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42852#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42853#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42854#@gui : sep = separator()
42855#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42856#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42857#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42858#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42859#@gui : sep = separator()
42860#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/31</i>.</small>")
42861fx_equalize_local_histograms :
42862  b0="normal" b1="overlay" b2="softlight"
42863  repeat $! l[$>]
42864    +ac "_fx_equalize_local_histograms ${1-6}",$7,1
42865    blend ${b$2},{$1%}
42866  endl done
42867
42868_fx_equalize_local_histograms :
42869  +n 0,511 round.
42870  f. "
42871    begin(
42872      const boundary = 1;
42873      const N = $3;
42874      const sigma = ($6?1:-1)*(0.1+$4);
42875      ref(vector512(),weights);
42876      repeat (size(weights),k, # Pre-compute exponentials
42877        weights[k] = sigma>=0?exp(-sqr(k/sigma)):1 - exp(-sqr(k/sigma))
42878      );
42879    );
42880
42881    ref(vector512(0),bins);
42882
42883    repeat (s,c,
42884      ref(crop(x - N,y - N,0,c,2*N + 1,2*N + 1,1,1),V);
42885      repeat (size(V),k, # Compute local weighted histogram
42886        val = V[k];
42887        diff = abs(val - V[size(V)/2]);
42888        bins[val]+=weights[diff];
42889      );
42890    );
42891
42892    sum = 0;
42893    repeat (size(bins),k,
42894      sum+=bins[k];
42895      bins[k] = sum;
42896    );
42897    bins/=max(1e-5,sum);
42898
42899    P = I;
42900    size(P)==1?(P = bins[P[0]]; 0):
42901    size(P)==2?(P = [ bins[P[0]], bins[P[1]] ]; 0):
42902    size(P)==3?(P = [ bins[P[0]], bins[P[1]], bins[P[2]] ]; 0):
42903    size(P)==4?(P = [ bins[P[0]], bins[P[1]], bins[P[2]], bins[P[3]] ]; 0);
42904    P"
42905  n. 0,255
42906  if $5 norm.. bilateral. ..,$5,{2+$5} fi
42907  k.
42908
42909fx_equalize_local_histograms_preview :
42910  gui_split_preview "fx_equalize_local_histograms $*",${-3--1}
42911
42912#@gui Freaky Details : fx_freaky_details, fx_freaky_details_preview(0)
42913#@gui : Amplitude = int(2,1,5)
42914#@gui : Scale = float(10,0,100)
42915#@gui : Iterations = int(1,1,4)
42916#@gui : sep = separator()
42917#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42918#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42919#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42920#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42921#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42922#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42923#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42924#@gui : sep = separator()
42925#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42926#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42927#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42928#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42929#@gui : sep = separator()
42930#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Patrick David</i>.
42931#@gui :       Latest Update: <i>2013/27/02</i>.</small>")
42932#@gui : sep = separator()
42933#@gui : note = note("This effect has been done following:")
42934#@gui : url = link("This tutorial from Patrick David",
42935#@gui : "https://patdavid.net/2013/02/calvin-hollywood-freaky-details-in-gimp.html")
42936fx_freaky_details :
42937  repeat $! l[$>]
42938    repeat $3
42939      . +-. 255 *. -1
42940      repeat $1 bilateral. $2,{1.5*$2} done
42941      blend[-2,-1] vividlight blend overlay
42942    done
42943  endl done n 0,255
42944
42945fx_freaky_details_preview :
42946  gui_split_preview "fx_freaky_details $*",${-3--1}
42947
42948#@gui Local Normalization : fx_normalize_local, fx_normalize_local_preview(0)
42949#@gui : Amplitude = float(2,0,60)
42950#@gui : Radius = int(6,1,64)
42951#@gui : Neighborhood Smoothness = float(5,0,40)
42952#@gui : Average Smoothness = float(20,0,40)
42953#@gui : Constrain Values = bool(1)
42954#@gui : sep = separator()
42955#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42956#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42957#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42958#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42959#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42960#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42961#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42962#@gui : sep = separator()
42963#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42964#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42965#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42966#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42967#@gui : sep = separator()
42968#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42969fx_normalize_local :
42970  repeat $! l[$>]
42971    ac "normalize_local $1,$2,$3,$4,$5,0,255",$6
42972  endl done
42973
42974fx_normalize_local_preview :
42975  gui_split_preview "fx_normalize_local $*",${-3--1}
42976
42977#@gui Local Processing : fx_local_processing, fx_local_processing_preview(1)
42978#@gui : Action = choice("Normalize","Equalize")
42979#@gui : Strength (%) = float(75,0,100)
42980#@gui : Neighborhood Size (%) = float(10,1,100)
42981#@gui : Overlap (%) = float(50,0,75)
42982#@gui : Regularization (%) = float(20,0,100)
42983#@gui : Process Channels Individually = bool(0)
42984#@gui : sep = separator()
42985#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42986#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42987#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42988#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42989#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42990#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42991#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42992#@gui : sep = separator()
42993#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42994#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42995#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42996#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42997#@gui : sep = separator()
42998#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/02/28</i>.</small>")
42999fx_local_processing :
43000  com0="n 0,255"
43001  com1="equalize 256,0,255 n 0,255"
43002  com=${com$1}
43003  if $6 com="s c "$com" a c" fi
43004  repeat $! l[$>]
43005    size={round(max(8,max(w,h)*$3%))}
43006    +ac "at \""$com"\","$size","$size",1,$4%,$4%",$7
43007    if $5 +norm[0] bilateral[1] .,{$5/20}%,{2+$5/4} rm. fi
43008    blend alpha,{$2%}
43009  endl done
43010
43011fx_local_processing_preview :
43012  gui_split_preview "fx_local_processing $*",${-3--1}
43013
43014#@gui Magic Details : fx_magic_details,fx_magic_details_preview(0)
43015#@gui : Amplitude = float(6,0,30)
43016#@gui : Spatial Scale = float(3,0,10)
43017#@gui : Value Scale = float(15,0,20)
43018#@gui : Edges = float(-0.5,-3,3)
43019#@gui : Smoothness = float(2,0,20)
43020#@gui : sep = separator()
43021#@gui : Channel(s) = choice(27,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43022#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43023#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43024#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43025#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43026#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43027#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]")
43028#@gui : sep = separator()
43029#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43030#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43031#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43032#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43033#@gui : sep = separator()
43034#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/10</i>.</small>")
43035fx_magic_details :
43036  ac "_fx_magic_details ${1-5}",$6,1
43037
43038_fx_magic_details :
43039  repeat $! l[$>]
43040    +bilateral $2,$3
43041    +gradient_norm.. +. 1
43042    pow. {$4>=0?3.1-$4:-3.1-$4}
43043    b. $5 n. 1,{1+$1}
43044    -... .. *[-3,-1] + c 0,255
43045  endl done
43046
43047fx_magic_details_preview :
43048  gui_split_preview "fx_magic_details $*",${-3--1}
43049
43050#@gui Mighty Details : fx_mighty_details, fx_mighty_details_preview(0)
43051#@gui : Amplitude = float(25,0,100)
43052#@gui : Details Amount = float(1,0,2)
43053#@gui : Details Scale = float(25,1,100)
43054#@gui : Details Smoothness = int(1,0,10)
43055#@gui : sep = separator()
43056#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43057#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43058#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43059#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43060#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43061#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43062#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43063#@gui : sep = separator()
43064#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43065#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43066#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43067#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43068#@gui : sep = separator()
43069#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/08/08</i>.</small>")
43070_fx_mighty_details :
43071  +smooth $3,0,1,0.5,0.5 -[1] [0]
43072  +abs. sign.. M={iM} ^. {2-$2} *. {$M/iM} *[-2,-1]
43073  +diffusiontensors[0] 0,1,0.5,0.5
43074  repeat $4 smooth[1] [2],20 done
43075  *[1] {-$1/5} +
43076
43077fx_mighty_details :
43078  ac "_fx_mighty_details ${1-4}",$5,1
43079  n 0,255
43080
43081fx_mighty_details_preview :
43082  gui_split_preview "fx_mighty_details $*",${-3--1}
43083
43084#@gui Sharpen [Deblur] : fx_deblur, fx_deblur_preview(0)
43085#@gui : Radius = float(2,0,20)
43086#@gui : Iterations = int(10,0,100)
43087#@gui : Time Step = float(20,0,50)
43088#@gui : Smoothness = float(0.1,0,10)
43089#@gui : Regularization = choice(1,"Tikhonov","Mean Curvature","Total Variation")
43090#@gui : sep = separator()
43091#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43092#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43093#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43094#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43095#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43096#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43097#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43098#@gui : sep = separator()
43099#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
43100#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
43101#@gui : sep = separator()
43102#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43103#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43104#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43105#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43106#@gui : sep = separator()
43107#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43108fx_deblur :
43109  ac "gui_parallel_overlap \"deblur ${1-5} c 0,255\",$7,$8",$6,1
43110
43111fx_deblur_preview :
43112  gui_split_preview "fx_deblur $*",${-3--1}
43113
43114#@gui Sharpen [Gold-Meinel] : fx_unsharp_goldmeinel, fx_unsharp_goldmeinel_preview(0)
43115#@gui : Sigma = float(1,0.5,10)
43116#@gui : Iterations = int(5,1,15)
43117#@gui : Acceleration = float(1,1,3)
43118#@gui : Blur = choice(1,"Exponential","Gaussian")
43119#@gui : Cut = bool(true)
43120#@gui : sep = separator()
43121#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43122#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43123#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43124#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43125#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43126#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43127#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43128#@gui : sep = separator()
43129#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
43130#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
43131#@gui : sep = separator()
43132#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43133#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43134#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43135#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43136#@gui : sep = separator()
43137#@gui : note = note("<small>Author: <i>Jérôme Boulanger</i>.      Latest Update: <i>2013/29/03</i>.</small>")
43138fx_unsharp_goldmeinel:
43139   ac "gui_parallel_overlap \"_fx_unsharp_goldmeinel $*\",$7,$8",$6,1
43140
43141_fx_unsharp_goldmeinel :
43142  deblur_goldmeinel $*
43143  if $5 c 0,255 else n 0,255 fi
43144
43145fx_unsharp_goldmeinel_preview:
43146  gui_split_preview "fx_unsharp_goldmeinel $*",${-3--1}
43147
43148#@gui Sharpen [Inverse Diffusion] : fx_sharpen_inversediff, fx_sharpen_inversediff_preview(0)
43149#@gui : Amplitude = float(50,1,300)
43150#@gui : Iterations = int(2,1,10)
43151#@gui : sep = separator()
43152#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43153#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43154#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43155#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43156#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43157#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43158#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43159#@gui : sep = separator()
43160#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43161#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43162#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43163#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43164#@gui : sep = separator()
43165#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43166fx_sharpen_inversediff :
43167  ac "repeat $2 sharpen $1 c 0,255 done",$3,1
43168
43169fx_sharpen_inversediff_preview :
43170  gui_split_preview "fx_sharpen_inversediff $*",${-3--1}
43171
43172#@gui Sharpen [Multiscale] : fx_sharpen_multiscale, fx_sharpen_multiscale_preview(0)
43173#@gui : Strength (%) = float(15,0,100)
43174#@gui : Regularity (%) = float(20,0,100)
43175#@gui : sep = separator()
43176#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43177#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43178#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43179#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43180#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43181#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43182#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43183#@gui : sep = separator()
43184#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43185#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43186#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43187#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43188#@gui : sep = separator()
43189#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/01/14</i>.</small>")
43190fx_sharpen_multiscale :
43191  ac "_fx_sharpen_multiscale $1,$2",$3
43192
43193fx_sharpen_multiscale_preview :
43194  gui_split_preview "fx_sharpen_multiscale $*",${-3--1}
43195
43196_fx_sharpen_multiscale :
43197  repeat $! l[$>]
43198    N={max(1,int(log2(min(w,h))-2))}
43199    +l repeat $N +r. 50%,50%,1,100%,2 +r. ..,..,1,100%,5 -[-3,-1] done endl # Decompose
43200    guided[0] 4,100 # Smooth guide image
43201
43202    # Process each scale.
43203    repeat $!-1 l[0,{$>+1}]
43204      +ri[0] [1],2
43205      +equalize.. 1024
43206      bilateral. ..,{2*$2%},100
43207      j[1] .,0,0,0,0,{$1%}
43208      k[0,1]
43209    endl done
43210    rm[0]
43211
43212    repeat $!-1 r. ..,..,1,100%,5 +[-2,-1] done # Recompose
43213    c 0,255
43214  endl done
43215
43216#@gui Sharpen [Octave Sharpening] : fx_unsharp_octave, fx_unsharp_octave_preview(0)
43217#@gui : Scales = int(4,1,10)
43218#@gui : Maximal Radius = float(5,0,20)
43219#@gui : Amount = float(3,0,10)
43220#@gui : Threshold = float(0,0,255)
43221#@gui : sep = separator()
43222#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43223#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43224#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43225#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43226#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43227#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43228#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43229#@gui : sep = separator()
43230#@gui : Parallel Processing = choice(1,"Auto","One Thread","Two Threads","Four Threads","Eight Threads",
43231#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
43232#@gui : sep = separator()
43233#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43234#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43235#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43236#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43237#@gui : sep = separator()
43238#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43239fx_unsharp_octave :
43240  ac "gui_parallel_overlap \"unsharp_octave $1,$2,$3,$4\",$6,$7",$5,1
43241
43242fx_unsharp_octave_preview :
43243  gui_split_preview "fx_unsharp_octave $*",${-3--1}
43244
43245#@gui Sharpen [Richardson-Lucy] : fx_unsharp_richardsonlucy, fx_unsharp_richardsonlucy_preview
43246#@gui : Sigma = float(1,0.5,10)
43247#@gui : Iterations = int(10,1,100)
43248#@gui : Blur = choice(1,"Exponential","Gaussian")
43249#@gui : Cut = bool(true)
43250#@gui : sep = separator()
43251#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43252#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43253#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43254#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43255#@gui : sep = separator()
43256#@gui : note = note("<small>Author: <i>Jérôme Boulanger</i>.      Latest Update: <i>2013/29/03</i>.</small>")
43257fx_unsharp_richardsonlucy :
43258  deblur_richardsonlucy $*
43259  if $4 c 0,255 else n 0,255 fi
43260
43261fx_unsharp_richardsonlucy_preview :
43262  gui_split_preview "fx_unsharp_richardsonlucy $*",${-3--1}
43263
43264#@gui Sharpen [Shock Filters] : fx_sharpen_shock, fx_sharpen_shock_preview(0)
43265#@gui : Amplitude = float(150,1,400)
43266#@gui : Edge Threshold = float(0.1,0,0.7)
43267#@gui : Gradient Smoothness = float(0.8,0,10)
43268#@gui : Tensor Smoothness = float(1.1,0,10)
43269#@gui : Iterations = int(1,1,10)
43270#@gui : sep = separator()
43271#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43272#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43273#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43274#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43275#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43276#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43277#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43278#@gui : sep = separator()
43279#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43280#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43281#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43282#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43283#@gui : sep = separator()
43284#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43285fx_sharpen_shock :
43286  ac "repeat $5 sharpen $1,$2,$3,$4 c 0,255 done",$6,1
43287
43288fx_sharpen_shock_preview :
43289  gui_split_preview "fx_sharpen_shock $*",${-3--1}
43290
43291#@gui Sharpen [Texture] : fx_sharpen_texture, fx_sharpen_texture_preview(0)
43292#@gui : Strength = float(1,0,4)
43293#@gui : Radius = float(4,0,32)
43294#@gui : sep = separator()
43295#@gui : Channel(s) = choice(16,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43296#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43297#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43298#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43299#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43300#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43301#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]")
43302#@gui : sep = separator()
43303#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43304#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43305#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43306#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43307#@gui : sep = separator()
43308#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/09</i>.</small>")
43309fx_sharpen_texture :
43310  ac "_fx_sharpen_texture ${1-2}",$3,1
43311
43312_fx_sharpen_texture :
43313  repeat $! l[$>]
43314    +rolling_guidance $2,5,0.5 -. [0] *. $1 - c 0,255
43315  endl done
43316
43317fx_sharpen_texture_preview :
43318  gui_split_preview "fx_sharpen_texture $*",${-3--1}
43319
43320#@gui Sharpen [Unsharp Mask] : fx_unsharp, fx_unsharp_preview(0)
43321#@gui : Sharpening Type = choice(1,"Gaussian","Bilateral")
43322#@gui : Spatial Radius = float(1.25,0,20)
43323#@gui : Bilateral Radius = float(10,0,60)
43324#@gui : Amount = float(2,0,10)
43325#@gui : Threshold = float(0,0,20)
43326#@gui : Darkness Level = float(1,0,4)
43327#@gui : Lightness Level = float(1,0,4)
43328#@gui : Iterations = int(1,1,10)
43329#@gui : Negative Effect = bool(0)
43330#@gui : sep = separator()
43331#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43332#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43333#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43334#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43335#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43336#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43337#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43338#@gui : sep = separator()
43339#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43340#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43341#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43342#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43343#@gui : note = note{"\n\n<small><b>Note: </b>
43344#@gui : This filter is inspired by the original GIMP <i>Unsharp Mask</i> filter, with additional parameters.
43345#@gui : </small>"}
43346#@gui : sep = separator()
43347#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43348_fx_unsharp :
43349  repeat $! repeat $8
43350    if $1==0 +b. $2 else +bilateral. $2,$3 fi
43351    -. .. *. -$4
43352    +norm. >=. $5% ri. .. *[-2,-1]
43353    if $9 *. -1 fi
43354    +c. 0,100% c.. -100%,0 *.. $6 *. $7 +[-2,-1]
43355    +[-2,-1] c. 0,255
43356  done mv. 0 done
43357
43358fx_unsharp :
43359  ac "_fx_unsharp $1,$2,$3,$4,$5,$6,$7,$8,$9",$10,1
43360
43361fx_unsharp_preview :
43362  gui_split_preview "fx_unsharp $*",${-3--1}
43363
43364#@gui Split Details [Alpha] : fx_split_details_alpha, fx_split_details_alpha_preview(0)
43365#@gui : Number of Levels = int(6,2,8)
43366#@gui : Base Scale = float(10,0,30)
43367#@gui : Details Scale = float(1,0,20)
43368#@gui : Opacity Gain = float(5,1,20)
43369#@gui : sep = separator()
43370#@gui : Preview Without Alpha = bool(0)
43371#@gui : sep = separator()
43372#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/22/04</i>.</small>")
43373fx_split_details_alpha :
43374  remove_opacity
43375  repeat $! l[$<]
43376    repeat $1-1
43377      s={$3+($2-$3)*$>/if($1-2>0,$1-2,1)}
43378      +_fx_split_details_alpha_blur. $s
43379      sub_alpha.. .,$4
43380    done
43381  endl done
43382
43383_fx_split_details_alpha_blur :
43384  if $1>=0.1 b. $1
43385  else
43386    if $1>=0.05 (1,4,7,4,1;4,16,26,16,4;7,26,41,26,7;4,16,26,16,4;1,4,7,4,1)
43387    else (1,2,1;2,4,2;1,2,1) fi
43388    normalize_sum. convolve.. . rm.
43389  fi
43390
43391fx_split_details_alpha_preview :
43392  repeat $! l[$>]
43393    fx_split_details_alpha ${1-4}
43394    if $5 remove_opacity[^-1] else to_rgba. fi
43395    N={int(sqrt($!))} N={round($!/$N,1,1)} r2dy {100/$N}%
43396    repeat $! l[$>] 0 text. "#"{1+$>}" ",1,1,24,1,255 +dilate. 5 to_rgba[1] j[0] [1],2,0,0,0,1,[2],255 k[0] endl done
43397    to_rgba frame 1,1,0 frame 3,3,255 append_tiles ,
43398  endl done
43399
43400#@gui Split Details [Gaussian] : fx_split_details_gaussian, fx_split_details_gaussian_preview(0)
43401#@gui : Number of Scales = int(6,3,12)
43402#@gui : Base Scale = float(10,0,200)
43403#@gui : Details Scale = float(1,0,20)
43404#@gui : sep = separator()
43405#@gui : Sharpen Details in Preview = bool(0)
43406#@gui : sep = separator()
43407#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/22/01</i>.</small>")
43408fx_split_details_gaussian :
43409  remove_opacity repeat $! l[$>]
43410    nm=${-gui_layer_name}
43411    pos=${-gui_layer_pos}
43412    split_details $1,$2,$3
43413    +[^0] 128 c[^0] 0,255 round
43414    repeat $!-1 nm[{1+$>}] "mode(grainmerge), name"($nm" [scale ""#"{1+$>}"]), pos("$pos")" done
43415    nm[0] "name"($nm" [residual]), pos("$pos")"
43416    rv
43417  endl done
43418
43419fx_split_details_gaussian_preview :
43420  repeat $! l[$>]
43421    fx_split_details_gaussian $*
43422    if $4 equalize[^-1] 256 fi n[^-1] 0,255
43423    N={int(sqrt($!))} N={round($!/$N,1,1)} r2dy {100/$N}%
43424    repeat $! l[$>] 0 text. "#"{1+$>}" ",1,1,24,1,255 +dilate. 5 to_rgba[1] j[0] [1],2,0,0,0,1,[2],255 k[0] endl done
43425    to_rgba frame 1,1,0 frame 3,3,255 append_tiles ,
43426  endl done
43427
43428#@gui Split Details [Wavelets] : fx_split_details_wavelets, fx_split_details_wavelets_preview(0)
43429#@gui : Number of Scales = int(6,2,12)
43430#@gui : Add Alpha Channels to Detail Scale Layers = _bool(0)
43431#@gui : sep = separator()
43432#@gui : Sharpen Details in Preview = bool(0)
43433#@gui : sep = separator()
43434#@gui : note = note{"<small><b>Note:</b> This filter decomposes an image into several detail scales,
43435#@gui : using wavelet atrous.
43436#@gui : It should provide similar results to the
43437#@gui : <a href="http://registry.gimp.org/node/11742">Wavelet Decompose Plug-in</a>
43438#@gui : (by Marco Rossini).
43439#@gui : </small>"}
43440#@gui : sep = separator()
43441#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/23/03</i>.</small>")
43442fx_split_details_wavelets :
43443  remove_opacity repeat $! l[$>]
43444    nm=${-gui_layer_name}
43445    pos=${-gui_layer_pos}
43446    split_details $1,0,0 rv +[^-1] 128 c[^-1] 0,255 round
43447    if $2 to_a[^-1] fi
43448    repeat $!-1 nm[$>] "mode(grainmerge), name"($nm" [scale ""#"{1+$>}"]), pos("$pos")" done
43449    nm. "name"($nm" [residual]), pos("$pos")"
43450  endl done
43451
43452fx_split_details_wavelets_preview :
43453  repeat $! l[$>]
43454    fx_split_details_wavelets $1,0
43455    if $3 equalize[^-1] 256 fi n[^-1] 0,255
43456    N={int(sqrt($!))} N={round($!/$N,1,1)} r2dy {100/$N}%
43457    repeat $! l[$>] 0 text. "#"{1+$>}" ",1,1,24,1,255 +dilate. 5 to_rgba[1] j[0] [1],2,0,0,0,1,[2],255 k[0] endl done
43458    to_rgba frame 1,1,0 frame 3,3,255 append_tiles ,
43459  endl done
43460
43461#@gui Tone Mapping : fx_map_tones, fx_map_tones_preview(0)
43462#@gui : Threshold = float(0.5,0,1)
43463#@gui : Gamma = float(0.7,0,1)
43464#@gui : Smoothness = float(0.1,0,10)
43465#@gui : Iterations = int(30,0,500)
43466#@gui : sep = separator()
43467#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43468#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43469#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43470#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43471#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43472#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43473#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43474#@gui : sep = separator()
43475#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43476#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43477#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43478#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43479#@gui : sep = separator()
43480#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43481fx_map_tones :
43482  ac "map_tones ${1-4}",$5,1
43483  n 0,255
43484
43485fx_map_tones_preview :
43486  gui_split_preview "fx_map_tones $*",${-3--1}
43487
43488#@gui Tone Mapping [Fast] : fx_map_tones_fast, fx_map_tones_fast_preview(0)
43489#@gui : Radius = float(3,0,20)
43490#@gui : Power = float(0.5,0,1)
43491#@gui : sep = separator()
43492#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43493#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43494#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43495#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43496#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43497#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43498#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43499#@gui : sep = separator()
43500#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43501#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43502#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43503#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43504#@gui : sep = separator()
43505#@gui : note = note("<small>Authors: <i>Paul Nasca</i> and <i>David Tschumperlé</i>.
43506#@gui :       Latest Update: <i>2011/10/06</i>.</small>")
43507fx_map_tones_fast :
43508  ac "map_tones_fast $1,$2",$3,2
43509
43510fx_map_tones_fast_preview :
43511  gui_split_preview "fx_map_tones_fast ${^0}",${-3--1}
43512
43513#@gui ____<b>Frames</b>
43514#----------------------
43515
43516#@gui Droste : fx_droste, fx_droste_preview(1)
43517#@gui : note = note("<span color=\"red\">Upper-left coordinates :</span>")
43518#@gui : Point #0 = point(20,20,0,1,255,0,0)
43519#@gui : sep = separator()
43520#@gui : note = note("<span color=\"magenta\">Upper-right coordinates :</span>")
43521#@gui : Point #1 = point(80,20,0,1,255,0,255)
43522#@gui : sep = separator()
43523#@gui : note = note("<span color=\"blue\">Lower-right coordinates :</span>")
43524#@gui : Point #2 = point(80,80,0,1,0,128,255)
43525#@gui : sep = separator()
43526#@gui : note = note("<span color=\"cyan\">Lower-left coordinates :</span>")
43527#@gui : Point #3 = point(20,80,0,1,0,255,255)
43528#@gui : sep = separator()
43529#@gui : Iterations = int(1,1,10)
43530#@gui : X-Shift = float(0,-100,100)
43531#@gui : Y-Shift = float(0,-100,100)
43532#@gui : Angle = float(0,0,360)
43533#@gui : Zoom = float(1,0.1,5)
43534#@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes")
43535#@gui : Boundary = choice(1,"Transparent","Nearest","Periodic","Mirror")
43536#@gui : Drawing Mode = choice{"Replace","Replace (Sharpest)","Behind","Below"}
43537#@gui : View Outlines Only = bool(0)
43538#@gui : sep = separator()
43539#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/11/06</i>.</small>")
43540fx_droste :
43541  repeat $!
43542    if $16==1 100%,100%,1,1,'x' 100%,100%,1,1,'y' a[-2,-1] c fi
43543    repeat $9
43544      x0={round($1*w/100)} y0={round($2*h/100)} x1={round($3*w/100)} y1={round($4*h/100)}
43545      x2={round($5*w/100)} y2={round($6*h/100)} x3={round($7*w/100)} y3={round($8*h/100)}
43546      100%,100%,1,2,-32767 polygon. 4,$x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3,1,-65535
43547      sh. 0
43548      f. "if(i==-65535,
43549            x03 = "$x0"+(y-"$y0")/("$y3"-"$y0")*("$x3"-"$x0");
43550            x12 = "$x1"+(y-"$y1")/("$y2"-"$y1")*("$x2"-"$x1");
43551            (x-x03)/(x12-x03)*(w-1),i)"
43552      rm.
43553      sh. 1
43554      f. "if(i==-65535,
43555            y01 = "$y0"+(x-"$x0")/("$x1"-"$x0")*("$y1"-"$y0");
43556            y32 = "$y3"+(x-"$x3")/("$x2"-"$x3")*("$y2"-"$y3");
43557            (y-y01)/(y32-y01)*(h-1),i)"
43558      rm.
43559      xshift={w*$10/100} yshift={h*$11/100} alpha={-$12*pi/180}
43560      ca={cos($alpha)/$13} sa={sin($alpha)/$13} w2={w/2} h2={h/2}
43561      f. 'if(i==-32767,i,X=i(x,y,0,0)-$w2;Y=i(x,y,0,1)-$h2;if(c==0,$w2-$xshift+X*$ca-Y*$sa,$h2-$yshift+X*$sa+Y*$ca))'
43562      if $14==0 sh. 0 f. 'if(i==-32767,x,i)' rm. sh. 1 f. 'if(i==-32767,y,i)' rm.
43563      elif $14==1 sh. 0 f. 'if(i==-32767,x,w-1-i)' rm. sh. 1 f. 'if(i==-32767,y,i)' rm.
43564      elif $14==2 sh. 0 f. 'if(i==-32767,x,i)' rm. sh. 1 f. 'if(i==-32767,y,h-1-i)' rm.
43565      else sh. 0 f. 'if(i==-32767,x,w-1-i)' rm. sh. 1 f. 'if(i==-32767,y,h-1-i)' rm.
43566      fi
43567      if $16<2 warp.. .,0,{$16==0},$15 rm.
43568      else
43569        +warp.. .,0,1,$15 rm..
43570        if $16==3 rv[-2,-1] fi
43571        blend[-2,-1] alpha
43572      fi
43573    done
43574    if $16==1 warp.. .,0,1,1 rm. fi
43575    mv. 0 done
43576
43577fx_droste_preview :
43578  if !$17 fx_droste $* else polygon 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,0.3,0,0,0,255 fi
43579  polygon 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,1,0xFFFFFFFF,0,0,0,255
43580
43581#@gui Frame [Blur] : fx_frame_blur, fx_frame_blur(1)
43582#@gui : Horizontal Size (%) = float(30,0,100)
43583#@gui : Vertical Size (%) = float(30,0,100)
43584#@gui : sep = separator()
43585#@gui : Crop = float(0,0,100)
43586#@gui : Blur = float(5,0,10)
43587#@gui : Roundness = float(0,0,1)
43588#@gui : Apply Color Balance = bool(0)
43589#@gui : Balance Color = color(128,128,128)
43590#@gui : Normalization = choice("None","Stretch","Equalize")
43591#@gui : sep = separator()
43592#@gui : Outline Size = float(5,0,50)
43593#@gui : Outline Color = color(255,255,255)
43594#@gui : X-Shadow = float(2,-10,10)
43595#@gui : Y-Shadow = float(2,-10,10)
43596#@gui : Shadow Smoothness = float(1,0,5)
43597#@gui : Shadow Contrast = float(0,0,100)
43598#@gui : X-Centering = float(0.5,0,1)
43599#@gui : Y-Centering = float(0.5,0,1)
43600#@gui : Angle = float(0,-180,180)
43601#@gui : sep = separator()
43602#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/19/01</i>.</small>")
43603fx_frame_blur :
43604  repeat $! l[$>] to_rgb
43605    sx={$1%*max(w,h)} sy={$2%*max(w,h)}
43606    +r {w+$sx},{h+$sy},1,100%,3 b[1] $4%
43607
43608    if $6 balance_gamma[1] ${7-9} fi
43609    if $10==1 n[1] 0,255 elif $10==2 n[1] 0,255 equalize[1] 256 fi
43610    rv
43611
43612    z[1] {$3/2}%,{$3/2}%,{100-$3/2}%,{100-$3/2}%
43613    to_rgba[1]
43614
43615    if $5 r={1+1/$5} sh[1] 100% f. '1-(abs(x/w-0.5)^$r+abs(y/h-0.5)^$r)^(1/$r)'
43616      v={min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2))}
43617      c. $v,{$v+0.5/max(w,h)} n. 0,255 rm. fi
43618
43619    s={$11%*max(w,h)}
43620    r[1] {w+$s},{h+$s},1,4,0,0,0.5,0.5
43621    i[1] 100%,100%,1,3 fc[1] ${12-14} blend[1,2] alpha to_a.
43622
43623    if $5 sh[1] 100% f. '1-(abs(x/w-0.5)^$r+abs(y/h-0.5)^$r)^(1/$r)'
43624      v={min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2))}
43625      c. $v,{$v+0.5/max(w,h)} n. 0,255 rm. fi
43626    rotate[1] $21,1,0
43627    r[1] [0],[0],1,4,0,0,$19,$20
43628    +channels[1] 100% b. $17%,0 c. 0,{max(1,100-$18)}% n. 0,255
43629    shift. {round(w*$15%)},{round(h*$16%)},0,0,0 /. -255 +. 1 *[0,-1]
43630
43631    blend alpha
43632  endl done
43633
43634#@gui Frame [Cube] : frame_cube, frame_cube(1)
43635#@gui : Depth = float(3,0,30)
43636#@gui : X-Center = float(0,-2,2)
43637#@gui : Y-Center = float(0,-2,2)
43638#@gui : Left Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY")
43639#@gui : Right Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY")
43640#@gui : Upper Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY")
43641#@gui : Lower Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY")
43642#@gui : sep = separator()
43643#@gui : note = note("<small>Author: <i>David Tschumperlé, Angelo Lama</i>.
43644#@gui :       Latest Update: <i>2012/29/01</i>.</small>")
43645
43646#@gui Frame [Fuzzy] : fx_frame_fuzzy, fx_frame_fuzzy(0)
43647#@gui : Horizontal Size (%) = float(5,0,100)
43648#@gui : Vertical Size (%) = float(5,0,100)
43649#@gui : Fuzzyness = float(10,0,40)
43650#@gui : Smoothness = float(1,0,5)
43651#@gui : Color = color(255,255,255,255)
43652#@gui : sep = separator()
43653#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43654fx_frame_fuzzy :
43655  repeat $! l[$>]
43656    sx={$1%*max(w,h)/2} sy={$2%*max(w,h)/2}
43657    frame_fuzzy $sx,$sy,${3-8}
43658  endl done
43659
43660#@gui Frame [Mirror] : fx_frame_mirror, fx_frame_mirror_preview(1)
43661#@gui : note = note("<b>Frame size:</b>")
43662#@gui : Horizontal (%) = float(10,0,100)
43663#@gui : Vertical (%) = float(10,0,100)
43664#@gui : sep = separator()
43665#@gui : note = note("<b>Image alignment:</b>")
43666#@gui : Horizontal (%) = float(50,0,100)
43667#@gui : Vertical (%) = float(50,0,100)
43668#@gui : sep = separator()
43669#@gui : note = note("<b>Frame dilation/shrinking:</b>")
43670#@gui : Left = float(0,-5,5)
43671#@gui : Right = float(0,-5,5)
43672#@gui : Up = float(0,-5,5)
43673#@gui : Bottom = float(0,-5,5)
43674#@gui : sep = separator()
43675#@gui : Preview Opacity (%) = float(0.75,0,1)
43676#@gui : sep = separator()
43677#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/08/20</i>.</small>")
43678fx_frame_mirror :
43679  repeat $! l[$>]
43680    {100+2*$1}%,{100+2*$2}%,1,100%,"
43681      const boundary = 3;
43682      const offx = (w - w#-1)*$3%;
43683      const offy = (h - h#-1)*$4%;
43684      const f_left = 2^$5;
43685      const f_right = 2^$6;
43686      const f_up = 2^$7;
43687      const f_bottom = 2^$8;
43688      x = x - offx;
43689      y = y - offy;
43690      x<0?(x*=-f_left):
43691      x>=w#-1?(x = w#-1 - 1 - f_right*(x - w#-1));
43692      y<0?(y*=-f_up):
43693      y>=h#-1?(y = h#-1 - 1 - f_bottom*(y - h#-1));
43694      I(#-1,x,y)"
43695  k. endl done
43696
43697fx_frame_mirror_preview :
43698  repeat $! l[$>]
43699    ws,hs={[w,h]} fx_frame_mirror $* wd,hd={[w,h]}
43700    rr2d $_preview_width,$_preview_height wp,hp={[w,h]}
43701    ws,hs={[$ws,$hs]*[$wp,$hp]/[$wd,$hd]}
43702
43703    coords={off=([$wp,$hp]-[$ws,$hs])*[$3,$4]%;[off,off+[$ws,$hs]-1]}
43704    split_opacity 100%,100%,1,1,$9 rectangle. $coords,1,1 *[0,-1] a c
43705    rectangle $coords,0.75,0xF0F0F0F0,255
43706    rectangle $coords,0.75,0x0F0F0F0F,0,0,0,255
43707  endl done
43708
43709#@gui Frame [Painting] : fx_frame_painting, fx_frame_painting_preview(1)
43710#@gui : Size (%) = float(10,0,100)
43711#@gui : Contrast = float(0.4,0,1)
43712#@gui : Smoothness = float(6,0,30)
43713#@gui : Color = color(225,200,120)
43714#@gui : sep = separator()
43715#@gui : Vignette Size = float(2,0,50)
43716#@gui : Vignette Contrast = float(400,0,1000)
43717#@gui : sep = separator()
43718#@gui : Defects Contrast = float(50,0,512)
43719#@gui : Defects Density = float(10,0,100)
43720#@gui : Defects Size = float(1,0,10)
43721#@gui : Defects Smoothness = float(0.5,0,20)
43722#@gui : sep = separator()
43723#@gui : Serial Number = int(123456,0,1000000)
43724#@gui : Frame as a New Layer = _bool(false)
43725#@gui : sep = separator()
43726#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/07/06</i>.</small>")
43727fx_frame_painting :
43728  if $14
43729    repeat $! 100%,100%,1,4 frame_painting. $1%,$2,$3%,${4-6},$7%,${8-13}
43730    rv[-2,-1] to_a. r. ..,..,1,4,0,0,0.5,0.5 mv[-2,-1] 0 done
43731  else frame_painting $1%,$2,$3%,${4-6},$7%,${8-13}
43732  fi
43733
43734fx_frame_painting_preview :
43735  frame_painting $1%,$2,$3%,${4-6},$7%,${8-13}
43736
43737#@gui Frame [Pattern] : fx_frame_pattern, fx_frame_pattern_preview(1)
43738#@gui : Tiles = int(10,3,30)
43739#@gui : Pattern = choice(1,"Top Layer","Self Image")
43740#@gui : Iterations = int(1,1,10)
43741#@gui : Constrain Image Size = _bool(1)
43742#@gui : sep = separator()
43743#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/01/08</i>.</small>")
43744fx_frame_pattern :
43745  if $2" || "$!==1 repeat $3 frame_pattern $1,$4 done
43746  else repeat $3 frame_pattern[^0] $1,[0],$4 done fi
43747
43748fx_frame_pattern_preview :
43749  fx_frame_pattern ${1-3},1
43750
43751#@gui Frame [Regular] : fx_frame, fx_frame(1)
43752#@gui : note = note("<b>Crop parameters :</b>")
43753#@gui : X-Start (%) = int(0,0,100)
43754#@gui : X-End (%) = int(100,0,100)
43755#@gui : Y-Start (%) = int(0,0,100)
43756#@gui : Y-End (%) = int(100,0,100)
43757#@gui : sep = separator()
43758#@gui : note = note("<b>Frame parameters :</b>")
43759#@gui : Width (%) = int(10,0,100)
43760#@gui : Height (%) = int(10,0,100)
43761#@gui : Color = color(0,0,0,255)
43762#@gui : Outline Size = int(1,0,100)
43763#@gui : Outline Color = color(255,255,255,255)
43764#@gui : sep = separator()
43765#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43766fx_frame :
43767 to_rgba repeat $!
43768   z. $1%,$3%,$2%,$4%
43769   frame. $11,$11,${12-15}
43770    sx={$5%*max(w,h)} sy={$6%*max(w,h)}
43771   frame. $sx,$sy,${7-10}
43772 mv. 0 done
43773
43774#@gui Frame [Round] : fx_frame_round, fx_frame_round(1)
43775#@gui : Sharpness = float(6,0.1,40)
43776#@gui : Size (%) = float(20,0,100)
43777#@gui : Smoothness = float(0.1,0,15)
43778#@gui : Shade = float(0,0,1)
43779#@gui : Color = color(255,255,255,255)
43780#@gui : Blur Frame = float(0,0,100)
43781#@gui : Blur Shade = float(0.1,0,1)
43782#@gui : Blur Amplitude = float(3,0,10)
43783#@gui : sep = separator()
43784#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43785fx_frame_round :
43786  frame_round ${1-8}
43787  if $9 frame_blur $1,{min(99,$1+$9)},$3,$10,$11% fi
43788
43789#@gui Frame [Smooth] : fx_frame_smooth, fx_frame_smooth(1)
43790#@gui : Width (%) = int(10,0,100)
43791#@gui : Height (%) = int(10,0,100)
43792#@gui : Roundness = float(0.25,0,1)
43793#@gui : sep = separator()
43794#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/25/04</i>.</small>")
43795fx_frame_smooth :
43796  repeat $! l[$>]
43797     sx={$1%*max(w,h)} sy={$2%*max(w,h)}
43798     100%,100%,1,1,0
43799     if $3 r={1+1/$3} f. '1-(abs(x/w-0.5)^$r+abs(y/h-0.5)^$r)^(1/$r)'
43800       v={min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2))} <=. $v
43801     fi
43802     frame $sx,$sy,1
43803     inpaint_pde[0] [1],100%,1,15
43804     rm.
43805  endl done c 0,255
43806
43807#@gui Old Photograph : fx_old_photo, fx_old_photo(1)
43808#@gui : Vignette Strength = float(200,0,255)
43809#@gui : Vignette Min Radius = float(50,0,100)
43810#@gui : Vignette Max Radius = float(85,0,100)
43811#@gui : sep = separator()
43812#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43813fx_old_photo :
43814  vignette ${1-3} old_photo
43815
43816#@gui Polaroid : fx_polaroid, fx_polaroid(1)
43817#@gui : Frame Size = int(10,0,400)
43818#@gui : Bottom Size = int(20,0,400)
43819#@gui : X-Shadow = float(0,-20,20)
43820#@gui : Y-Shadow = float(0,-20,20)
43821#@gui : Smoothness = float(3,0,5)
43822#@gui : Curvature = float(0,0,1)
43823#@gui : Angle = float(20,-180,180)
43824#@gui : Vignette Strength = float(50,0,255)
43825#@gui : Vignette Min Radius = float(70,0,100)
43826#@gui : Vignette Max Radius = float(95,0,100)
43827#@gui : sep = separator()
43828#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
43829fx_polaroid :
43830  vignette ${8-10} polaroid $1,$2 drop_shadow $3%,$4%,$5%,$6 rotate $7,1,0
43831
43832#@gui Tunnel : fx_tunnel, fx_tunnel(1)
43833#@gui : Depth = int(4,1,100)
43834#@gui : Factor = float(80,1,99)
43835#@gui : Center (%) = point(50,50)
43836#@gui : Opacity = float(0.2,0,1)
43837#@gui : Angle = float(0,-90,90)
43838#@gui : sep = separator()
43839#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/22/11</i>.</small>")
43840fx_tunnel :
43841  tunnel $1,$2%,{[${3,4}]%},${5-6}
43842
43843#@gui Vignette : fx_vignette, fx_vignette
43844#@gui : Strength = float(70,0,255)
43845#@gui : Min Radius = float(70,0,100)
43846#@gui : Max Radius = float(95,0,100)
43847#@gui : Color = color(0,0,0,255)
43848#@gui : sep = separator()
43849#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/24/10</i>.</small>")
43850fx_vignette :
43851  repeat $! to_rgb l[$>]
43852    to_rgba split_opacity
43853    =. 0 vignette. ${1-3} a c +fc ${4-7} rv blend alpha
43854  endl done
43855
43856#@gui ____<b>Frequencies</b>
43857#----------------------------
43858
43859#@gui Bandpass : fx_bandpass, fx_bandpass_preview(0)
43860#@gui : Low Frequency = float(0,0,100)
43861#@gui : High Frequency = float(100,0,100)
43862#@gui : sep = separator()
43863#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43864#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43865#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43866#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43867#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43868#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43869#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43870#@gui : Value Action = choice(2,"None","Cut","Normalize")
43871#@gui : sep = separator()
43872#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43873#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43874#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43875#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43876#@gui : sep = separator()
43877#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43878fx_bandpass :
43879  repeat $! l[$>] split_opacity l[0]
43880    ac "bandpass $1%,$2%",$3,$4
43881  endl a c endl done
43882
43883fx_bandpass_preview :
43884  gui_split_preview "fx_bandpass $*",${-3--1}
43885
43886#@gui Fourier Analysis : fx_display_fft, fx_display_fft(1)
43887#@gui : sep = separator()
43888#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43889fx_display_fft :
43890  to_rgb display_fft
43891
43892#@gui Fourier Transform : fx_fourier, fx_fourier_preview(1)
43893#@gui : Magnitude / Phase = choice{1,"One Layer (Horizontal)","One Layer (Vertical)","Two Layers"}
43894#@gui : Discard Transparency = bool(1)
43895#@gui : sep = separator()
43896#@gui : note = note{"<small><b>Note:</b> Apply this filter once to get the direct FFT,
43897#@gui : and once again to get the reverse transform.</small>"}
43898#@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=3137dDa6P4s")
43899#@gui : sep = separator()
43900#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/06/16</i>.</small>")
43901fx_fourier : skip ${2=0}
43902  if $2 remove_opacity fi
43903  magic="GMICFFT"
43904
43905  i=0 for $i<$! ni={$i+1} nm={$i,n}
43906
43907    # Detect FFT/iFFT mode.
43908    is_ifft=0
43909    +columns[$i] 100%
43910    l.
43911      mag,m0,M0,m1,M1=${u\ {t}}
43912      if ['$mag']=='$magic' is_ifft=1 fi
43913    onfail endl rm.
43914    if !$is_ifft +rows[$i] 100% l. mag,m0,M0,m1,M1=${u\ {t}} if ['$mag']=='$magic' is_ifft=2 fi onfail endl rm. fi
43915    if !$is_ifft
43916      +rows[$i] 100% l. mag,m0,M0=${u\ {t}} if ['$mag']=='$magic' is_ifft=3 fi onfail endl rm.
43917      if $is_ifft==3
43918        is_ifft=0 +rows[$ni] 100%
43919        l. mag,m1,M1=${u\ {t}} if ['$mag']=='$magic' is_ifft=3 fi onfail endl
43920        rm.
43921      fi
43922    fi
43923
43924    # Compute the transform.
43925    if !$is_ifft # FFT
43926      l[$i]
43927        fftpolar +.. 1 log.. m0,M0,m1,M1={[im#0,iM#0,im#1,iM#1]} n[-2,-1] 0,255
43928        if $1==0 ({'$magic,$m0,$M0,$m1,$M1'},0) y. a x
43929        elif $1==1 ({'$magic,$m0,$M0,$m1,$M1'},0) a y
43930        else ({'$magic,$m0,$M0'},0) a[-3,-1] y ({'$magic,$m1,$M1'},0) a[-2,-1] y
43931        fi
43932        nm $nm
43933        endl
43934
43935    else # iFFT
43936      if $is_ifft==1 columns[$i] 0,{$i,w-2} s[$i] x,2
43937      elif $is_ifft==2 rows[$i] 0,{$i,h-2} s[$i] y,2
43938      else rows[$i,$ni] 0,{$i,h-2}
43939      fi
43940      l[$i,{$i+1}] n[0] $m0,$M0 n[1] $m1,$M1 exp[0] -[0] 1 ifftpolar c 0,255 endl
43941    fi
43942    i+={$1<2" || "$is_ifft>2?1:2}
43943  done
43944
43945fx_fourier_preview :
43946  if $2 remove_opacity fi
43947  dfft
43948
43949#@gui Fourier Watermark : fx_watermark_fourier, _none_
43950#@gui : Text = text{"(c) G'MIC"}
43951#@gui : Size = int(53,13,128)
43952#@gui : sep = separator()
43953#@gui : note = note("<small><b>Note: </b> To make the watermark visible afterwards, use the
43954#@gui : 'Fourier Analysis' filter. </small>")
43955#@gui : sep = separator()
43956#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43957fx_watermark_fourier :
43958  watermark_fourier "$1",$2 c 0,255
43959
43960#@gui ____<b>Layers</b>
43961#-----------------------
43962
43963#@gui Align Layers : fx_align_layers, fx_align_layers_preview : *
43964#@gui : Alignment Type = choice(0,"Rigid","Non-Rigid")
43965#@gui : Smoothness = float(0.7,0,1)
43966#@gui : Scales = choice(0,"Auto","1","2","3","4","5","6","7","8")
43967#@gui : Revert Layers = bool(0)
43968#@gui : sep = separator()
43969#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/01/11</i>.</small>")
43970fx_align_layers :
43971  to_colormode 0
43972  r ${-max_wh},1,100%,0,0,0.5,0.5
43973  if ${4=0} _fx_revert_layers fi
43974  remove_opacity
43975  if $1 register_nonrigid[^-1] .,$2,0.1,$3
43976  else register_rigid[^-1] .,$2
43977  fi
43978
43979fx_align_layers_preview :
43980  fx_align_layers $1,$2,0 blend_edges 0.1
43981
43982_fx_revert_layers :
43983  repeat int($!/2) rv[{2*$>},{2*$>+1}] done
43984
43985#@gui Blend [Average All] : fx_blend_average_all, fx_blend_average_all : *
43986#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
43987#@gui : sep = separator()
43988#@gui : note = note{"<small><b>Note:</b>
43989#@gui : This filter takes multiple layers as input and average them. Set the <i>Input layers</i> option
43990#@gui : to handle multiple input layers.
43991#@gui : </small>"}
43992#@gui : sep = separator()
43993#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/11/08</i>.</small>")
43994fx_blend_average_all :
43995  if $! to_rgba
43996    N=$! r ${-max_wh},1,100%,0,0,0.5,0.5
43997    _gb_fwd $1
43998    + / $N
43999    _gb_bwd $1
44000  fi
44001
44002_gb_fwd :
44003  to_color
44004  if $1==1 repeat $! l[$>] sh 0,2 srgb2rgb. rm. endl done
44005  elif $1==2 repeat $! l[$>] sh 0,2 srgb2rgb. rgb2lab. rm. endl done
44006  fi
44007
44008_gb_bwd :
44009  to_color
44010  if $1==1 repeat $! l[$>] sh 0,2 rgb2srgb. rm. endl done
44011  elif $1==2 repeat $! l[$>] sh 0,2 lab2rgb. rgb2srgb. rm. endl done
44012  fi
44013
44014#@gui Blend [Edges] : fx_blend_edges, fx_blend_edges(0) : *
44015#@gui : Opacity = float(1,0,1)
44016#@gui : Smoothness = float(0.8,0,5)
44017#@gui : Revert Layers = bool(0)
44018#@gui : sep = separator()
44019#@gui : note = note{"<small><b>Note:</b>
44020#@gui : This filter needs two layers to work properly. Set the <i>Input layers</i> option to handle
44021#@gui : multiple input layers.
44022#@gui : </small>"}
44023#@gui : sep = separator()
44024#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/21/01</i>.</small>")
44025fx_blend_edges :
44026  repeat int($!/2) l[$>,{$>+1}] if $3 rv fi +blend_edges[-2,-1] $2 rm... blend[-2,-1] alpha,$1 endl done
44027
44028#@gui Blend [Fade] : fx_blend_fade, fx_blend_fade(1) : +
44029#@gui : Preset = choice{1,"Custom","Linear","Circular","Wave","Keftales"}
44030#@gui : Offset = float(0,-1,1)
44031#@gui : Thinness = float(0,0,10)
44032#@gui : Sharpness = float(5,1,20)
44033#@gui : Sharpest = bool(0)
44034#@gui : Revert Layers = bool(0)
44035#@gui : Colorspace = choice("sRGB","Linear RGB","Lab")
44036#@gui : note = note{\n<small>
44037#@gui : The parameters below are used in most presets.
44038#@gui : </small>}
44039#@gui : 1st Parameter = float(0,-1,1)
44040#@gui : 2nd Parameter = float(0,-1,1)
44041#@gui : 3rd Parameter = float(0,-1,1)
44042#@gui : note = note{\n<small>
44043#@gui : The formula below is used for the <i>Custom</i> preset.
44044#@gui : </small>}
44045#@gui : Formula = text{"cos(4*pi*x/w) * sin(4*pi*y/h)"}
44046#@gui : note = note{"<small><b>Note:</b>
44047#@gui : This filter needs two layers to work properly. Set the <i>Input layers</i> option to handle
44048#@gui : multiple input layers.
44049#@gui : </small>"}
44050#@gui : sep = separator()
44051#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/21/01</i>.</small>")
44052fx_blend_fade :
44053  if $!==1 return fi
44054  to_colormode 4
44055  _gb_fwd $7
44056  if $1==0 [0],[0],1,1,"$11"
44057  else _fx_blend_fade$1 $8,$9,$10 r. [0],[0],1,1,3
44058  fi
44059  n. {-($!-2)*$3},{($!-2)*(1+$3)}
44060  -. {$2*(1+$3)*($!-2)}
44061  c. 0,{$!-2}
44062  if $6 rv[^-1] fi
44063  if $5 round. 1
44064  else roundify. $4
44065  fi
44066  blend_fade[^-1] . rm.
44067  _gb_bwd $7
44068  c 0,255
44069
44070_fx_blend_fade1 : [0],[0],1,1,"a=$1*pi/2; x*cos(a) + y*sin(a)"
44071_fx_blend_fade2 : [0],[0],1,1,0 =. 1,{($1+1)*50}%,{($2+1)*50}% distance. 1
44072_fx_blend_fade3 : [0],[0],1,1,0 =. 1,{($1+1)*50}%,{($2+1)*50}% distance. 1 *. {0.01+$3/2} cos.
44073_fx_blend_fade4 : [0],[0],1,1,"((x-w*($1+0.5))*(y-h*($2+0.5)))%(0.2*w*h*(1.001+$3))"
44074
44075#@gui Blend [Median] : fx_blend_median, fx_blend_median(0) : *
44076#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
44077#@gui : sep = separator()
44078#@gui : note = note{"<small><b>Note:</b>
44079#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
44080#@gui : multiple input layers.
44081#@gui : </small>"}
44082#@gui : sep = separator()
44083#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Iain Fergusson</i>.
44084#@gui :       Latest Update: <i>2014/16/12</i>.</small>")
44085fx_blend_median :
44086  _gb_fwd $1
44087  blend_median
44088  _gb_bwd $1
44089
44090#@gui Blend [Seamless] : fx_blend_seamless, fx_blend_seamless_preview(1) : *
44091#@gui : Mixed Mode = bool(0)
44092#@gui : Inner Fading = float(0,0,100)
44093#@gui : Outer Fading = float(25,0,100)
44094#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
44095#@gui : sep = separator()
44096#@gui : Output as Separate Layers = _bool(0)
44097#@gui : sep = separator()
44098#@gui : note = note{"<small><b>Note:</b>
44099#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
44100#@gui : multiple input layers.
44101#@gui : </small>"}
44102#@gui : sep = separator()
44103#@gui : url = link("Click here for a detailed description of this filter.",\
44104# "http://gimpchat.com/viewtopic.php?f=28&t=10204")
44105#@gui : url = link("+ Video tutorial 1","http://www.youtube.com/watch?v=Nu-S1HmOCgE")
44106#@gui : url = link("+ Video tutorial 2","http://www.youtube.com/watch?v=zsHgQY6025I")
44107#@gui : url = link("+ Video tutorial 3","http://www.youtube.com/watch?v=2e6FikWMkaQ")
44108#@gui : sep = separator()
44109#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/04/05</i>.</small>")
44110fx_blend_seamless :
44111  rv
44112  _gb_fwd $4
44113  to_a[^0] r[^0] [0],[0],1,100%,0
44114  repeat $! pos=${gui_layer_pos[$>]} shift[$>] ${u\ $pos},0,0 done
44115  if $5 # Output as separate layers
44116    +blend_seamless $1,$2%,$3%
44117    remove_opacity[0,-1] k[0,-1] rv sub_alpha[0] [1],1
44118  else
44119    blend_seamless $1,$2%,$3% # Output as a single layer.
44120  fi
44121  _gb_bwd $4
44122
44123fx_blend_seamless_preview :
44124  fx_blend_seamless ${1-4},0
44125
44126#@gui Blend [Standard] : fx_blend, fx_blend_preview : *
44127#@gui : Mode = choice{6,"Add","Alpha","And","Average","Blue","Burn","Custom formula","Darken","Difference",
44128#@gui : "Divide","Dodge","Edges","Exclusion","Freeze","Grain Extract","Grain Merge","Green","Hard Light",
44129#@gui : "Hard Mix","Hue","Interpolation","Lighten","Lightness","Linear Burn","Linear Light","Luminance",
44130#@gui : "Multiply","Negation","Or","Overlay","Pin Light","Red","Reflect","Saturation",
44131#@gui : "Shape Area Max","Shape Area Max0","Shape Area Min","Shape Area Min0","Shape Average","Shape Average0",
44132#@gui : "Shape Median","Shape Median0","Shape Min","Shape Min0","Shape Max","Shape Max0",
44133#@gui : "Soft Burn","Soft Dodge","Soft Light","Screen","Stamp","Subtract","Value","Vivid Light","Xor"}
44134#@gui : Process As = choice("Two-by-Two","Upper Layer is the Top Layer for All Blends",
44135#@gui : "Lower Layer is the Bottom Layer for All Blends")
44136#@gui : Opacity (%) = float(100,0,100)
44137#@gui : Preview All Outputs = bool(1)
44138#@gui : sep = separator()
44139#@gui : Custom Formula = text{"1/2 - 1/4*cos(pi*a) - 1/4*cos(pi*b)"}
44140#@gui : note = note{"<small><b>Note:</b> In custom formulas, <samp>a</samp> and <samp>b</samp> respectively stand for
44141#@gui : the values of the <i>base layer<i> and the <i>blend layer</i>,
44142#@gui : and are defined in value range [0,1].</small>"}
44143#@gui : sep = separator()
44144#@gui : note = note{"<small><b>Note:</b>
44145#@gui : This filter needs at least two layers to work properly. Do not forget to set the <i>Input layers</i> option
44146#@gui : below to handle multiple input layers.
44147#@gui : </small>"}
44148#@gui : url = link("Reference page for G'MIC blending modes",
44149#@gui : "https://github.com/dtschump/gmic-community/wiki/Blending-modes")
44150#@gui : sep = separator()
44151#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/03/08</i>.</small>")
44152fx_blend :
44153  mode=${arg\ 1+$1,add,alpha,and,average,blue,burn,custom_formula,darken,difference,\
44154       divide,dodge,edges,exclusion,freeze,grainextract,grainmerge,green,hardlight,\
44155       hardmix,hue,interpolation,lighten,lightness,linearburn,linearlight,luminance,\
44156       multiply,negation,or,overlay,pinlight,red,reflect,saturation,\
44157       shapeareamax,shapeareamax0,shapeareamin,shapeareamin0,\
44158       shapeaverage,shapeaverage0,shapemedian,shapemedian0,\
44159       shapemin,shapemin0,shapemax,shapemax0,\
44160       softburn,softdodge,softlight,screen,stamp,subtract,value,\
44161       vividlight,xor}
44162  m "_blend_custom_formula : f. \"a = i#0/255; b = i#1/255; 255*cut(($5),0,1)\""
44163  if $2==0 repeat int($!/2) l[$>,{$>+1}] rv blend $mode,{$3%} endl done # Two-by-two.
44164  elif $2==1" && "$!>1 blend[^0] [0],$mode,{$3%},0 rm[0]  # Top layer is top for all blends.
44165  elif $2==2" && "$!>1 blend[^-1] .,$mode,{$3%},1 rm. # Bottom layer is bottom for all blends.
44166  fi
44167  um _blend_custom_formula
44168
44169fx_blend_preview :
44170  fx_blend $"*"
44171  if $4 append_tiles , fi
44172
44173#@gui Colors to Layers : fx_split_colors, fx_split_colors_preview(1)
44174#@gui : Color Tolerance = float(50,0,256)
44175#@gui : Maximum Number of Output Layers = int(16,2,256)
44176#@gui : Minimal Area (%) = float(1,0,100)
44177#@gui : Autocrop Output Layers = bool()
44178#@gui : sep = separator()
44179#@gui : note = note{"<small><b>Note:</b> This filter decomposes an image into several layers each with
44180#@gui : a single color + a residual layer (if any).
44181#@gui : </small>"}
44182#@gui : sep = separator()
44183#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/11/03</i>.</small>")
44184fx_split_colors : skip ${2=0}
44185  to_rgb repeat $! l[$>]
44186    nm=${-gui_layer_name}
44187    min_area={max(1,w*h*$3%)}
44188    split_colors $1,$2,$min_area
44189    nm name($nm)
44190    if $4 gui_autocrop_layers fi
44191  endl done
44192
44193fx_split_colors_preview :
44194  repeat $! l[$>]
44195    +fx_split_colors ${1-4} drgba
44196    repeat $! l[$>] to ${arg\ {1+!!$>},"Original","#"$>},1,1,43,7,1,255 endl done
44197    frame 1,1,0 frame 3,3,255 to_rgba append_tiles ,
44198  endl done
44199
44200#@gui Fade Layers : fx_fade_layers, fx_fade_layers_preview : +
44201#@gui : Inter-Frames = _int(10,2,100)
44202#@gui : sep = separator()
44203#@gui : note = note{"<small><b>Note:</b>
44204#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
44205#@gui : multiple input layers.
44206#@gui : </small>"}
44207#@gui : sep = separator()
44208#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/04/08</i>.</small>")
44209fx_fade_layers :
44210  if $!<2 return fi
44211  to_colormode 0
44212  r ${-max_wh},1,100%,0,0,0.5,0.5
44213  a z r 100%,100%,{(d-1)*$1+1},100%,3 s z
44214
44215fx_fade_layers_preview :
44216  if $!<2 return fi
44217  to_colormode 0
44218  r ${-max_wh},1,100%,0,0,0.5,0.5
44219  k[0,1] + / 2
44220
44221#@gui Layers to Tiles : append_tiles, fx_append_tiles_preview(1) : *
44222#@gui : X-Tiles = int(0,0,256)
44223#@gui : Y-Tiles = int(0,0,256)
44224#@gui : note = note("<small>For both parameters, <i>0</i> means <i>automatic</i>.</small>")
44225#@gui : sep = separator()
44226#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44227fx_append_tiles_preview :
44228  frame 1,1,0,0,0,255 append_tiles $1,$2
44229
44230#@gui Morph Layers : fx_morph_layers, gui_no_preview : *
44231#@gui : Inter-Frames = _int(10,2,100)
44232#@gui : Smoothness = _float(0.2,0,2)
44233#@gui : Precision = _float(0.1,0,2)
44234#@gui : Revert Layers = bool(0)
44235#@gui : sep = separator()
44236#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44237fx_morph_layers :
44238  if ${4=0} _fx_revert_layers fi
44239  to_rgb morph $1,$2,$3
44240
44241#@gui Multiscale Operator : fx_apply_multiscale, fx_apply_multiscale_preview(1)
44242#@gui : Number of Scales = int(4,2,16)
44243#@gui : sep = separator()
44244#@gui : Starting Scale (%) = float(25,0,400)
44245#@gui : Ending Scale (%) = float(100,0,400)
44246#@gui : Non-Linearity = float(0,-1,1)
44247#@gui : Rescaling = choice(3,"Bloc","Linear","Cubic","Lanczsos")
44248#@gui : sep = separator()
44249#@gui : X-Centering = float(0.5,0,1)
44250#@gui : Y-Centering = float(0.5,0,1)
44251#@gui : Angle = float(0,-180,180)
44252#@gui : sep = separator()
44253#@gui : Enable Interpolated Motion = bool(0)
44254#@gui : Ending X-Centering = float(0.5,0,1)
44255#@gui : Ending Y-Centering = float(0.5,0,1)
44256#@gui : Ending Angle = float(0,-180,180)
44257#@gui : sep = separator()
44258#@gui : G'MIC Operator = text("")
44259#@gui : Return Scaling = choice("None","Bloc","Linear","Cubic","Lanczos")
44260#@gui : Lock Return Scaling to Source Layer = bool(0)
44261#@gui : sep = separator()
44262#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/30/03</i>.</small>")
44263fx_apply_multiscale : skip "${13=}"
44264  repeat $! l[$<]
44265    w0={w} h0={h}
44266    apply_scales "$13",$1,$2%,$3%,{10^$4},{arg(1+$5,1,3,5,6)}
44267    if $8" || "($9" && "$8!=$12) to_a N=$! repeat $!
44268      angle={$9?$8+($12-$8)*$>/max($N-1,1):$8}
44269      rotate[$>] $angle
44270    done fi
44271    if $14
44272      if $15 siz=$w0,$h0 else siz=${-max_wh} fi
44273      r $siz,1,100%,{arg($14,1,3,5,6)}
44274      c 0,255
44275    fi
44276    w=${-max_w} h=${-max_h} N=$!
44277    repeat $!
44278      cx={$9?$6+($10-$6)*$>/max($N-1,1):$6}
44279      cy={$9?$7+($11-$7)*$>/max($N-1,1):$7}
44280      gui_set_layer_pos[$>] {$>,($w-w)*$cx},{$>,($h-h)*$cy}
44281    done
44282  endl done
44283
44284fx_apply_multiscale_preview :
44285  repeat $! l[$>]
44286    fx_apply_multiscale $"*"
44287    N={int(sqrt($!))} N={round($!/$N,1,1)} r2dy {100/$N}%
44288    to_rgba
44289    max_wh=${-max_wh}
44290    N=$! repeat $! l[$>]
44291      cx={$9?$6+($10-$6)*$>/max($N-1,1):$6}
44292      cy={$9?$7+($11-$7)*$>/max($N-1,1):$7}
44293      r $max_wh,1,100%,0,0,$cx,$cy
44294      0 text. "#"{1+$>}" ",1,1,24,1,255 +dilate. 5 to_rgba[1] j[0] [1],2,0,0,0,1,[2],255 k[0]
44295    endl done
44296    frame 1,1,0 frame 3,3,255 append_tiles ,
44297  endl done
44298
44299#@gui Pack : fx_pack, fx_pack_preview(1) : *
44300#@gui : Order By = choice(2,"Width","Height","Maximum Dimension","Area","Name")
44301#@gui : Tends to Be Square = bool(1)
44302#@gui : Force Transparency = bool(1)
44303#@gui : Add Image Label = bool(0)
44304#@gui : Font Height (px) = float(16,0,64)_0
44305#@gui : Font Colors = choice(1,"White on black","Black on white")_0
44306#@gui : sep = separator()
44307#@gui : Output Coordinates File = _bool(0)
44308#@gui : Output Folder = _folder()
44309#@gui : sep = separator()
44310#@gui : note = note{"<small>This filter tries to pack all input layers into a single image, while trying to
44311#@gui : minimize the empty areas.
44312#@gui : This problem being NP-hard, the algorithm finds (of course) a <b>non-optimal</b>, but often acceptable
44313#@gui : solution to this packing problem.</small>"}
44314#@gui : sep = separator()
44315#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/03/20</i>.</small>")
44316fx_pack : skip "${8=}"
44317  if $4 to_rgba repeat $! l[$>] nm0={n} gui_layer_name nm=${}
44318    0 t. {``$nm},3,0,$5,1,1 frame. 1,1,0
44319    if $6 *. -1 fi
44320    n. 0,255 to_rgba. r. {[w+2,h+1,1,4]},0,0,0,1
44321    rv a y,{w#0>w?0.5:0} nm $nm0
44322  endl done
44323  fi
44324  if $3 to_a fi
44325  repeat $! gui_layer_name[$>] nm$>=${} nm[$>] {`lowercase(['${nm$>}'])`} done
44326  c0="w" c1="h" c2="max(w,h)" c3="w*h" c4="n"
44327  pack $2,${c$1} coords=${}
44328  if $7
44329    repeat 256 filename "$8/gmic_pack.txt",$> filename=${} if isfile(['{/$filename}']) else break fi done
44330    if !narg($filename) filename="$8/gmic_pack.txt" fi
44331    l[] repeat narg($coords)/2
44332      x={arg(1+2*$>,$coords)} y={arg(2+2*$>,$coords)}
44333      ('"Image ""#"{1+$>}" ("${nm$>}"): "$x,$y\n')
44334    done a x ot $filename rm endl
44335  fi
44336  nm "name(G'MIC packing),pos(0,0),mode(normal)"
44337  if $4 autocrop fi
44338
44339fx_pack_preview :
44340  if !$! return fi
44341  w={w} h={h}
44342  filled=0 repeat $! filled={$>,$filled+w*h} done
44343  fx_pack $1,$2,$3,$4,$5,$6,0
44344  area={w*h}
44345  to_rgba rr2d $w,$h,0
44346  i[0] $w,16,1,4,255 t[0] "Filled: "{round(100*$filled/$area)}%,3,1,14,1,0,0,0,255
44347  a y,0.5
44348  u "{$1}{$2}{$3}{$4}"\
44349    "{$5}_"{2*$4}\
44350    "{$6}_"{2*$4}\
44351    "{$7}{$8}"
44352
44353#@gui Stroke : fx_stroke, fx_stroke_preview(0)
44354#@gui : Thickness (px) = int(3,1,256)
44355#@gui : Threshold (%) = float(50,0,100)
44356#@gui : Smoothness (px) = float(0,0,10)
44357#@gui : Shape = choice(2,"Square","Diamond","Round")
44358#@gui : Direction = choice(1,"Inward","Outward")
44359#@gui : sep = separator()
44360#@gui : Zoom (%) = float(100,1,300)
44361#@gui : X-Shift (px) = int(0,-256,256)
44362#@gui : Y-Shift (px) = int(0,-256,256)
44363#@gui : sep = separator()
44364#@gui : Starting Color = color(255,255,255,255)
44365#@gui : Ending Color = color(255,255,255,255)
44366#@gui : Inside Color = color(0,0,0,0)
44367#@gui : Outside Color = color(0,0,0,0)
44368#@gui : sep = separator()
44369#@gui : Output Stroke Layer On = choice(1,"Bottom","Top")
44370#@gui : Keep Original Image Size = bool(0)
44371#@gui : sep = separator()
44372#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/24/06</i>.</small>")
44373fx_stroke :
44374  to_a repeat $! l[$<] nm={n}
44375    if !$26" && "$5 expand_xy $1,0 is_frame1=0 else expand_xy 1,0 is_frame1=1 fi
44376    split_opacity +l.
44377      b $3
44378      if $6>=100
44379        shift $7,$8
44380        if $6!=100 wh={w},{h} r $6%,$6%,1,1,3 r $wh,1,1,0,0,0.5,0.5 fi
44381      else
44382        if $6!=100 wh={w},{h} r $6%,$6%,1,1,3 r $wh,1,1,0,0,0.5,0.5 fi
44383        shift $7,$8
44384      fi
44385      > {99.99-min(99.99,$2)}%
44386      distance $5,$4
44387      ($9^$10^$11^$12)
44388      if $1>1 ($13^$14^$15^$16) a[-2,-1] x r. $1,1,1,4,3 c. 0,255 fi
44389      i.. ($21^$22^$23^$24) ($17^$18^$19^$20) if $5 rv[-3,-1] fi
44390      a[-3--1] x map.. .,1 rm.
44391      nm $nm
44392    endl
44393    a[0,1] c
44394    if $is_frame1 shrink_xy 1 fi
44395    if $25 rv fi
44396  endl done
44397
44398fx_stroke_preview :
44399  repeat $! l[$>]
44400    fx_stroke $*
44401    nm foo
44402    gui_merge_layers
44403  endl done
44404
44405#@gui Tiles to Layers : split_tiles, fx_tiles2layers_preview(1)
44406#@gui : X-Tiles = int(3,1,100)
44407#@gui : Y-Tiles = int(3,1,100)
44408#@gui : Force Tiles to Have Same Size = _bool(false)
44409#@gui : sep = separator()
44410#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44411fx_tiles2layers_preview :
44412  split_tiles $1,$2,$3 to_rgba frame 1,1,0,0,0,255 frame 3,3,0,0,0,0 append_tiles ,
44413
44414#@gui Tones to Layers : fx_tones2layers, fx_tones2layers_preview(0)
44415#@gui : Number of Tones = int(3,2,10)
44416#@gui : Start of Mid-Tones = int(85,0,255)
44417#@gui : End of Mid-Tones = int(170,0,255)
44418#@gui : Smoothness = float(0.5,0,5)
44419#@gui : Alpha = choice("Binary","Scalar")
44420#@gui : sep = separator()
44421#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/05/04</i>.</small>")
44422fx_tones2layers :
44423  sval=$2 eval={max($2,$3)}
44424  remove_opacity repeat $! l[$<]
44425    +luminance rv
44426    repeat $1-1
44427      [1]
44428      val0={$sval+($eval-$sval)*$>/($1-2)}
44429      val1={$sval+($eval-$sval)*($>+1)/($1-2)-1}
44430      +ir[0] $val0,$val1
44431      if $5 *. [0] b. $4% n. 0,255  # Scalar alpha.
44432      else b. $4% n. 0,255  # Binary alpha.
44433      fi
44434      a[-2,-1] c
44435    done
44436    rm[0] rv
44437  endl done
44438
44439fx_tones2layers_preview :
44440  fx_tones2layers $* rv
44441  r {100/$!}%,{100/$!}%,1,100%,2
44442  to_rgba frame 1,1,0,0,0,255 frame 3,3,0,0,0,0 append_tiles ,
44443
44444#@gui ____<b>Lights & Shadows</b>
44445#---------------------------------
44446
44447#@gui Burn : fx_burn, fx_burn_preview(1)
44448#@gui : Amplitude = float(0.5,0,1)
44449#@gui : Scale = float(30,1,100)
44450#@gui : Smoothness = float(1,0,4)
44451#@gui : sep = separator()
44452#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44453#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44454#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44455#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44456#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44457#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44458#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44459#@gui : Value Action = choice("None","Cut","Normalize")
44460#@gui : sep = separator()
44461#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44462#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44463#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44464#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44465#@gui : sep = separator()
44466#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/24/11</i>.</small>")
44467_fx_burn :
44468  repeat $! l[$>]
44469    w,h={[w,h]}
44470    +norm
44471    fx_fourier. 2
44472    +rows. 0,{$h-1} r. $2%,$2%,1,100%,0,0,0.5,0.5 b. $3%
44473    j.. .,{($w-w)/2},{($h-h)/2} rm.
44474    fx_fourier[-2,-1] 2
44475    blend overlay,$1
44476  endl done
44477
44478fx_burn :
44479  ac "_fx_burn ${1-3}",$4,$5
44480
44481fx_burn_preview :
44482 gui_split_preview "fx_burn ${^0}",${-3--1}
44483
44484#@gui Contrast Swiss Mask : fx_contrast_swm , fx_contrast_swm(0)
44485#@gui : sep = separator()
44486#@gui : Blur the Mask = float(2,0.5,10)
44487#@gui : sep = separator()
44488#@gui : note = note ("Contrast Mask need the negative of the mask")
44489#@gui : Skip to Use the Mask to Boost = bool(false)
44490#@gui : note = note ("Uncheck for Contrast Mask,Check for Contrast Boost")
44491#@gui : sep = separator()
44492#@gui : note = note("Merge the Mask")
44493#@gui : Intensity = float(1,0,1)
44494#@gui : sep = separator()
44495#@gui : note = note("<small>Author: <i>PhotoComiX</i>.      Latest Update: <i>2011/01/01</i>.</small>")
44496#@gui : url = link("Filter explained here","http://www.gimpchat.com/viewtopic.php?f=9&t=864")
44497fx_contrast_swm :
44498  repeat $! l[$>] split_opacity l[0]
44499   +luminance to_rgb
44500    blur_xy[1] $1,$1
44501    if $2==0 negate[1] fi
44502    rv blend hardlight,$3
44503  endl a c endl done
44504
44505#@gui Drop Shadow : fx_drop_shadow, fx_drop_shadow(1)
44506#@gui : X-Shadow = float(3,-20,20)
44507#@gui : Y-Shadow = float(3,-20,20)
44508#@gui : Smoothness = float(1.8,0,5)
44509#@gui : Curvature = float(0,0,1)
44510#@gui : Corner Brightness = float(0,0,1)
44511#@gui : Angle = float(0,0,360)
44512#@gui : sep = separator()
44513#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/14/11</i>.</small>")
44514fx_drop_shadow :
44515  * -1 + 255 vignette {255*$5},80,95 * -1 + 255
44516  drop_shadow $1%,$2%,$3%,$4 rotate $6,1,0
44517
44518#@gui Drop Shadow 3D : fx_drop_shadow3d, fx_drop_shadow3d_preview(1)
44519#@gui : X-Angle = float(0,-90,90)
44520#@gui : Y-Angle = float(0,-90,90)
44521#@gui : Z-Angle = float(0,-90,90)
44522#@gui : Zoom = float(0,-100,100)
44523#@gui : X-Offset = float(1,-50,50)
44524#@gui : Y-Offset = float(1,-50,50)
44525#@gui : Perspective = float(2,0,10)
44526#@gui : Smoothness = float(0.5,0,5)
44527#@gui : Color = color(0,0,0,200)
44528#@gui : Preview Only Shadow = bool(0)
44529#@gui : sep = separator()
44530#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/02/07</i>.</small>")
44531fx_drop_shadow3d :
44532  repeat $! l[$<]
44533    +_fx_drop_shadow3d $*
44534  endl done
44535
44536fx_drop_shadow3d_preview :
44537  repeat $! l[$<]
44538    if $13 _fx_drop_shadow3d $*
44539    else +_fx_drop_shadow3d $* rv blend alpha
44540    fi
44541  endl done
44542
44543_fx_drop_shadow3d :
44544  point3d 0,0,1 r3d. 1,0,0,$1 r3d. 0,1,0,$2 r3d. 0,0,1,$3
44545  u={i(0,8)} v={i(0,9)} w={i(0,10)} rm.
44546  to_a channels 100% if im==iM return fi
44547  +f 'X=x/w-0.5;Y=y/h-0.5;A=($7-$4*$7/100)*$w/(X*$u+Y*$v+$7*$w);if(A<0,1e8,A)'
44548  +*. 'y/h-0.5' *.. 'x/w-0.5' +.. {0.5-$5/100} +. {0.5-$6/100} *.. {w} *. {h}
44549  a[-2,-1] c warp[0] .,0,1,0 rm.
44550  b $8% n 0,$12 i.. ($9^$10^$11) r.. .,.,1,3 a[-2,-1] c
44551
44552#@gui Equalize Light : fx_equalize_light, fx_equalize_light_preview(1)
44553#@gui : Amount (%) = float(75,0,100)
44554#@gui : Mode = choice("Preserve range","Preserve covariance")
44555#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44556#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44557#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44558#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44559#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44560#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44561#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44562#@gui : sep = separator()
44563#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44564#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44565#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44566#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44567#@gui : sep = separator()
44568#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/03/23</i>.</small>")
44569fx_equalize_light :
44570  ac "_fx_equalize_light $1,$2",$3,1
44571
44572_fx_equalize_light :
44573  repeat $! l[$>] split_opacity l[0]
44574    . +b. {max(0.1,100-$1)}% -[-2,-1]
44575    if $2 transfer_pca. .. else n. ..,.. fi
44576    rm..
44577  endl a c endl done
44578
44579fx_equalize_light_preview :
44580  gui_split_preview "fx_equalize_light $*",${-3--1}
44581
44582#@gui Equalize Shadow : fx_equalize_shadow, fx_equalize_shadow_preview(1)
44583#@gui : Amplitude = float(1,0,1)
44584#@gui : sep = separator()
44585#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44586#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44587#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44588#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44589#@gui : sep = separator()
44590#@gui : note = note("<small>Authors: <i>Francois Grassard</i> and <i>David Tschumperlé</i>.
44591#@gui :       Latest Update: <i>2021/03/23</i>.</small>")
44592fx_equalize_shadow :
44593  repeat $! l[$>] +negate blend softlight,$1 endl done
44594
44595fx_equalize_shadow_preview :
44596  gui_split_preview "fx_equalize_shadow $1",${-3--1}
44597
44598#@gui Guided Light Rays : fx_guided_lightrays,fx_guided_lightrays_preview(1) : +
44599#@gui : Amplitude (%) = float(10,0,100)
44600#@gui : Ray Length = float(2,0,2)
44601#@gui : Mode = choice("Boundary","Dense")
44602#@gui : Density (%) = float(80,0,100)
44603#@gui : Smoothness (%) = float(0.1,0,5)
44604#@gui : Threshold (%) = float(50,0,100)
44605#@gui : Light Position = point(50,50,0,1,255,255,0,-128,1%)
44606#@gui : Light Color = color(255,255,255)
44607#@gui : Blend Mode = choice(7,"Add","Alpha","Grain Merge","Hard Light","Lighten","Lightness",
44608#@gui : "Luminance","Overlay","Soft Light","Value")
44609#@gui : Opacity (%) = float(100,0,100)
44610#@gui : sep = separator()
44611#@gui : Preview Light Shape = bool(1)
44612#@gui : sep = separator()
44613#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/04/06</i>.</small>")
44614fx_guided_lightrays :
44615  bmode=${"arg0 $12,add,alpha,grainmerge,hardlight,lighten,lchlightness,\
44616           luminance,overlay,softlight,value"}
44617  if $3 cond="i" else cond="i && !(j(-1) && j(1) && j(0,-1) && j(0,1))" fi
44618  if 0$_is_preview" && "$14 i[1] [0] gui_set_layer_opacity[1] 50 fi
44619
44620  l[0] # Process top layer only (light shape).
44621    if s==2" || "s==4 channels 100% else compose_channels max fi
44622    ge $6%
44623
44624    # Draw lightrays.
44625    100%,100%
44626    eval.. "*
44627      const L = $2<1?$2:$2^5;
44628      const Xl = $7<0?3*$7:$7>100?100+3*($7-100):$7;
44629      const Yl = $8<0?3*$8:$8>100?100+3*($8-100):$8;
44630      const xl = Xl*(w - 1)/100;
44631      const yl = Yl*(h - 1)/100;
44632      "$cond" && u<=$4%?(
44633        u = x - xl; v = y - yl;
44634        polygon(#-1,2,xl,yl,xl + L*u,yl + L*v,-1,255);
44635     )"
44636
44637    equalize. 65536,1,{iM} n. 0,1 power={10^(-$1%)} pow. {max(1e-2,$power)} b. $5% n. 0,255
44638    i[-2] (${9-11}:cyzx) r.. .,.,1,3 a[-2,-1] c
44639    k. gui_set_layer_mode $bmode gui_set_layer_opacity $13
44640  endl
44641  if 0$_is_preview gui_merge_layers
44642  elif $_output_mode k[0]
44643  fi
44644
44645fx_guided_lightrays_preview :
44646  _is_preview=1
44647  fx_guided_lightrays $*
44648
44649#@gui Illuminate 2D Shape : fx_illuminate_shape2d,fx_illuminate_shape2d_preview(1)+
44650#@gui : note = note("<small><b>Input / Output:</b></small>)
44651#@gui : Input Type = choice{"Single Opaque Shapes Over Transp. BG","Multiple Colored Shapes Over Transp. BG",
44652#@gui : "Bump Map","Normal Map"}
44653#@gui : Output Type = choice{"Illumination","Bump Map","Normal Map"}
44654#@gui : Input Guide Color = color(255,0,0,255)
44655#@gui : Keep Base Layer as Input Background = bool(1)
44656#@gui : Keep Transparency in Output = bool(1)
44657#@gui : sep = separator()
44658#@gui : note = note("<small><b>Shape:</b></small>)
44659#@gui : Minimal Shape Area = int(4,1,100)
44660#@gui : note = note{"<small>Parameter <i>Minimal shape area</i> is only active in <i>Multiple colored shapes</i>
44661#@gui : input mode.</small>"}
44662#@gui : Preview Detected Shapes = bool(0)
44663#@gui : Erosion / Dilation = float(0,-10,10)
44664#@gui : Smoothness = float(3,0,6)
44665#@gui : Bump Factor = float(1,-5,5)
44666#@gui : Avg / Max Weight = float(1,0,1)
44667#@gui : Resolution = choice{4,"Full (Slower)","2048","1024","512","256","128","64 (Faster)"}
44668#@gui : sep = separator()
44669#@gui : note = note("<small><b>Illumination:</b></small>)
44670#@gui : Blending Mode = choice(10,"Normal","Lighten","Screen","Dodge","Add","Darken","Multiply","Burn","Overlay",
44671#@gui : "Soft Light","Hard Light","Grain Merge")
44672#@gui : Opacity (%) = float(75,0,100)
44673#@gui : Ambient (%) = float(30,-100,100)
44674#@gui : Diffuse (%) = float(40,0,200)
44675#@gui : Specular (%) = float(40,0,300)
44676#@gui : Shininess = float(80,0,100)
44677#@gui : Smoothness = float(0.2,0,5)
44678#@gui : Flatness = float(1,0,3)
44679#@gui : Linearity = float(0,-100,100)
44680#@gui : Levels = int(0,0,16)
44681#@gui : Light-X = float(2,-20,20)
44682#@gui : Light-Y = float(-2,-20,20)
44683#@gui : Light-Z = float(2,0,20)
44684#@gui : Normalize Illumination = bool(0)
44685#@gui : sep = separator()
44686#@gui : Open Interactive Preview = button()
44687#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44688#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44689#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44690#@gui : sep = separator()
44691#@gui : note = note{"<small><b>Note:</b> This filter automatically adds illumination to an opaque shape defined
44692#@gui : over a transparent background.</small>"}
44693#@gui : sep = separator()
44694#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/05/18</i>.</small>")
44695fx_illuminate_shape2d :
44696  input_type,\
44697  output_type,\
44698  keep_input_bg,\
44699  preview_shapes,\
44700  blending_mode,\
44701  opacity=$1,$2,$7,$10,$16,$17
44702  blending_mode=${arg\ 1+$blending_mode,normal,lighten,screen,dodge,add,darken,multiply,burn,overlay,\
44703                  softlight,hardlight,grainmerge}
44704  keep_input_bg&={$!>1}
44705
44706  if $output_type # Output : bumpmap or normalmap
44707    repeat $!-$keep_input_bg _fx_illuminate_shape2d[$>] $* done
44708
44709  else # Output : illumination
44710    repeat $!-$keep_input_bg if $keep_input_bg sel=$>,-1 else sel=$> fi l[$sel]
44711      if !$keep_input_bg" && "$input_type>=2 # From bumpmap/normalmap w/o background
44712        _fx_illuminate_shape2d $*
44713      elif $keep_input_bg" && "$input_type>=2 # From bumpmap/normalmap w/ background
44714        _fx_illuminate_shape2d[0] $*
44715      elif !$keep_input_bg" && "$input_type<=1 # From shape w/o background
44716        +_fx_illuminate_shape2d $* rv
44717      else # From shape w/ background
44718        _fx_illuminate_shape2d[0] $*
44719      fi
44720      if !$preview_shapes" || "0$_is_preview!=1
44721        gui_set_layer_mode[0] $blending_mode
44722        gui_set_layer_opacity[0] $opacity
44723        if $!>1" && "(0$_output_mode==0" || "0$_is_preview==1)
44724          if $keep_input_bg" && "!0$_is_preview . fi
44725          gui_merge_layers[0,1]
44726        else k[0] fi
44727      fi
44728    endl done
44729  fi
44730
44731fx_illuminate_shape2d_preview :
44732  _is_preview=1
44733  input_type,\
44734  keep_input_bg,\
44735  preview_interactive=$1,$7,$-2
44736  keep_input_bg&={$!>1}
44737  if $preview_interactive fx_illuminate_shape2d_preview_interactive $* fi
44738  if $keep_input_bg
44739    repeat $!-1 l[$>,-1]
44740      fx_illuminate_shape2d $*
44741      rv to_colormode 0 a z
44742      gui_split_preview "slices 50%,100%",$-1
44743    endl done
44744  else
44745    gui_split_preview "fx_illuminate_shape2d $*",$-1
44746  fi
44747
44748fx_illuminate_shape2d_preview_interactive :
44749  _output_mode=0
44750  input_type,\
44751  keep_input_bg=$1,$7
44752  keep_input_bg&={$!>1}
44753  repeat $!-$keep_input_bg if $keep_input_bg sel=$>,-1 else sel=$> fi +l[$sel]
44754    to_rgba
44755    +_fx_illuminate_shape2d[0] $1,2,${3-6},0,1,""$9,0,${11-15},""${16-29},""0,0
44756    if $!>2 rm[0] elif $input_type>=2 sh[0] 0,2 f. 128 rm. fi
44757    siz=${fitscreen\ {[w,h,1]},256,640}
44758    wsiz0=${fitscreen\ $siz,1,30%,100%}
44759    wsiz=$wsiz0
44760    r $siz,1,100%,3
44761    s. c,-3 !=. 0 l.. - 128 / 127 s c,-2 / endl a[-2,-1] c # Gradient map
44762    rv s. c,-3
44763    (160,128;128,160) r. 16,16 r. ..,..,1,3,0,2
44764    30,30,1,1 circle. 50%,50%,15%,1,1 b. 4 n. 0,1
44765    100%,100%,1,3,[255,255,0]
44766    nm normal,rgb,alpha,background,light_alpha,light_rgb
44767
44768    w[] $wsiz,0,0,{rgb,([{*,u},{*,v}]-[$wsiz])/2},"[G'MIC] Illuminate 2D Shape"
44769    cursor 0
44770    x0,y0,ox,oy,ob,olightz=-1
44771    lightz=2 clicked=0
44772
44773    do
44774      x,y,b,mw={rgb,[{*,x},{*,y}]*[w,h]/[{*,w},{*,h}]},{*,b},{*,-o}
44775      lightz={cut($lightz-0.3*sign($mw)+($y0>=0?3*($y-$y0)/h),0.1,4)}
44776      if $x<0 x,y={rgb,ang=$|;(1+[cos(1.4*ang),sin(0.85*ang)])*[w,h]/2} fi
44777      if !$b" || "($b&1)
44778        if $b" && "!$clicked x0,y0=$x,$y
44779        elif !$b x0,y0=-1
44780        fi
44781        lightx,lighty={rgb,3.5*(2*[$x/w,$y/h]-1)}
44782        if [$ox,$oy,$ob,$olightz]!=[$x,$y,$b,$lightz]
44783          +fx_illuminate_shape2d[normal,rgb] 4,0,${3-6},1,1,""$9,0,${11-15},""${16-25},$lightx,$lighty,$lightz,$29,""0,0
44784          +j[background] .,0,0,0,0,1,[alpha],255 rm..
44785          +r2dx[light_alpha,light_rgb] {light_rgb,8+$lightz*(w-8)} j... .,{[$x,$y]-[w,h]/2},0,0,1,.. rm[-2,-1]
44786          r. $wsiz,1,100% to. "Light: ("{``{_round([$lightx,$lighty,$lightz],0.1)}}")",2,2,16
44787          w.  rm. wait 20
44788        else wait
44789        fi
44790        clicked=$b
44791      elif $b&2
44792        +j[background] [rgb],0,0,0,0,1,[alpha],255
44793        +r2dx[light_alpha,light_rgb] {light_rgb,8+$lightz*(w-8)} j... .,{[$x,$y]-[w,h]/2},0,0,1,.. rm[-2,-1]
44794        w. rm. wait
44795      fi
44796      if {*,CTRLLEFT}" && "{*,-D} w[] {1.5*[{*,w},{*,h}]} wsiz={*,w},{*,h}
44797      elif {*,CTRLLEFT}" && "{*,-C} w[] {0.75*[{*,w},{*,h}]} wsiz={*,w},{*,h}
44798      elif {*,CTRLLEFT}" && "{*,-R} w[] $wsiz0
44799      fi
44800      ox,oy,ob=$x,$y,$b
44801
44802    while {*}" && "!{*,ESC}" && "!{*,Q}
44803    w 0
44804  rm endl done
44805
44806_fx_illuminate_shape2d : # Input selection must contains a single image. Output is a single image.
44807  input_type,\
44808  output_type,\
44809  gR,gG,gB,gA,\
44810  keep_input_bg,\
44811  keep_output_transparency,\
44812  min_shape_area,\
44813  preview_shapes,\
44814  dilation,\
44815  shape_smoothness,\
44816  bump_factor,\
44817  weight_avg_max,\
44818  resolution,\
44819  blending_mode,\
44820  opacity,\
44821  ambient,\
44822  diffuse,\
44823  specular,\
44824  shininess,\
44825  light_smoothness,\
44826  flatness,\
44827  linearity,\
44828  levels,\
44829  lightx,\
44830  lighty,\
44831  lightz,\
44832  normalize_illumination,\
44833  preview_interactive,\
44834  preview_mode=${1-31}
44835
44836  # Generate 2D binary shape and corresponding bumpmap
44837  nm={n}
44838  if $input_type==0 # Single opaque shape
44839    to_rgba
44840    +channels. 100% >. 0 .
44841    select_color... 0,$gR,$gG,$gB,$gA
44842    mv... $! -[-2,-1]
44843
44844  elif $input_type==1 # Multiple colored shapes
44845    to_rgba
44846    +channels. 100% >. 0 *[-2,-1]
44847    if $min_shape_area>1 +quantize_area. {$min_shape_area^2} fi
44848    s. c,-{s-1} >. 0 rv[-2,-1]
44849    if s>1 f. "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm. round. 0.01 fi
44850    label. 0,0 f. "j(1)!=i || j(0,1)!=i" thinning. 1 ==. 0 *. ..
44851    select_color... 0,$gR,$gG,$gB,$gA
44852    mv... $! -[-2,-1]
44853
44854  elif $input_type==2 # Bump map
44855    to_a
44856    s c,-{s-1} >. 0 *.. . rv s. c S={$!-1} +[^0] /. $S
44857
44858  elif $input_type==3 # Normal map
44859    +channels 100% >. 0 *.. . rv
44860    f. "I==vector4(0)?[128,128,255,255]:I"
44861    channels. 0,2
44862
44863  else # Gradient map (hidden mode used by interactive preview)
44864    +channels 100% rv
44865  fi
44866
44867  if 0$_is_preview" && "$preview_shapes
44868    if $input_type==3 k[0] else k. fi
44869    +dilate. 3
44870    label_fg.. 0 srand 0 {-2,iM+1},1,1,3,'x==0?[0,0,0]:x==1?[255,255,255]:u([255,255,255])' map... . rm.
44871    *. 255 a c
44872    return
44873  fi
44874
44875  if $input_type<=1
44876    shape2bump. {arg($resolution,2048,1024,512,256,128,64)},$weight_avg_max,{$dilation%*max(w,h)},\
44877                {$shape_smoothness*50}
44878  fi
44879  if $input_type<=2
44880    if $input_type==2" && "$shape_smoothness mM={[im,iM]} guided. ..,$shape_smoothness%,100 n. $mM fi
44881    *. $bump_factor
44882  fi
44883
44884  # Generate output.
44885  if $output_type==1 # Output as a bump map
44886    if $input_type<=2
44887       if $keep_output_transparency k[-2,-1] n 0,255 rv a c  # With transparency
44888       else k. n 0,255                                       # Without transparency
44889       fi
44890    else
44891      rm gui_error_preview "Cannot convert a normal map to a bump map." return
44892    fi
44893
44894  elif $output_type==2 # Output as a normal map
44895    if $input_type<=2 round 0.0001 bump2normal. f. "i(#-2)?I:[128,128,255]" fi
44896    if $keep_output_transparency k[-2,-1] rv *. 255 a c # With transparency
44897    else k.                                             # Without transparency
44898    fi
44899
44900  else # Output as illumination layer (phong model)
44901    if $input_type<=2 g. xy a[-2,-1] c
44902    elif $input_type==3 -. 128 /. 127 s. c,-2 /[-2,-1]
44903    fi
44904    f. "*
44905      begin(
44906        const flatness = "$flatness";                 # Surface flatness
44907        const ka = "$ambient"%;                       # Ambient
44908        const kd = "$diffuse"%;                       # Diffuse
44909        const ks = "$specular"%;                      # Specular
44910        const alpha = "$shininess";                   # Specularity
44911        const m1 = max(1,"$lightz");
44912        const mwh1 = max(w,h) - 1;
44913        light = [ "m1*$lightx,m1*$lighty,-$lightz" ]; # Light position
44914        camera = [ 0,0,-"$lightz" ];                  # Camera position
44915      );
44916      res = i#0?(
44917        P = [ 2*x/mwh1 - 1,2*y/mwh1 - 1,0 ];
44918        L = light - P;
44919        L/=norm(L);
44920        V = camera - P;
44921        V/=norm(V);
44922        N = -[ i0,i1,flatness ];
44923        N/=norm(N);
44924        R = 2*dot(N,L)*N - L;
44925        res = ka + kd*dot(L,N) + ks*max(dot(R,V),0)^alpha;
44926      ):0;
44927      [ res,0 ]"
44928    channels. 0 *. 255 c. 0,255
44929    if $light_smoothness" || "$linearity
44930      mM={[im,iM]}
44931      if $light_smoothness b. $light_smoothness% fi
44932      if $linearity n. 0,1 ^. {10^-($linearity%)} fi
44933      n. $mM
44934    fi
44935    if $levels quantize. $levels,1,1 fi
44936    if $normalize_illumination n. 0,255 fi
44937    rv[-2,-1] *. 255 a[-2,-1] c
44938    nm $nm
44939    if !$keep_output_transparency remove_opacity. fi
44940  fi
44941  nm $nm
44942
44943#@gui Light Glow : fx_lightglow, fx_lightglow_preview(0)
44944#@gui : Density = float(30,0,100)
44945#@gui : Amplitude = float(0.5,0,2)
44946#@gui : Mode = choice(8,"Burn","Dodge","Freeze","Grain Merge","Hard Light","Interpolation","Lighten","Multiply",
44947#@gui : "Overlay","Reflect","Soft Light","Stamp","Value")
44948#@gui : Opacity = float(0.8,0,1)
44949#@gui : sep = separator()
44950#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44951#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44952#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44953#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44954#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44955#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44956#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44957#@gui : sep = separator()
44958#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44959#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44960#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44961#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44962#@gui : sep = separator()
44963#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/21/02</i>.</small>")
44964_fx_lightglow :
44965  mode=${arg\ 1+$3,burn,dodge,freeze,grainmerge,hardlight,interpolation,lighten,multiply,overlay,reflect,\
44966         softlight,stamp,value}
44967  repeat $!
44968    +gradient_norm. >=. {100-$1}% distance. 1 ^. $2 *. -1 n. 0,255 blend $mode,$4
44969  mv. 0 done
44970
44971fx_lightglow :
44972  ac "_fx_lightglow ${1-4}",$5
44973
44974fx_lightglow_preview :
44975  gui_split_preview "fx_lightglow $*",${-3--1}
44976
44977#@gui Light Leaks : fx_light_leaks, fx_light_leaks_preview(1)
44978#@gui : Leak Type = int(0,0,70)
44979#@gui : Angle = float(0,-180,180)
44980#@gui : X-Scale = float(1,1,10)
44981#@gui : Y-Scale = float(1,1,10)
44982#@gui : Hue = float(0,-180,180)
44983#@gui : Opacity = float(0.85,0,1)
44984#@gui : Blend Mode = choice(2,"Normal","Lighten","Screen","Dodge","Add","Darken","Multiply","Burn","Overlay",
44985#@gui : "Soft Light","Hard Light","Difference","Subtract","Grain Extract","Grain Merge","Divide","Hue","Saturation",
44986#@gui : "Value")
44987#@gui : Output as Separate Layers = _bool(1)
44988#@gui : sep = separator()
44989#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44990#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44991#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44992#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44993#@gui : sep = separator()
44994#@gui : note = note{"<small>This filter uses the free light leaks dataset available at :</small>"}
44995#@gui : url = link{"Lomo Light Leaks","http://www.photoshoptutorials.ws/downloads/mockups-graphics/lomo-light-leaks/"}
44996#@gui : sep = separator()
44997#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/01/07</i>.</small>")
44998fx_light_leaks :
44999  filename=lightleak_${"padint $1",6}.cimgz
45000  if isfile(['{/${-path_cache}$filename}']) i ${-path_cache}$filename
45001  else i https://gmic.eu/data_lightleaks/$filename o. ${-path_cache}$filename
45002  fi
45003  mode=${arg\ 1+$7,normal,lighten,screen,dodge,add,darken,multiply,burn,overlay,softlight,hardlight,difference,\
45004         subtract,grainextract,grainmerge,divide,hue,saturation,value}
45005  mv. 0
45006  repeat $!-1 l[0,{1+$<}]
45007    +r[0] {1,w},{1,h},1,3,5
45008    rotate. $2,1,1,50%,50%
45009    if $3>1" || "$4>1 f. 'w2=w/2;h2=h/2;X=x-w2;Y=y-h2;i(w2+X/$3,h2+Y/$4,0,c,1,0)' fi
45010    c. 0,255
45011    if $5 rgb2hsv. sh. 0 +. $5 rm. hsv2rgb. fi
45012    if $8
45013      nm=${gui_layer_name[1]}
45014      nm. name($nm),opacity({$6*100}),mode($mode) rv[-2,-1]
45015    else blend[1,-1] $mode,$6 fi
45016  endl done
45017  rm[0]
45018
45019fx_light_leaks_preview :
45020  gui_split_preview "fx_light_leaks ${1--5},0",${-3--1}
45021
45022_fx_light_leaks :
45023  u="" repeat 71 if narg($u) u=$u, fi u=${u}lightleak_${"padint "$>,6} done
45024  u $u
45025
45026#@gui Light Patch : fx_light_patch, fx_light_patch(0)
45027#@gui : Density = int(5,2,30)
45028#@gui : Darkness = float(0.7,0,1)
45029#@gui : Lightness = float(2.5,1,4)
45030#@gui : sep = separator()
45031#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45032#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45033#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45034#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45035#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45036#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45037#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45038#@gui : sep = separator()
45039#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45040fx_light_patch :
45041  repeat $! l[$>] split_opacity l[0]
45042    ac "light_patch $1,$2,$3",$4
45043  endl a c endl done
45044
45045#@gui Light Rays : fx_lightrays, fx_lightrays(1)
45046#@gui : Density = float(80,0,100)
45047#@gui : Center (%) = point(50,50,0,1)
45048#@gui : Length = float(1,0,1)
45049#@gui : Attenuation = float(0.5,0,1)
45050#@gui : Transparency = bool(0)
45051#@gui : sep = separator()
45052#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/03/01</i>.</small>")
45053fx_lightrays :
45054  lightrays $1,$2%,$3%,$4,$5
45055  if $6 repeat $! r[$>] 100%,100%,1,{{$>,s}+({$>,s}%2)} done fi
45056
45057#@gui Pop Shadows : fx_pop_shadows, fx_pop_shadows_preview(1)
45058#@gui : Strength = float(0.75,0,1)
45059#@gui : Scale = float(5,0,20)
45060#@gui : Post-Normalize = bool(1)
45061#@gui : sep = separator()
45062#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45063#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45064#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45065#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45066#@gui : sep = separator()
45067#@gui : note = note("<small>Authors: <i>Morgan Hardwood</i> and <i>David Tschumperlé</i>.
45068#@gui :       Latest Update: <i>2017/03/05</i>.</small>")
45069fx_pop_shadows :
45070  repeat $! l[$>] split_opacity l[0]
45071    .x2
45072    luminance.. negate.. imM={-2,[im,iM]} b.. $2% n.. $imM
45073    blend[0,1] overlay,$1
45074    max
45075    if $3 n 0,255 fi
45076  endl a c endl done
45077
45078fx_pop_shadows_preview :
45079  gui_split_preview "fx_pop_shadows $*",${-3--1}
45080
45081#@gui Relief Light : fx_light_relief, fx_light_relief(1)
45082#@gui : Ambient Lightness = float(0.3,0,5)
45083#@gui : Specular Lightness = float(0.2,0,2)
45084#@gui : Specular Size = float(0.2,0,1)
45085#@gui : Darkness = float(0,0,1)
45086#@gui : Light Smoothness = float(1,0,5)
45087#@gui : XY-Light = point(50,50,0,1,255,255,128,200,10)
45088#@gui : Z-Light = float(5,0,20)
45089#@gui : Z-Scale = float(0.5,0,3)
45090#@gui : Opacity as Heightmap = bool(0)
45091#@gui : Image Smoothness = float(0,0,10)
45092#@gui : sep = separator()
45093#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45094fx_light_relief :
45095  b $11% light_relief ${1-5},{[$6,$7]%},${8-10}
45096
45097#@gui Shadow Patch : fx_shadow_patch, fx_shadow_patch(1)
45098#@gui : Opacity = float(0.7,0,1)
45099#@gui : sep = separator()
45100#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45101#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45102#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45103#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45104#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45105#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45106#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45107#@gui : sep = separator()
45108#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45109fx_shadow_patch :
45110  repeat $! l[$>] split_opacity l[0]
45111    ac "shadow_patch $1",$2
45112  endl a c endl done
45113
45114#@gui Slice Luminosity : fx_slice_luminosity, fx_slice_luminosity_preview
45115#@gui : Luminosity Type = choice(1,"Average RGB","Luminance","Lightness","Value")
45116#@gui : Output As = _choice(1,"Mask","Masked Image")
45117#@gui : Preview Type = choice(2,"Mask","Mask + Background","Image","Image + Background")
45118#@gui : sep = separator()
45119#@gui : note = note{"<small><b>Slice 1</b> (shadows):</small>"}
45120#@gui : Activate Slice 1 = bool(1)
45121#@gui : Starting Value = int(0,0,255)
45122#@gui : Ending Value = int(64,0,255)
45123#@gui : Starting Feathering = int(0,0,255)
45124#@gui : Ending Feathering = int(0,0,255)
45125#@gui : sep = separator()
45126#@gui : note = note{"<small><b>Slice 2</b> (low midtones):</small>"}
45127#@gui : Activate Slice 2 = bool(1)
45128#@gui : Starting Value = int(64,0,255)
45129#@gui : Ending Value = int(128,0,255)
45130#@gui : Starting Feathering = int(0,0,255)
45131#@gui : Ending Feathering = int(0,0,255)
45132#@gui : sep = separator()
45133#@gui : note = note{"<small><b>Slice 3</b> (high midtones):</small>"}
45134#@gui : Activate Slice 3 = bool()
45135#@gui : Starting Value = int(128,0,255)
45136#@gui : Ending Value = int(192,0,255)
45137#@gui : Starting Feathering = int(0,0,255)
45138#@gui : Ending Feathering = int(0,0,255)
45139#@gui : sep = separator()
45140#@gui : note = note{"<small><b>Slice 4</b> (highlights):</small>"}
45141#@gui : Activate Slice 4 = bool()
45142#@gui : Starting Value = int(192,0,255)
45143#@gui : Ending Value = int(255,0,255)
45144#@gui : Starting Feathering = float(0,0,255)
45145#@gui : Ending Feathering = float(0,0,255)
45146#@gui : sep = separator()
45147#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/22/09</i>.</small>")
45148fx_slice_luminosity :
45149  remove_opacity repeat $! l[$<] to_rgb
45150    _fx_slice_luminosity $*
45151    if $2 i[0] [0] a[-2,-1] c fi
45152    rv
45153  endl done
45154
45155fx_slice_luminosity_preview :
45156  remove_opacity  repeat $! l[$>] to_rgb
45157    _fx_slice_luminosity $*
45158    if $3==0 rm[0] channels {s-1}
45159    elif $3==1 100%,100%,1,1,128 a[0,-1] c r. 100%,100%,1,4 blend alpha
45160    elif $3==2 a c
45161    else +. 96 c. 0,255 a c
45162    fi
45163  endl done
45164
45165_fx_slice_luminosity :
45166  if $1==0 +compose_channels + /. 3
45167  elif $1==1 +luminance
45168  elif $1==2 +srgb2lab8. channels. 0
45169  else +compose_channels max
45170  fi
45171  if $4 +apply_curve[1] 0,{$5-$7-0.1},0,$5,255,$6,255,{$6+$8+0.1},0,512,0 fi
45172  if $9 +apply_curve[1] 0,{$10-$12-0.1},0,$10,255,$9,255,{$11+$13+0.1},0,512,0 fi
45173  if $14 +apply_curve[1] 0,{$15-$17-0.1},0,$15,255,$16,255,{$16+$18+0.1},0,512,0 fi
45174  if $19 +apply_curve[1] 0,{$20-$22-0.1},0,$20,255,$21,255,{$21+$23+0.1},0,512,0 fi
45175  rm[1] max[^0]
45176
45177#@gui ____<b>Patterns</b>
45178#------------------------
45179
45180#@gui Bayer Filter : rgb2bayer, rgb2bayer(0)
45181#@gui : Starting Pattern = choice(0,"Red-Green","Blue-Green","Green-Red","Green-Blue")
45182#@gui : Keep Colors = bool(1)
45183#@gui : sep = separator()
45184#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45185
45186#@gui Box Fitting : fx_boxfitting, fx_boxfitting_preview(0)
45187#@gui : Minimal Size = int(3,1,32)
45188#@gui : Maximal Size = int(0,0,32)
45189#@gui : note = note("<small><b>Note:</b> Set <i>Maximal size</i> to <i>0</i> to allow any size
45190#@gui : for the squares.</small>")
45191#@gui : Initial Density = float(0.1,0,1)
45192#@gui : Transparency = bool(0)
45193#@gui : sep = separator()
45194#@gui : note = note("<small><b>Note:</b> This filter has been highly inspired by the work of Jared Tarbell,
45195#@gui : described on the page:</small>")
45196#@gui : url = link("http://www.complexification.net/gallery/machines/boxFittingImg/")
45197#@gui : sep = separator()
45198#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/06/06</i>.</small>")
45199fx_boxfitting :
45200  boxfitting ${1-3},3
45201  if $4 to_rgba replace_color 0,0,0,0,0,255,0,0,0,0 fi
45202
45203fx_boxfitting_preview :
45204  boxfitting ${1-3},1
45205  if $4 to_rgba replace_color 0,0,0,0,0,255,0,0,0,0 fi
45206
45207#@gui Camouflage : fx_camouflage, fx_camouflage
45208#@gui : Scale = int(9,2,12)
45209#@gui : Levels = int(12,2,32)
45210#@gui : Coherence = float(100,0,1000)
45211#@gui : Color 1 = color(30,46,33)
45212#@gui : Color 2 = color(75,90,65)
45213#@gui : Color 3 = color(179,189,117)
45214#@gui : Color 4 = color(255,246,158)
45215#@gui : sep = separator()
45216#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/26/10</i>.</small>")
45217fx_camouflage :
45218  repeat $! l[$>] split_opacity l[0]
45219    channels 0 r {w+16},{h+16},1,1,0 rand 0,16
45220    amp=$3 do smooth {min(50,$amp)},0,1 amp-=50 while $amp>0
45221    shrink_xy. 8 n 1,$2 round
45222    repeat $1 +area 0,0 <. {1+2^$>} inpaint[0] [1],0,3 rm. done
45223    +colormap 0 n.. 0,{w-1}
45224    4,1,1,3,"col=[${4-15}];col[3*x,3]"
45225    r. ..,..,1,3,3 rm.. map.. . rm.
45226  endl a c endl done
45227
45228#@gui Canvas : fx_canvas, fx_canvas_preview(0)
45229#@gui : note = note{"<b>First direction :</b>"}
45230#@gui : Amplitude = float(70,0,300)
45231#@gui : Angle = float(45,0,180)
45232#@gui : Sharpness = float(400,0,2000)
45233#@gui : note = note{"\n<b>Second direction : </b>"}
45234#@gui : Activate Second Direction = bool(true)
45235#@gui : Amplitude = float(70,0,300)
45236#@gui : Angle = float(135,0,180)
45237#@gui : Sharpness = float(400,0,2000)
45238#@gui : sep = separator()
45239#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45240#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45241#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45242#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45243#@gui : sep = separator()
45244#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45245fx_canvas :
45246  repeat $! l.
45247    if $4
45248      ({cos($2*pi/180)}^{sin($2*pi/180)}) vector2tensor. r. ..,.. +smooth.. .,$1 rm.. sharpen. $3 c. 0,255
45249      ({cos($6*pi/180)}^{sin($6*pi/180)}) vector2tensor. r. ..,.. smooth... .,$5 rm. sharpen.. $7 c.. 0,255
45250      +[-2,-1] /. 2
45251    else
45252      ({cos($2*pi/180)}^{sin($2*pi/180)}) vector2tensor. r. ..,.. smooth.. .,$1 rm. sharpen. $3 c. 0,255
45253    fi
45254  endl mv. 0 done
45255
45256fx_canvas_preview :
45257  gui_split_preview "fx_canvas $*",${-3--1}
45258
45259#@gui Canvas Texture : texturize_canvas, texturize_canvas(0)
45260#@gui : Amplitude = float(20,0,256)
45261#@gui : Fibrousness = float(3,0,20)
45262#@gui : Emboss = float(0.6,0,1)
45263#@gui : sep = separator()
45264#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45265
45266#@gui Cracks : fx_cracks, fx_cracks_preview(0)
45267#@gui : Density (%) = float(30,0,100)
45268#@gui : Relief = bool(true)
45269#@gui : Color = color(255,255,255,128)
45270#@gui : sep = separator()
45271#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45272#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45273#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45274#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45275#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45276#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45277#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45278#@gui : sep = separator()
45279#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45280#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45281#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45282#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45283#@gui : sep = separator()
45284#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/07</i>.</small>")
45285fx_cracks :
45286  ac "cracks $1,$2,{$6/255},${3-5},255",$7
45287
45288fx_cracks_preview :
45289  gui_split_preview "fx_cracks $*",${-3--1}
45290
45291#@gui Crystal : fx_crystal, fx_crystal_preview(0)
45292#@gui : Density = float(50,0,100)
45293#@gui : Smoothness = float(0.2,0,2)
45294#@gui : Edges = float(20,0,100)
45295#@gui : sep = separator()
45296#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45297#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45298#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45299#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45300#@gui : sep = separator()
45301#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/19/01</i>.</small>")
45302fx_crystal :
45303  repeat $! l[$>] split_opacity l[0]
45304    s={s}
45305    +gradient_norm >=. {(100-$3)/5} remove_pixels. {100-max(0.1,$1*$3%)}%,{is} *
45306    +norm !=. 0 a c
45307    sigma=0.2
45308    do
45309      +b. $sigma sigma*={(1+$2)}
45310      sh[0,-1] $s max. .. rm[-2,-1]
45311      f. 'W=i(x,y,z,$s);if(W<0.001||W>=1,0,if(c<$s,i/W,1))'
45312      if !iM rm[1] break fi
45313      sh. $s
45314      j[0] [1],0,0,0,0,1,[2] k[0]
45315    while 1
45316    channels 0,{$s-1}
45317  endl a c endl done
45318
45319fx_crystal_preview :
45320  gui_split_preview "fx_crystal $*",${-3--1}
45321
45322#@gui Crystal Background : fx_crystal_background, fx_crystal_background
45323#@gui : Iterations = int(10,1,32)
45324#@gui : Density (%) = float(25,0,100)
45325#@gui : Random Seed = int(0,0,65535)
45326#@gui : Opacity (%) = float(100,0,100)
45327#@gui : Color = bool(1)
45328#@gui : sep = separator()
45329#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/18/10</i>.</small>")
45330fx_crystal_background :
45331  repeat $! l[$>] split_opacity l[0]
45332    N={2*max(3,round((${"is_percent $2"}?4*wh*$2:$2)))}
45333    if $5 col="u([255,255,255])" else col="u(255)" fi
45334    srand $3 M={max(w,h)} 2,$N
45335    repeat $1 rand. {-$M/2},{3*$M/2} polygon.. $N,{^},{-$4%},{$col} done
45336    rm. n 0,255
45337  endl a c endl done
45338
45339#@gui Halftone : fx_halftone, fx_halftone_preview(0)
45340#@gui : note = note("<b><small>Image parameters :</small></b>")
45341#@gui : Brightness (%) = float(0,-100,100)
45342#@gui : Contrast (%) = float(0,-100,100)
45343#@gui : Gamma (%) = float(0,-100,100)
45344#@gui : Smoothness = float(0,0,10)
45345#@gui : sep = separator()
45346#@gui : note = note("<b><small>Halftone parameters :</small></b>")
45347#@gui : Number of Tones = int(5,2,32)
45348#@gui : Size for Dark Tones = int(8,2,256)
45349#@gui : Size for Bright Tones = int(8,2,256)
45350#@gui : Shape = choice{5,"Square","Diamond","Circle","Square (Inv.)","Diamond (Inv.)","Circle (Inv.)"}
45351#@gui : Smoothness = float(0.1,0,32)
45352#@gui : sep = separator()
45353#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45354#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45355#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45356#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45357#@gui : sep = separator()
45358#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/23/07</i>.</small>")
45359fx_halftone :
45360  adjust_colors ${1-3},0,0,0,255 b $4
45361  repeat $! l[$>] split_opacity
45362    halftone[0] ${5-9}
45363  a c endl done
45364
45365fx_halftone_preview :
45366  gui_split_preview "fx_halftone $*",${-3--1}
45367
45368#@gui Hearts : fx_hearts, fx_hearts_preview(0)
45369#@gui : Density = float(2,0,30)
45370#@gui : sep = separator()
45371#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45372#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45373#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45374#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45375#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45376#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45377#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45378#@gui : sep = separator()
45379#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45380#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45381#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45382#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45383#@gui : sep = separator()
45384#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45385fx_hearts :
45386  ac "hearts $1",$2
45387
45388fx_hearts_preview :
45389  gui_split_preview "fx_hearts $*",${-3--1}
45390
45391#@gui Lava : fx_lava, fx_lava_preview(0)
45392#@gui : Perturbation = int(8,0,15)
45393#@gui : Smoothness = float(5,0,100)
45394#@gui : Scale = float(3,0,20)
45395#@gui : Sharpness = float(0,0,1000)
45396#@gui : sep = separator()
45397#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45398#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45399#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45400#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45401#@gui : sep = separator()
45402#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/26/11</i>.</small>")
45403fx_lava :
45404  repeat $! l[$>] split_opacity l[0] norm
45405    100%,100% plasma. 1,1,{16-$1} smooth. $2,0,1,$3,$3,0.8,90 *
45406    gradient_norm n 0,255
45407    equalize map 3
45408    sharpen $4
45409  endl a c endl done
45410
45411fx_lava_preview :
45412  gui_split_preview "fx_lava $*",${-3--1}
45413
45414#@gui Marble : fx_marble, fx_marble
45415#@gui : Image Weight = float(.5,0,30)
45416#@gui : Pattern Weight = float(1,0,30)
45417#@gui : Pattern Angle = float(0,0,360)
45418#@gui : Amplitude = float(0,0,1000)
45419#@gui : Sharpness = float(.4,0,5)
45420#@gui : Anisotropy = float(.6,0,1)
45421#@gui : Alpha = float(.6,0,20)
45422#@gui : Sigma = float(1.1,0,20)
45423#@gui : Cut Low = float(0,0,100)
45424#@gui : Cut High = float(100,0,100)
45425#@gui : sep = separator()
45426#@gui : note = note("<small>Author: <i>Preben Soeberg</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45427fx_marble :
45428  repeat $! l[$>] split_opacity l[0]
45429    marble $1/10,$2/10,$3,$4,$5,$6,$7,$8,$9%,$10%
45430  endl a c endl done
45431
45432#@gui Maze : fx_maze, fx_maze
45433#@gui : Cell Size = int(24,1,256)
45434#@gui : Thickness = int(1,1,10)
45435#@gui : Masking = choice("None","Render on Dark Areas","Render on White Areas")
45436#@gui : Preserve Image Dimension = bool(1)
45437#@gui : Maze Type = choice("Dark Walls","White Walls")
45438#@gui : sep = separator()
45439#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/02/09</i>.</small>")
45440fx_maze :
45441  repeat $! l[$>]
45442    w={w} h={h}
45443    r. {100/$1}%,{100/$1}%,1,100%,2
45444    if $3==0 f. 1
45445    elif $3==1 negate.
45446    fi
45447    maze_mask. $1 dilate. $2 *. 255
45448    if !$5 negate. fi
45449    if $4 r. $w,$h,100%,100% fi
45450  endl done
45451
45452#@gui Mineral Mosaic : fx_mineral_mosaic,fx_mineral_mosaic(0)
45453#@gui : Density = float(1,0,3)
45454#@gui : Area = float(2,0,32)
45455#@gui : Smoothness = float(1,0,10)
45456#@gui : Shade Strength = float(100,0,255)
45457#@gui : Shade Angle = float(0,0,360)
45458#@gui : sep = separator()
45459#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/01/02</i>.</small>")
45460fx_mineral_mosaic :
45461  repeat $! l[$>] to_rgb
45462    +b $3 segment_watershed. $1 +norm.
45463    area. 0 +<=. {$2^2} inpaint.. . rm. label.
45464    +f[0] 'if(c==0,x,y)' rv[-2,-1] +blend[-2,-1] shapeaverage,1,1
45465    -[-3,-1] rm[0,-2] channels. 0,1
45466    alpha={$5*pi/180} sh. 0 *. {cos($alpha)} rm. sh. 1 *. {sin($alpha)} rm. compose_channels. +
45467    normalize_local. 1000 n. -$4,$4
45468    + c 0,255
45469  endl done
45470
45471#@gui Mosaic : fx_mosaic, fx_mosaic_preview(0)
45472#@gui : Density (%) = float(50,0,100)
45473#@gui : sep = separator()
45474#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45475#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45476#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45477#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45478#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45479#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45480#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45481#@gui : sep = separator()
45482#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45483#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45484#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45485#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45486#@gui : sep = separator()
45487#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/19/07</i>.</small>")
45488fx_mosaic :
45489  ac "repeat $! l[$>] split_opacity mosaic[0] $1 a c endl done",$2
45490
45491fx_mosaic_preview :
45492  gui_split_preview "fx_mosaic $*",${-3--1}
45493
45494#@gui Op Art : fx_shapes,fx_shapes_preview(0)
45495#@gui : Shape = choice{1,"Custom Layers","Circles","Squares","Diamonds","Triangles","Horizontal Stripes",
45496#@gui : "Vertical Stripes","Balls","Hearts","Stars","Arrows","Truchet","Circles (Outline)","Squares (Outline)",
45497#@gui : "Diamonds (Outline)","Triangles (Outline)","Hearts (Outline)","Stars (Outline)","Arrows (Outline)"}
45498#@gui : Number of Scales = int(16,2,24)
45499#@gui : Resolution = float(10,1,50)
45500#@gui : Zoom Factor = _int(2,1,8)
45501#@gui : Minimal Size = float(5,0,150)
45502#@gui : Maximal Size = float(90,0,150)
45503#@gui : Stencil Type = choice(0,"Black & White","RGB","Color")
45504#@gui : Allow Angle = choice("0 deg.","90 deg.","180 deg.")
45505#@gui : Negative = bool(1)
45506#@gui : Antialiasing = bool(1)
45507#@gui : sep = separator()
45508#@gui : note = note{"<small><b>Note:</b>
45509#@gui : If you set the parameter <i>Shape</i> to <i>Custom layers</i>, the different shapes used to map
45510#@gui : the pixel intensities will be defined as
45511#@gui : the <i>Number of scales</i> top layers of your image. Don't forget to set also <i>Input layers</i> to
45512#@gui : <i>All</i> to be sure
45513#@gui : these layers are passed to the filter.
45514#@gui : </small>"}
45515#@gui : sep = separator()
45516#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45517#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45518#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45519#@gui : sep = separator()
45520#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/16/12</i>.</small>")
45521fx_shapes :
45522  if $1 # Pre-defined shapes.
45523    remove_opacity repeat $! l[$>]
45524      if !$7 _fx_shapes $* * 255
45525      elif $7==1 split_opacity to_rgb[0] s[0] c repeat 3 _fx_shapes[$>] $* done *[0-2] 255 a[0-2] c a c
45526      else +_fx_shapes $* r[0] $3%,$3% r[0] [1],[1] *
45527      fi
45528    endl done
45529  else # Custom shapes.
45530    if $!<=$2
45531      error[] "Command '$0': Some layers are missing in 'Custom layers' mode ("{$2+1}" expected at least, "\
45532              $!" provided)." fi
45533    to_colormode[0-{$2-1}] ${max_s[0-{$2-1}]} remove_opacity[$2--1]
45534    repeat $!-$2 l[0-{$2-1},{$2+$>}]
45535      norm. w={w} h={h} r. $3%,$3%,1,1,2
45536      s={$4*max(round($w/w),round($h/h))}
45537      r0={$s*$5%} r1={$s*$6%}
45538      repeat $2 r={round($r0+$>*($r1-$r0)/($2-1))} if $r +r[$>] $r,$r,1,100%,3 else 1,1 fi done
45539      r[-$2--1] $s,$s,1,100%,0,0,0.5,0.5
45540      map_sprites[$2--1] $2,$8
45541    endl done rm[0-{$2-1}]
45542  fi
45543
45544fx_shapes_preview :
45545  if $1 repeat $! l[$>]
45546    w={w} h={h}
45547    gui_split_preview "fx_shapes ${1-3},1,${5--2}",$-1
45548    r $w,$h,1,100%,0,0,0.5,0.5
45549    endl done
45550  else
45551    if $!>$2 repeat $!-$2 l[0-{$2-1},{$2+$>}]
45552      w={w} h={h}
45553      +fx_shapes ${1-3},1,${5--2} rm..
45554      r. $w,$h,1,100%,0,0,0.5,0.5
45555    endl done rm[0-{$2-1}]
45556    else gui_warning_preview "Missing input layers!"
45557    fi
45558  fi
45559
45560_fx_shapes :
45561  norm w={w} h={h} r $3%,$3%,1,1,2
45562  s={(1+$10)*$4*max(round($w/w),round($h/h))}
45563  r0={$s*$5%} r1={$s*$6%}
45564  repeat $2 r={round($r0+$>*($r1-$r0)/($2-1))} if $r _fx_shapes{$1-1}[] $r,$s else 1,1 fi done
45565  r[-$2--1] $s,$s,1,1,0,0,0.5,0.5
45566  if $9 rv[-$2--1] *[-$2--1] -1 +[-$2--1] 1 fi
45567  map_sprites $2,$8
45568  if $10 r 50%,50%,1,1,2 fi
45569
45570_fx_shapes0 :
45571  shape_circle $1
45572
45573_fx_shapes1 :
45574  $1,$1,1,1,1
45575
45576_fx_shapes2 :
45577  $1,$1,1,1 = 1,50%,50% distance 1,1 < {$1/2}
45578
45579_fx_shapes3 :
45580  $2,$2,1,1,'x+y<=2*$1-1'
45581
45582_fx_shapes4 :
45583  $2,$1,1,1,1
45584
45585_fx_shapes5 :
45586  $1,$2,1,1,1
45587
45588_fx_shapes6 :
45589  ball $1,200 n 0,1
45590
45591_fx_shapes7 :
45592  shape_heart 65 r $1,$1,1,1,2 >= 50%
45593
45594_fx_shapes8 :
45595  shape_star $1
45596
45597_fx_shapes9 :
45598  arrow3d 0,0,0,1,0,0,15%,40%,30% col3d 1 *3d $1 c3d
45599  $2,$2 j3d. ..,50%,50%,0,1,2,0,0
45600  rm.. +mirror y max
45601
45602_fx_shapes10 :
45603  S={$2+1-($2%2)}
45604  $S,$S,1,1,"X=x/(w-1);Y=y/(h-1);r=abs(0.5-sqrt(X^2+Y^2));a=atan2(y,x);r<0.1-0.17*(0.5-$1/$2)*sin(2*a)"
45605  +mirror xy max
45606
45607_fx_shapes11 :
45608  _fx_shapes0 $* expand_xy 1,0 +erode 3 -
45609
45610_fx_shapes12 :
45611  _fx_shapes1 $* expand_xy 1,0 +erode 3 -
45612
45613_fx_shapes13 :
45614  _fx_shapes2 $* expand_xy 1,0 +erode 3 -
45615
45616_fx_shapes14 :
45617  _fx_shapes3 $* expand_xy 1,0 +erode 3 -
45618
45619_fx_shapes15 :
45620  _fx_shapes7 $* expand_xy 1,0 +erode 3 -
45621
45622_fx_shapes16 :
45623  _fx_shapes8 $* expand_xy 1,0 +erode 3 -
45624
45625_fx_shapes17 :
45626  _fx_shapes9 $* expand_xy 1,0 +erode 3 -
45627
45628#@gui Pack Sprites : fx_pack_sprites, gui_no_preview
45629#@gui : Number of Scales = int(5,1,16)
45630#@gui : Minimal Scale (%) = float(25,1,100)
45631#@gui : Allow Angle = choice(3,"0 deg.","180 deg.","90 deg.","Any")
45632#@gui : Spacing = int(1,-16,16)
45633#@gui : Precision = int(7,1,32)
45634#@gui : sep = separator()
45635#@gui : Masking = choice("No Masking","Mask as Bottom Layer")
45636#@gui : Width = int(512,32,2048)
45637#@gui : Height = int(512,32,2048)
45638#@gui : note = note("<small><b>Notes:</b>\n - Parameters <i>Width</i> and <i>Height</i> are considered only when
45639#@gui : <i>No masking</i> mode is selected.\n
45640#@gui : - Set different sprites on different layers to pack multiple sprites at the same time.</small>")
45641#@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=bpg7CGH7vCM")
45642#@gui : sep = separator()
45643#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/24/06</i>.</small>")
45644fx_pack_sprites :
45645  if $6 # With mask.
45646    if $!<2 error[] "Command '$0': Masking requires at least two input layers !
45647                     (please check that 'Input Layers' is correctly set)." fi
45648    repeat $!-1 l[$>] to_rgba split_opacity +!=[1] 0 *[0] . a c autocrop 0 endl done
45649    remove_empty[0--2] +channels. 100% channels. -4,0 mv. 0
45650    pack_sprites[0--2] ${1-5}
45651  else # No masking
45652    repeat $! l[$>] to_rgba split_opacity +!=[1] 0 *[0] . a c autocrop 0 endl done
45653    remove_empty i[0] $7,$8,1,5 pack_sprites ${1-5}
45654  fi
45655  channels[0] 0,{0,s-2}
45656
45657#@gui Paper Texture : fx_paper, fx_paper_preview(0)
45658#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45659#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45660#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45661#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45662#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45663#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45664#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45665#@gui : sep = separator()
45666#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45667#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45668#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45669#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45670#@gui : sep = separator()
45671#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45672fx_paper :
45673  ac "texturize_paper",$1
45674
45675fx_paper_preview :
45676  gui_split_preview "fx_paper $*",${-3--1}
45677
45678#@gui Plaid : fx_plaid_texture,fx_plaid_texture(1)
45679#@gui : Line = float(50,0,100)
45680#@gui : Number of Angles = int(2,1,8)
45681#@gui : Starting Angle = float(0,0,360)
45682#@gui : Angle Range = float(90,0,360)
45683#@gui : Smoothness = float(1,0,5)
45684#@gui : Sharpen = float(300,0,1000)
45685#@gui : sep = separator()
45686#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/16/05</i>.</small>")
45687fx_plaid_texture :
45688  repeat $! l[$>]
45689    w={w} h={h} s={s}
45690    rows $1%
45691    b $5% sharpen $6
45692    r $w,$h,1,$s,2
45693    +rotate[0] $3,1,2,50%,50%
45694    repeat $2-1 +rotate[0] {$3+$4*($>+1)/($2-1)},1,2,50%,50% +[-2,-1] done rm[0]
45695    / $2
45696  endl done
45697
45698#@gui Polka Dots : fx_polka_dots, fx_polka_dots(1)
45699#@gui : Size = float(80,0,100)
45700#@gui : Density = float(20,0.1,100)
45701#@gui : First Offset = float(50,0,100)
45702#@gui : Second Offset = float(50,0,100)
45703#@gui : Angle = float(0,0,180)
45704#@gui : Aliasing = float(0.5,0.1,1)
45705#@gui : Shading = float(0.1,0.1,1)
45706#@gui : Opacity = float(1,0,1)
45707#@gui : Color = color(255,0,0,255)
45708#@gui : sep = separator()
45709#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45710fx_polka_dots :
45711  to_rgba polka_dots {$1*$2/100},${2--1}
45712
45713#@gui Random Color Ellipses : fx_color_ellipses, fx_color_ellipses(1)
45714#@gui : Density = int(400,0,3000)
45715#@gui : Radius = float(8,0,30)
45716#@gui : Opacity = float(0.1,0.01,0.5)
45717#@gui : sep = separator()
45718#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45719fx_color_ellipses :
45720  color_ellipses $1,$2,$3
45721
45722#@gui Random Pattern : fx_random_pattern, fx_random_pattern_preview(1)
45723#@gui : Size = _int(1024,16,8192)
45724#@gui : Min Detail Level = float(2,0,20)
45725#@gui : Seed = float(4038,0,100000)
45726#@gui : Randomize Seed = button()
45727#@gui : sep = separator()
45728#@gui : Brightness (%) = float(0,-100,100)
45729#@gui : Contrast (%) = float(0,-100,100)
45730#@gui : Gamma (%) = float(0,-100,100)
45731#@gui : Hue (%) = float(0,-100,100)
45732#@gui : Saturation (%) = float(0,-100,100)
45733#@gui : sep = separator()
45734#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/10/08</i>.</small>")
45735_fx_random_pattern :
45736  if $4 srand _seed={_round(u(100000))} else _seed=$3 fi
45737  srand $_seed
45738  random_pattern $1,$1,$2
45739  adjust_colors. ${5-9}
45740  mv. 0
45741
45742fx_random_pattern :
45743  if $_output_mode rm fi
45744  _fx_random_pattern $*
45745
45746fx_random_pattern_preview :
45747  _fx_random_pattern {max($_preview_width,$_preview_height)},${2--1}
45748  k[0] rr2d $_preview_width,$_preview_height,2,2
45749  to "Seed: \#"$_seed,5,5,5%,2
45750  u "{$1}{$2}{"{$4?$_seed:$3}"}{0}{$5}{$6}{$7}{$8}{$9}"
45751
45752#@gui Resynthetize Texture [FFT] : syntexturize, fx_syntexturize_preview(1)
45753#@gui : Width = _int(1024,32,8192)
45754#@gui : Height = _int(1024,32,8192)
45755#@gui : Equalize Light = float(0,0,100)
45756#@gui : sep = separator()
45757#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45758#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45759#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45760#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45761#@gui : sep = separator()
45762#@gui : note = note{"<small><b>Note:</b> This filter tries to re-synthetize a <b>micro</b>-texture
45763#@gui : (given as the input image) onto an output (seamless) image with an arbitrary size.
45764#@gui : It uses a phase randomization technique, as described in:</small>"}
45765#@gui : url = link("Micro-Texture Synthesis by Phase Randomization","http://www.ipol.im/pub/art/2011/ggm_rpn/")
45766#@gui : note = note("<small>This filter is based on the work of <i>Bruno Galerne</i>, <i>Yann Gousseau</i> and
45767#@gui : <i>Jean-Michel Morel</i>.</small>")
45768#@gui : sep = separator()
45769#@gui : url = link("Click here for a detailed description of this filter.",\
45770# "http://gimpchat.com/viewtopic.php?f=28&t=10141")
45771#@gui : sep = separator()
45772#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Jérome Boulanger</i>.
45773#@gui :       Latest Update: <i>2014/09/04</i>.</small>")
45774fx_syntexturize :
45775  repeat $! l[$>]
45776    if $3 +b {20.5-$3/50}% -[0] [1] fc. ${average_colors.} + c 0,255 fi
45777    syntexturize $1,$2
45778  endl done
45779
45780fx_syntexturize_preview :
45781  gui_split_preview "fx_syntexturize 100%,100%,$3",${-3--1}
45782
45783#@gui Resynthetize Texture [Patch-Based] : syntexturize_matchpatch, fx_syntexturize_matchpatch_preview(1)
45784#@gui : Width = _int(512,32,8192)
45785#@gui : Height = _int(512,32,8192)
45786#@gui : Number of Scales = int(0,0,16)
45787#@gui : Patch Size = int(7,1,32)
45788#@gui : Blending Size = int(5,1,24)
45789#@gui : Precision = float(1,0,5)
45790#@gui : Equalize Light = float(0,0,100)
45791#@gui : sep = separator()
45792#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45793#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45794#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45795#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45796#@gui : sep = separator()
45797#@gui : note = note{"<small><b>Note:</b> This filter tries to re-synthetize an input texture image onto a
45798#@gui : bigger output image (with an arbitrary size).
45799#@gui : Beware, this filter is quite slow to compute!</small>"}
45800#@gui : sep = separator()
45801#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/22/10</i>.</small>")
45802_fx_syntexturize_matchpatch_preview :
45803  repeat $! l[$>]
45804    if $7 +b {20.5-$7/50}% -[0] [1] fc. ${average_colors.} + c 0,255 fi
45805    w={w} h={h}
45806    syntexturize_matchpatch 100%,100%,${3--1}
45807    to_rgba r $w,$h,1,4,0,0,0.5,0.5
45808  endl done
45809
45810fx_syntexturize_matchpatch_preview :
45811  gui_split_preview "_fx_syntexturize_matchpatch_preview ${1--2}",${-3--1}
45812
45813#@gui Rorschach : fx_rorschach, fx_rorschach
45814#@gui : Scale = float(3,0,10)
45815#@gui : Mirror = choice(1,"None","X-Axis","Y-Axis","XY-Axes")
45816#@gui : Stencil Type = choice(2,"Black & White","RGB","Color")
45817#@gui : sep = separator()
45818#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/12/03</i>.</small>")
45819fx_rorschach :
45820  repeat $! remove_opacity l[$>]
45821    if $3==0 norm rorschach $1%,$2 * 255
45822    elif $3==1 to_rgb rorschach $1%,$2 * 255
45823    else +norm rorschach. $1%,$2 blend shapeaverage0
45824    fi
45825  endl done
45826
45827#@gui Satin : fx_satin, fx_satin(1)
45828#@gui : Iterations = int(20,4,128)
45829#@gui : Smoothness (%) = float(1,0,5)
45830#@gui : Seed = int(0,0,65535)
45831#@gui : sep = separator()
45832#@gui : Dark Color = color(0,0,0,255)
45833#@gui : Light Color = color(255,255,255,255)
45834#@gui : Stretch Contrast = bool(0)
45835#@gui : sep = separator()
45836#@gui : Brightness (%) = float(0,-100,100)
45837#@gui : Contrast (%) = float(0,-100,100)
45838#@gui : Gamma (%) = float(-50,-100,100)
45839#@gui : Hue (%) = float(0,-100,100)
45840#@gui : Saturation (%) = float(0,-100,100)
45841#@gui : sep = separator()
45842#@gui : note = note{"This filter has been inspired by
45843#@gui : <a href="https://fence-post.deviantart.com/art/Satin-Texture-in-GIMP-46937633">this tutorial</a>
45844#@gui : from DeviantArt user <i>fence-post</i>."}
45845#@gui : sep = separator()
45846#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/11/27</i>.</small>")
45847fx_satin :
45848  ($4,$8^$5,$9^$6,$10^$7,$11) srgb2rgb. r. 256,1,1,4,3 rgb2srgb.
45849  repeat $!-1 l[$>]
45850    srand $3 channels 0 f 0
45851    repeat $1
45852      100%,100%,1,1,"begin(
45853           A = u([0,0],[w,h]-1);
45854           B = u([0,0],[w,h]-1);
45855           N = [0,-1,1,0]*(B - A);
45856           D = A + N;
45857           C = B + N;
45858           abc = solve([A,1,B,1,C,1,D,1],[0,255,255,0]);
45859         );
45860         dot(abc,[x,y,1])"
45861      c. 0,255 -- abs
45862    done
45863    b $2% gradient_norm negate n 0,255
45864    if $12 normalize_local , fi
45865    pass. 1 map.. . rm.
45866    sh. 0,2 adjust_colors. ${13-17} rm.
45867  endl done
45868  rm.
45869
45870#@gui Seamless Turbulence : fx_seamless_turbulence, fx_seamless_turbulence(0)
45871#@gui : Amplitude = float(15,0,30)
45872#@gui : Smoothness = float(20,0,40)
45873#@gui : Orientation = float(0,0,180)
45874#@gui : Deviation = float(1,0,1)
45875#@gui : Contrast = float(3,0,4)
45876#@gui : Color Rendering = bool(0)
45877#@gui : sep = separator()
45878#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/02/04</i>.</small>")
45879fx_seamless_turbulence :
45880  repeat $! l[$>]
45881    wh={w},{h} rm
45882    $wh,1,{if($6,3,1)} rand. 0,255
45883    $wh rand. {$3*pi/180-$4*10*pi},{$3*pi/180+$4*10*pi} +sin. cos.. a[-2,-1] c
45884    r[-2,-1] 130%,130%,1,100%,0,2,0.5,0.5 b. $2 orientation.
45885    vector2tensor.
45886    smooth.. .,$1,0.5,20 rm.
45887    r. $wh,1,100%,0,0,0.5,0.5
45888    if $5!=1 ia={ia} - $ia * $5 + $ia fi
45889  endl done
45890  c 0,255 n 0,255
45891
45892#@gui Shock Waves : fx_shockwaves, fx_shockwaves_preview
45893#@gui : Amplitude = float(10,0,100)
45894#@gui : Low Frequency = float(10,0,100)
45895#@gui : Frequency Range = float(20,0,100)
45896#@gui : sep = separator()
45897#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45898#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45899#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45900#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45901#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45902#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45903#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45904#@gui : sep = separator()
45905#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45906#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45907#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45908#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45909#@gui : sep = separator()
45910#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/01/12</i>.</small>")
45911_fx_shockwaves :
45912  dct
45913  100%,100%,1,1,1 circle. 0,0,{$2+$3}%,1,{$1+1} circle. 0,0,$2%,1,1
45914  * idct c 0,255
45915
45916fx_shockwaves :
45917  ac "_fx_shockwaves ${1-3}",$4
45918
45919fx_shockwaves_preview :
45920  gui_split_preview "fx_shockwaves $*",${-3--1}
45921
45922#@gui Sponge : fx_sponge, fx_sponge_preview(0)
45923#@gui : Size = int(13,3,21)
45924#@gui : sep = separator()
45925#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45926#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45927#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45928#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45929#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45930#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45931#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45932#@gui : sep = separator()
45933#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45934#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45935#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45936#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45937#@gui : sep = separator()
45938#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45939fx_sponge :
45940  ac "sponge $1",$2
45941
45942fx_sponge_preview :
45943  gui_split_preview "fx_sponge $*",${-3--1}
45944
45945#@gui Stained Glass : fx_stained_glass, fx_stained_glass_preview(0)
45946#@gui : Edges = float(20,0,100)
45947#@gui : Shading = float(0.1,0,0.5)
45948#@gui : Thin Separators = bool(1)
45949#@gui : sep = separator()
45950#@gui : Equalize = bool(1)
45951#@gui : Colors = float(1,0,3)
45952#@gui : Brightness (%) = float(0,-100,100)
45953#@gui : Contrast (%) = float(0,-100,100)
45954#@gui : Gamma (%) = float(0,-100,100)
45955#@gui : sep = separator()
45956#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45957#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45958#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45959#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45960#@gui : sep = separator()
45961#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/03</i>.</small>")
45962fx_stained_glass :
45963  repeat $! l[$>] split_opacity l[0] to_rgb
45964    stained_glass $1,$2,$3
45965    n 0,255
45966    if $4 equalize. fi
45967    rgb2lab. sh. 1,2 *. $5 rm. lab2rgb.
45968    adjust_colors. ${6-8}
45969  endl a c endl done
45970
45971fx_stained_glass_preview :
45972  gui_split_preview "fx_stained_glass $*",${-3--1}
45973
45974#@gui Stars : fx_stars, fx_stars(0)
45975#@gui : Density = float(10,0,200)
45976#@gui : Depth = float(0,0,5)
45977#@gui : Size = int(32,8,128)
45978#@gui : Branches = int(5,3,16)
45979#@gui : Thickness = float(0.38,0.1,1)
45980#@gui : Smoothness = float(0,0,10)
45981#@gui : Color = color(255,255,100,200)
45982#@gui : sep = separator()
45983#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/01/10</i>.</small>")
45984fx_stars :
45985  repeat $! l[$>] split_opacity rv
45986    stars $1%,$2,$3,$4,$5,$6%,${7-9},{$10/255}
45987  rv a c endl done
45988
45989#@gui Stencil : fx_stencil, fx_stencil_preview(0)
45990#@gui : Radius = float(3,0,10)
45991#@gui : Smoothness = float(0,0,30)
45992#@gui : Iterations = int(8,1,100)
45993#@gui : Aliasing = float(0,0,5)
45994#@gui : Stencil Type = choice(2,"Black & White","RGB","Color")
45995#@gui : Transparency = bool(0)
45996#@gui : sep = separator()
45997#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45998#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45999#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46000#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46001#@gui : sep = separator()
46002#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46003fx_stencil :
46004  if $5==0 norm stencil $1,$2,$3
46005  elif $5==1 stencil $1,$2,$3
46006  else repeat $!
46007    +norm. stencil. $1,$2,$3 >=. 50% blend[-2,-1] shapeaverage0
46008  mv. 0 done fi
46009  if $6 to_rgba replace_color 0,0,0,0,0,255,0,0,0,0 fi
46010  if $4 smooth {30*$4},0,1,1 fi
46011
46012fx_stencil_preview :
46013  gui_split_preview "fx_stencil $*",${-3--1}
46014
46015#@gui Tetris : fx_tetris, fx_tetris(0)
46016#@gui : Scale = int(10,1,20)
46017#@gui : sep = separator()
46018#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46019fx_tetris :
46020  tetris $1
46021
46022#@gui Truchet : fx_truchet, fx_truchet(0)
46023#@gui : Scale = int(32,1,256)
46024#@gui : Radius = int(5,1,64)
46025#@gui : Smoothness = float(1,0,10)
46026#@gui : Type = choice(1,"Straight","Curved")
46027#@gui : Color = choice("White on Black","Black on White","White on Transparent","Black on Transparent",
46028#@gui : "Transparent on White","Transparent on Black","Random")
46029#@gui : sep = separator()
46030#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/26/10</i>.</small>")
46031fx_truchet :
46032  repeat $! l[$>]
46033    100%,100% truchet $1,$2,$4 rm..
46034    if $5==1 * -1
46035    elif $5==2 i[0] 100%,100%,1,1,1
46036    elif $5==3 i[0] 100%,100%
46037    elif $5==4 * -1 i[0] 100%,100%
46038    elif $5==5 * -1 i[0] 100%,100%,1,1,-1
46039    elif $5==6 label 0,1 {iM+1},1,1,3 rand. 0,255 map.. . rm.
46040    fi
46041    a c b $3 n 0,255
46042  endl done
46043
46044#@gui Voronoi : fx_voronoi, fx_voronoi_preview(0)
46045#@gui : Threshold = float(160,0,255)
46046#@gui : Threshold on = choice(1,"Pixel values","Gradient values")
46047#@gui : Smoothness = float(0.5,0,10)
46048#@gui : Subsampling (%) = float(50,0,100)
46049#@gui : sep = separator()
46050#@gui : Flat color = choice(3,"Black","White","Transparent","Image")
46051#@gui : Outline thickness = int(1,0,8)
46052#@gui : Outline color = color(0,0,0,100)
46053#@gui : Centers radius = int(2,0,10)
46054#@gui : Centers color = color(255,255,255,40)
46055#@gui : sep = separator()
46056#@gui : Anti-aliasing = choice{1,"x1 (none)","x1.5","x2","x2.5"}
46057#@gui : sep = separator()
46058#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/04/30</i>.</small>")
46059fx_voronoi :
46060  repeat $! l[$>] wh={[w,h]}
46061    f={arg(1+$16,1,1.5,2,2.5)*100} r $f%,$f%,1,100%,3
46062
46063    if $2 +gradient_norm t={(255-$1)/4} else +s c med[^0] t={255-$1} fi
46064    if $3 mM={[im,iM]} b. $3 n. $mM fi
46065    >. $t
46066    f. "u<($4%)^4?i:0"
46067    label_fg. 0,1 voronoi.
46068
46069    # Flat.
46070    if $5<3
46071      1,1,1,4,"$5==0?[0,0,0,255]:$5==1?[255,255,255,255]:$5==2?[128,128,128,0]"
46072      r. [0],[0],1,4 rv[0,-1] rm.
46073    else
46074      blend[0] .,shapeaverage
46075    fi
46076
46077    # Outline.
46078    if $6" && "$10
46079      +f. "const boundary=1; i!=j(1) || i!=j(0,1)"
46080      dilate. $6
46081      1,1,1,4,"[${7-9},255]" r. [0],[0],1,4
46082      j[0] .,0,0,0,0,{$10/255},.. rm[-2,-1]
46083    fi
46084
46085    # Centers.
46086    if $11" && "$15
46087      1,{iM+1},1,3 eval.. "I[#-1,i]+=[x,y,1]" s. c,-2 /[-2,-1] rm..
46088      eval. "const r = ($11-1)*arg(1+$16,1,1.5,2,2.5); ellipse(#0,i0,i1,r,r,0,$15/255,[${12-14},255])"
46089    fi
46090    rm.
46091    r $wh,1,100%,2
46092
46093  endl done
46094
46095fx_voronoi_preview :
46096  fx_voronoi ${1-15},{min(1,$16)}
46097
46098#@gui Weave : weave, weave(1)
46099#@gui : Density = int(6,1,32)
46100#@gui : Thickness = float(65,0,100)
46101#@gui : Shadow = float(0,0,100)
46102#@gui : Shading = float(0.5,0,3)
46103#@gui : Fibers Amplitude = float(0,0,255)
46104#@gui : Fibers Smoothness = float(0,0,10)
46105#@gui : Angle = choice("0 deg.","22.5 deg.","45 deg.","67.5 deg.")
46106#@gui : X-Curvature = float(0,-1,1)
46107#@gui : Y-Curvature = float(0,-1,1)
46108#@gui : sep = separator()
46109#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/18/01</i>.</small>")
46110
46111#@gui Whirls : fx_whirls, fx_whirls_preview(0)
46112#@gui : Density = int(7,3,20)
46113#@gui : Smoothness = float(2,0,10)
46114#@gui : Darkness = float(0.2,0,1)
46115#@gui : Lightness = float(1.8,1,3)
46116#@gui : sep = separator()
46117#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46118#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46119#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46120#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46121#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46122#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46123#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46124#@gui : sep = separator()
46125#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46126#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46127#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46128#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46129#@gui : sep = separator()
46130#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46131fx_whirls :
46132  ac "whirls $1,$2,$3,$4",$5
46133
46134fx_whirls_preview :
46135  gui_split_preview "fx_whirls ${1-5}",${-3--1}
46136
46137
46138#@gui ____<b>Repair</b>
46139#-----------------------
46140
46141#@gui Bayer Reconstruction : bayer2rgb, gui_no_preview
46142#@gui : G/M Smoothness = _float(6,0,20)
46143#@gui : R/B Smoothness (Principal) = _float(6,0,20)
46144#@gui : R/B Smoothness (Secondary) = _float(4,0,20)
46145#@gui : sep = separator()
46146#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46147
46148#@gui Deinterlace : deinterlace, fx_deinterlace_preview(0)
46149#@gui : Algorithm = choice("Standard","Motion-Compensated")
46150#@gui : sep = separator()
46151#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46152#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46153#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46154#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46155#@gui : sep = separator()
46156#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46157fx_deinterlace :
46158  deinterlace 0 skip ${^0}
46159
46160fx_deinterlace_preview :
46161  gui_split_preview "fx_deinterlace $*",${-3--1}
46162
46163#@gui Inpaint [Holes] : fx_inpaint_holes, fx_inpaint_holes(0)
46164#@gui : Maximal Area = float(4,1,512)
46165#@gui : Tolerance = float(20,0,255)
46166#@gui : Connectivity = choice(1,"Low","High")
46167#@gui : sep = separator()
46168#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/27/05</i>.</small>")
46169fx_inpaint_holes :
46170  inpaint_holes {$1^1.5},$2,$3
46171
46172#@gui Inpaint [Morphological] : fx_inpaint_morpho, fx_inpaint_morpho_preview(1)
46173#@gui : Mask Color = _color(255,0,0,255)
46174#@gui : Mask Dilation = _int(0,0,32)
46175#@gui : sep = separator()
46176#@gui : note = note{"<small><b>Note:</b> It is strongly suggested to apply this filter only on a selection
46177#@gui : around the region to inpaint, to save computation time!</small>"}
46178#@gui : sep = separator()
46179#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/25/11</i>.</small>")
46180fx_inpaint_morpho :
46181  repeat $! l[$>]
46182    R=$1 G=$2 B=$3 A=$4
46183    if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color.
46184    +round select_color. 0,{round([$R,$G,$B,$A])}
46185    if $5 dilate. {1+2*$5} fi
46186    inpaint_morpho.. [1]
46187    rm.
46188  endl done
46189
46190fx_inpaint_morpho_preview :
46191  fx_inpaint_morpho ${1-4},{1+$5}
46192
46193#@gui Inpaint [Multi-Scale] : fx_inpaint_matchpatch, fx_inpaint_matchpatch_preview(1)
46194#@gui : Number of Scales = int(0,0,16)
46195#@gui : note = note{"<small>(Set <i>Number of scales</i> to <i>0</i> for automatic scale detection)</small>"}
46196#@gui : Patch Size = int(9,1,64)
46197#@gui : Number of Iterations per Scale = int(10,1,100)
46198#@gui : Blend Size = int(5,0,32)
46199#@gui : Allow Outer Blending = bool(1)
46200#@gui : Mask Color = color(255,0,0,255)
46201#@gui : Mask Dilation = int(0,0,32)
46202#@gui : sep = separator()
46203#@gui : Preview Progression While Running = _bool(0)
46204#@gui : sep = separator()
46205#@gui : note = note{"<small><b>Note:</b> Preview and final result may strongly differ.</small>"}
46206#@gui : sep = separator()
46207#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/25/11</i>.</small>")
46208fx_inpaint_matchpatch :
46209  repeat $! l[$>] nm={n}
46210    R=$6 G=$7 B=$8 A=$9
46211    if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color.
46212    +round select_color. 0,{round([$R,$G,$B,$A])}
46213    if $10 dilate. {1+2*$10} fi
46214    if $11
46215      visu_size=${fitscreen[]" "{0,[w,h,1]},25%,50%}
46216      w1.. $visu_size,0,"[Preview] G'MIC: Inpaint [multi-scale]"
46217    fi
46218    srand 0 inpaint_matchpatch.. [1],${1-5}
46219    rm. nm $nm
46220  endl done
46221
46222fx_inpaint_matchpatch_preview :
46223  fx_inpaint_matchpatch ${1-9},{1+$10},0
46224
46225#@gui Inpaint [Patch-Based] : fx_inpaint_patch, fx_inpaint_patch_preview
46226#@gui : Patch Size = _int(7,1,64)
46227#@gui : Lookup Size = _float(16,1,32)
46228#@gui : Lookup Factor = _float(0.1,0,1)
46229#@gui : Blend Size = _float(1.2,0,5)
46230#@gui : Blend Threshold = _float(0,0,1)
46231#@gui : Blend Decay = _float(0.05,0,0.5)
46232#@gui : Blend Scales = _int(10,1,20)
46233#@gui : Allow Outer Blending = _bool(1)
46234#@gui : Mask Color = _color(255,0,0,255)
46235#@gui : Mask Dilation = _int(0,0,32)
46236#@gui : Process by Blocs of Size = _choice("100%","75%","50%","25%","10%","5%","2%","1%")
46237#@gui : sep = separator()
46238#@gui : note = note("<small>A quick tutorial on how to use this filter can be found here:</small>")
46239#@gui : url = link("G'MIC Inpainting tutorial on Patrick David's blog.",
46240#@gui : "https://patdavid.net/2014/02/getting-around-in-gimp-gmic-inpainting.html")
46241#@gui : sep = separator()
46242#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Maxime Daisy</i>.
46243#@gui :       Latest Update: <i>2015/25/11</i>.</small>")
46244_fx_inpaint_patch :
46245  repeat $! l[$>]
46246    R=$9 G=$10 B=$11 A=$12
46247    if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color.
46248    +round select_color. 0,{round([$R,$G,$B,$A])}
46249    if $13 dilate. {1+2*$13} fi
46250    inpaint.. [1],$1,{$1*$2},$3,1,{$4*$1},${5-8}
46251    rm.
46252  endl done
46253
46254fx_inpaint_patch :
46255  repeat $! l[$>]
46256    if $14
46257      bs={max(16,min(w,h)*arg(1+$14,100,75,50,25,10,5,2,1)%)}
46258      at "_fx_inpaint_patch $*",$bs,$bs,1,25%,25%,0,2
46259    else _fx_inpaint_patch $*
46260    fi
46261  endl done
46262
46263fx_inpaint_patch_preview :
46264  fx_inpaint_patch ${1-12},{1+$13},100
46265
46266#@gui Inpaint [Transport-Diffusion] : fx_inpaint_pde, fx_inpaint_pde_preview(1)
46267#@gui : Smoothness (%) = float(75,0,100)
46268#@gui : Regularization = choice(1,"Isotropic","Delaunay-Oriented","Edge-Oriented")
46269#@gui : Regularization Iterations = int(20,0,100)
46270#@gui : Mask Color = _color(255,0,0,255)
46271#@gui : Mask Dilation = _int(0,0,32)
46272#@gui : sep = separator()
46273#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/10/04</i>.</small>")
46274fx_inpaint_pde :
46275  repeat $! l[$>]
46276    R=$4 G=$5 B=$6 A=$7
46277    if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color.
46278    +select_color 0,$R,$G,$B,$A
46279    if $8 dilate. {1+2*$8} fi
46280    inpaint_pde.. [1],$1%,$2,$3
46281    rm.
46282  endl done c 0,255
46283
46284fx_inpaint_pde_preview :
46285  fx_inpaint_pde ${1-7},{1+$8}
46286
46287#@gui Red-Eye Attenuation : red_eye, red_eye
46288#@gui : Threshold = float(75,0,100)
46289#@gui : Smoothness = float(3.5,0,20)
46290#@gui : Factor = float(0.1,0,1)
46291#@gui : sep = separator()
46292#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46293
46294#@gui Remove Hot Pixels : fx_remove_hotpixels, fx_remove_hotpixels_preview(0)
46295#@gui : Mask Size = int(3,3,20)
46296#@gui : Threshold = float(10,0,200)
46297#@gui : sep = separator()
46298#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46299#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46300#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46301#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46302#@gui : sep = separator()
46303#@gui : note = note("<small>Author: <i>Jérome Boulanger</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46304fx_remove_hotpixels :
46305  remove_hotpixels $1,$2
46306
46307fx_remove_hotpixels_preview :
46308  gui_split_preview "fx_remove_hotpixels $*",${-3--1}
46309
46310#@gui Solidify : fx_solidify_td, fx_solidify_td_preview(1)
46311#@gui : Smoothness (%) = float(75,0,100)
46312#@gui : Regularization = choice(1,"Isotropic","Delaunay-Oriented","Edge-Oriented")
46313#@gui : Regularization Iterations = int(20,0,100)
46314#@gui : Dilation / Erosion = int(0,-20,20)
46315#@gui : Colorspace = choice(1,"sRGB","Linear RGB")
46316#@gui : sep = separator()
46317#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46318#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46319#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46320#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46321#@gui : sep = separator()
46322#@gui : note = note{"<small><b>Note:</b>
46323#@gui : This filter reconstructs transparent regions of an image using a transport-diffusion algorithm.
46324#@gui : Useful only for images having an alpha-channel.
46325#@gui : </small>"}
46326#@gui : sep = separator()
46327#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/07/04</i>.</small>")
46328fx_solidify_td :
46329  repeat $! l[$>]
46330    to_rgba sh 0,{s-2} if $5 srgb2rgb. fi rm.
46331    if $4
46332      . sh. 100% if $4>0 erode. {1+2*$4} else dilate. {1-2*$4} fi rm.
46333      solidify. $1%,$2,$3
46334      rv blend alpha
46335    else
46336      solidify $1%,$2,$3
46337    fi
46338    if $5 rgb2srgb. fi
46339  endl done
46340
46341fx_solidify_td_preview :
46342  gui_split_preview "fx_solidify_td $*",${-3--1}
46343
46344#@gui Smooth [Anisotropic] : fx_smooth_anisotropic, fx_smooth_anisotropic_preview(0)
46345#@gui : Amplitude = float(60,0,1000)
46346#@gui : Sharpness = float(0.7,0,2)
46347#@gui : Anisotropy = float(0.3,0,1)
46348#@gui : Gradient Smoothness = float(0.6,0,10)
46349#@gui : Tensor Smoothness = float(1.1,0,10)
46350#@gui : Spatial Precision = float(0.8,0.1,2)
46351#@gui : Angular Precision = float(30,1,180)
46352#@gui : Value Precision = float(2,0.1,5)
46353#@gui : Interpolation = choice(0,"Nearest Neighbor","Linear","Runge-Kutta")
46354#@gui : Fast Approximation = bool(1)
46355#@gui : Iterations = int(1,1,10)
46356#@gui : sep = separator()
46357#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46358#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46359#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46360#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46361#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46362#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46363#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46364#@gui : sep = separator()
46365#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46366#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46367#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46368#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46369#@gui : sep = separator()
46370#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/08/27</i>.</small>")
46371fx_smooth_anisotropic :
46372  repeat $! l[$>]
46373    ac "repeat $11 smooth $1,$2,$3,$4,$5,$6,$7,$8,$9,$10 done",$12
46374  endl done c 0,255
46375
46376fx_smooth_anisotropic_preview :
46377  gui_split_preview "fx_smooth_anisotropic $*",${-3--1}
46378
46379#@gui Smooth [Antialias] : fx_smooth_antialias, fx_smooth_antialias_preview(0)
46380#@gui : Amplitude = float(5,0,100)
46381#@gui : Edge Threshold (%) = float(10,0,100)
46382#@gui : Smoothness = float(0.8,0,5)
46383#@gui : sep = separator()
46384#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46385#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46386#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46387#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46388#@gui : sep = separator()
46389#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/11/13</i>.</small>")
46390fx_smooth_antialias :
46391  repeat $! l[$>]
46392    +diffusiontensors 0,1,1,$3,$3
46393    +gradient_norm.. >=. $2% *[-2,-1]
46394    smooth.. .,{$1^1/3},0.5,120,2,1 rm.
46395  endl done
46396
46397fx_smooth_antialias_preview :
46398  gui_split_preview "fx_smooth_antialias $*",${-3--1}
46399
46400#@gui Smooth [Bilateral] : fx_smooth_bilateral, fx_smooth_bilateral_preview(0)
46401#@gui : Spatial Variance = float(10,0,100)
46402#@gui : Value Variance = float(7,0,100)
46403#@gui : Iterations = int(2,1,10)
46404#@gui : sep = separator()
46405#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46406#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46407#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46408#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46409#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46410#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46411#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46412#@gui : sep = separator()
46413#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46414#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46415#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46416#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46417#@gui : sep = separator()
46418#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
46419fx_smooth_bilateral : skip ${5=0},${6=0}
46420  ac "repeat $3 bilateral $1,$2 done",$4
46421
46422fx_smooth_bilateral_preview :
46423  gui_split_preview "fx_smooth_bilateral $*",${-3--1}
46424
46425#@gui Smooth [Guided] : fx_smooth_guided, fx_smooth_guided_preview(0)
46426#@gui : Guide As = choice("Self","Top Layer","Bottom Layer")
46427#@gui : Radius = int(5,1,100)
46428#@gui : Smoothness = float(30,0,512)
46429#@gui : Iterations = int(1,1,10)
46430#@gui : sep = separator()
46431#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46432#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46433#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46434#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46435#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46436#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46437#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46438#@gui : sep = separator()
46439#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46440#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46441#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46442#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46443#@gui : sep = separator()
46444#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/10/02</i>.</small>")
46445fx_smooth_guided : skip ${6=0},${7=0}
46446  if $1" && "!narg($_guide)
46447    if $!<2 gui_warning_preview "Missing guide layer" return fi
46448    store[{$1==1?0:-1}] _guide
46449  fi
46450  if $1==0 # Self-guide
46451    ac "repeat $4 guided $2,$3 done",$5
46452  elif $1==1 # Guide as top layer
46453    ac "$_guide r. ..,..,1,100%,0,0,0.5,0.5 repeat $4 guided[0] [1],$2,$3 done rm.",$5
46454    if !narg($_is_preview) $_guide mv. 0 fi
46455  else # Guide as bottom layer
46456    ac "$_guide r. ..,..,1,100%,0,0,0.5,0.5 repeat $4 guided[0] [1],$2,$3 done rm.",$5
46457    if !narg($_is_preview) $_guide fi
46458  fi
46459
46460fx_smooth_guided_preview :
46461  if $1" && "!narg($_guide)
46462    if $!<2 gui_warning_preview "Missing guide layer" return fi
46463    store[{$1==1?0:-1}] _guide
46464  fi
46465  _is_preview=1
46466  gui_split_preview "fx_smooth_guided $*",${-3--1}
46467
46468#@gui Smooth [Diffusion] : fx_smooth_diffusion, fx_smooth_diffusion_preview(0)
46469#@gui : Sharpness = float(0.7,0,2)
46470#@gui : Anisotropy = float(0.3,0,1)
46471#@gui : Gradient Smoothness = float(0.6,0,10)
46472#@gui : Tensor Smoothness = float(1.1,0,10)
46473#@gui : Time Step = float(15,5,50)
46474#@gui : Iterations = int(8,1,100)
46475#@gui : sep = separator()
46476#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46477#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46478#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46479#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46480#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46481#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46482#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46483#@gui : sep = separator()
46484#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46485#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46486#@gui : sep = separator()
46487#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46488#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46489#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46490#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46491#@gui : sep = separator()
46492#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
46493fx_smooth_diffusion :
46494  ac "gui_parallel_overlap \"smooth $6,$1,$2,$3,$4,$5,0 c 0,255\",$8,$9",$7
46495
46496fx_smooth_diffusion_preview :
46497  gui_split_preview "fx_smooth_diffusion $*",${-3--1}
46498
46499#@gui Smooth [Mean-Curvature] : fx_smooth_meancurvature, fx_smooth_meancurvature_preview(0)
46500#@gui : Time Step = float(30,5,50)
46501#@gui : Iterations = int(4,1,30)
46502#@gui : Keep Iterations as Different Layers = bool(false)
46503#@gui : sep = separator()
46504#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46505#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46506#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46507#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46508#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46509#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46510#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46511#@gui : sep = separator()
46512#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46513#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46514#@gui : sep = separator()
46515#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46516#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46517#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46518#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46519#@gui : sep = separator()
46520#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
46521fx_smooth_meancurvature :
46522  ac "gui_parallel_overlap \"meancurvature_flow $2,$1,$3 c 0,255\",$5,$6",$4
46523
46524fx_smooth_meancurvature_preview :
46525  gui_split_preview "fx_smooth_meancurvature $*",${-3--1}
46526
46527#@gui Smooth [Median] : fx_smooth_median, fx_smooth_median_preview(0)
46528#@gui : Radius = int(3,1,20)
46529#@gui : Threshold = float(255,0,255)
46530#@gui : sep = separator()
46531#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46532#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46533#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46534#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46535#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46536#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46537#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46538#@gui : sep = separator()
46539#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46540#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46541#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46542#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46543#@gui : sep = separator()
46544#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46545fx_smooth_median :
46546  ac "median $1,$2",$3
46547
46548fx_smooth_median_preview :
46549  gui_split_preview "fx_smooth_median $*",${-3--1}
46550
46551#@gui Smooth [NL-Means] : fx_smooth_nlmeans, fx_smooth_nlmeans_preview(0)
46552#@gui : Patch Size = float(4,0.5,10)
46553#@gui : Spatial Bandwidth = int(4,3,13)
46554#@gui : Tonal Bandwidth = float(10,1,50)
46555#@gui : Patch Measure = choice(3,"Linf-Norm","L1-Norm","L2-Norm","Luminance","Lightness","RGB")
46556#@gui : sep = separator()
46557#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46558#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46559#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46560#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46561#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46562#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46563#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46564#@gui : sep = separator()
46565#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46566#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46567#@gui : sep = separator()
46568#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46569#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46570#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46571#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46572#@gui : sep = separator()
46573#@gui : note = note("<small>Author: <i>Jérôme Boulanger</i>.      Latest Update: <i>2015/01/07</i>.</small>")
46574fx_smooth_nlmeans:
46575  repeat $! l[$>]
46576    if s==1 nlmeans $1,$2,$3,-_fx_smooth_nlmeans$4 # Handle separately gray scale images.
46577    else ac "gui_parallel_overlap \"nlmeans $1,$2,$3,-_fx_smooth_nlmeans$4\",$6,$7",$5
46578    fi
46579  endl done
46580
46581_fx_smooth_nlmeans0 : s c abs max
46582_fx_smooth_nlmeans1 : s c abs +
46583_fx_smooth_nlmeans2 : norm
46584_fx_smooth_nlmeans3 : if s>=3 channels 0,2 luminance else norm fi
46585_fx_smooth_nlmeans4 : if s>=3 channels 0,2 srgb2rgb rgb2lab channels 0 else norm fi
46586_fx_smooth_nlmeans5 :
46587
46588fx_smooth_nlmeans_preview:
46589  gui_split_preview "fx_smooth_nlmeans $*",${-3--1}
46590
46591#@gui Smooth [Patch-Based] : fx_smooth_patch, fx_smooth_patch_preview(0)
46592#@gui : Spatial Variance = float(10,0.1,200)
46593#@gui : Patch Variance = float(10,0.1,200)
46594#@gui : Patch Size = int(3,2,21)
46595#@gui : Lookup Size = int(5,2,21)
46596#@gui : Patch Smoothness = float(0,0,4)
46597#@gui : Fast Approximation = bool(1)
46598#@gui : Iterations = int(1,1,10)
46599#@gui : sep = separator()
46600#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46601#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46602#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46603#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46604#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46605#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46606#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46607#@gui : sep = separator()
46608#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46609#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46610#@gui : sep = separator()
46611#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46612#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46613#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46614#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46615#@gui : sep = separator()
46616#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
46617fx_smooth_patch :
46618  ac "gui_parallel_overlap \"repeat $7 denoise $1,$2,$3,$4,$5,$6 done c 0,255\",$9,$10",$8
46619
46620fx_smooth_patch_preview :
46621  gui_split_preview "fx_smooth_patch $*",${-3--1}
46622
46623#@gui Smooth [Patch-PCA] : fx_smooth_patchpca, fx_smooth_patchpca_preview(0)
46624#@gui : Strength = float(4,0,16)
46625#@gui : Patch Size = int(7,2,21)
46626#@gui : Lookup Size = int(11,2,21)
46627#@gui : Spatial Sampling = int(7,1,16)
46628#@gui : sep = separator()
46629#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46630#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46631#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46632#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46633#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46634#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46635#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46636#@gui : sep = separator()
46637#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46638#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46639#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46640#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46641#@gui : sep = separator()
46642#@gui : note = note{"<small><b>Note:</b> Beware, this filter uses a very computationally intensive algorithm to
46643#@gui : denoise images. So, do not complain too much if you have less than 8 cores available for the computation :)
46644#@gui : </small>"}
46645#@gui : sep = separator()
46646#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Jérome Boulanger</i>.
46647#@gui :       Latest Update: <i>2016/24/03</i>.</small>")
46648fx_smooth_patchpca :
46649  ac "denoise_patchpca ${1-4} c 0,255",$5
46650
46651fx_smooth_patchpca_preview :
46652  gui_split_preview "fx_smooth_patchpca $*",${-3--1}
46653
46654#@gui Smooth [Perona-Malik] : fx_smooth_peronamalik, fx_smooth_peronamalik_preview(0)
46655#@gui : K-Factor = float(20,0,255)
46656#@gui : Time Step = float(5,5,50)
46657#@gui : Iterations = int(5,1,30)
46658#@gui : Keep Iterations as Different Layers = bool(false)
46659#@gui : sep = separator()
46660#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46661#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46662#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46663#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46664#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46665#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46666#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46667#@gui : sep = separator()
46668#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46669#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46670#@gui : sep = separator()
46671#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46672#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46673#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46674#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46675#@gui : sep = separator()
46676#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/26/11</i>.</small>")
46677fx_smooth_peronamalik :
46678  ac "gui_parallel_overlap \"peronamalik_flow $1,$3,$2,$4 c 0,255\",$6,$7",$5
46679
46680fx_smooth_peronamalik_preview :
46681  gui_split_preview "fx_smooth_peronamalik $*",${-3--1}
46682
46683#@gui Smooth [Selective Gaussian] : fx_smooth_selective, fx_smooth_selective_preview(0)
46684#@gui : Amplitude = float(5,0,20)
46685#@gui : Edges = float(0.5,0,2)
46686#@gui : Scales = int(5,1,10)
46687#@gui : Iterations = int(1,1,10)
46688#@gui : sep = separator()
46689#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46690#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46691#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46692#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46693#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46694#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46695#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46696#@gui : sep = separator()
46697#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46698#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46699#@gui : sep = separator()
46700#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46701#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46702#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46703#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46704#@gui : sep = separator()
46705#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
46706fx_smooth_selective :
46707  ac "gui_parallel_overlap \"repeat $4 blur_selective $1,$2,$3 done c 0,255\",$6,$7",$5
46708
46709fx_smooth_selective_preview :
46710  gui_split_preview "fx_smooth_selective $*",${-3--1}
46711
46712#@gui Smooth [Skin] : fx_smooth_skin, fx_smooth_skin_preview(1)
46713#@gui : note = note("<small><b>Step 1:</b> Skin detection</small>")
46714#@gui : Skin Estimation = choice(2,"None","Manual","Automatic")
46715#@gui : Tolerance = float(0.5,0,1)
46716#@gui : Smoothness = float(1,0,5)
46717#@gui : Threshold = float(1,0,10)
46718#@gui : Pre-Normalize Image = bool(1)
46719#@gui : X-Coordinate [Manual] = float(50,0,100)
46720#@gui : Y-Coordinate [Manual] = float(50,0,100)
46721#@gui : Radius [Manual] = float(5,0,25)
46722#@gui : sep = separator()
46723#@gui : note = note("<small><b>Step 2:</b> Medium scale smoothing</small>")
46724#@gui : Base Scale = float(2,0,10)
46725#@gui : Fine Scale = float(0.2,0,0.8)
46726#@gui : Smoothness = float(3,0,10)
46727#@gui : Smoothness Type = choice(1,"Gaussian","Bilateral")
46728#@gui : sep = separator()
46729#@gui : note = note("<small><b>Step 3:</b> Details enhancement</small>")
46730#@gui : Gain = float(0.05,0,0.5)
46731#@gui : sep = separator()
46732#@gui : Preview Data = choice{5,"Skin Mask","Base Scale","Medium Scale (Original)","Medium Scale (Smoothed)",
46733#@gui : "Fine Scale","Result Image"}
46734#@gui : sep = separator()
46735#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46736#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46737#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46738#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46739#@gui : sep = separator()
46740#@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=H8pQfq-ybCc")
46741#@gui : sep = separator()
46742#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/20/12</i>.</small>")
46743fx_smooth_skin :
46744  repeat $! l[$>] split_opacity l[0] to_rgb
46745
46746    # Skin detection step.
46747    if $5 +balance_gamma 128,128,128 else [0] fi
46748    if $1==0 channels. 0 f. 1 elif $1==2 detect_skin. $2 else detect_skin. $2,$6%,$7%,$8% fi
46749    M={iM} b. $3% *. {$M/iM} *. $4 c. 0,1
46750
46751    # Details smoothing step.
46752    split_details[0] 4,$9%,$10%
46753    +_fx_smooth_skin[2] $12,$11
46754    j[2] .,0,0,0,0,1,.. rm[-2,-1]
46755    *. {10^$13} + c 0,255
46756
46757  endl a c endl done
46758
46759_fx_smooth_skin :
46760  if $1==0 b {$2/8}%
46761  else
46762    if $2>0
46763      m={im} M={iM} n 0,255
46764      repeat int($2/5) bilateral 3%,{5*3} done
46765      bilateral 3%,{($2%5)*3}
46766      * {($M-$m)/255} + $m
46767    fi
46768  fi
46769
46770fx_smooth_skin_preview :
46771  if $-2==0
46772    gui_split_preview "if $5 balance_gamma 128,128,128 fi if $1==0 f 1 elif $1==2 detect_skin $2 "\
46773     "else detect_skin $2,$6%,$7%,$8% fi M={iM} b $3% * {255*$M/iM} * $4 c 0,255",${-3--1}
46774  elif $-2==1
46775    gui_split_preview "b $9%",${-3--1}
46776  elif $-2==2
46777    gui_split_preview "split_details 4,$9%,$10% k.. n 0,255",${-3--1}
46778  elif $-2==3
46779    gui_split_preview "split_details 4,$9%,$10% k.. _fx_smooth_skin $12,$11 n 0,255",${-3--1}
46780  elif $-2==4
46781    gui_split_preview "split_details 4,$9%,$10% k. n 0,255",${-3--1}
46782  else
46783    gui_split_preview "fx_smooth_skin $*",${-3--1}
46784  fi
46785
46786  if $1==1
46787    to_rgb
46788    circle $6%,$7%,$8%,0.2,0,255,0
46789    circle $6%,$7%,$8%,0.4,0xFFFFFFFF,0,255,0
46790    line {$6-0.25*$8}%,{$7-0.25*$8}%,{$6+0.25*$8}%,{$7+0.25*$8}%,0.8,255,255,0
46791    line {$6+0.25*$8}%,{$7-0.25*$8}%,{$6-0.25*$8}%,{$7+0.25*$8}%,0.8,255,255,0
46792  fi
46793
46794#@gui Smooth [Thin Brush] : fx_smooth_anisotropic, fx_smooth_anisotropic(0)
46795#@gui : Amplitude = float(60,0,1000)
46796#@gui : Sharpness = float(0.9,0,2)
46797#@gui : Anisotropy = float(0.64,0,1)
46798#@gui : Gradient Smoothness = float(3.1,0,10)
46799#@gui : Tensor Smoothness = float(1.10,0,10)
46800#@gui : Spatial Precision = float(0.8,0.1,2)
46801#@gui : Angular Precision = float(30,1,180)
46802#@gui : Value Precision = float(2,0.1,5)
46803#@gui : Interpolation = choice(0,"Nearest Neighbor","Linear","Runge-Kutta")
46804#@gui : Fast Approximation = bool(1)
46805#@gui : Iterations = int(1,1,10)
46806#@gui : Channel(s) = choice("RGB","Luminance","Blue & Red chrominances","Blue chrominance","Red chrominance")
46807#@gui : sep = separator()
46808#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46809#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46810#@gui : note = note{"\n<small><b>Note: </b>This set of anisotropic smoothing parameters has been suggested
46811#@gui : by PhotoComiX.</small>"}
46812#@gui : sep = separator()
46813#@gui : note = note("<small>Author: <i>PhotoComiX</i>.      Latest Update: <i>2010/26/12</i>.</small>")
46814
46815#@gui Smooth [Total Variation] : fx_smooth_tv, fx_smooth_tv_preview(0)
46816#@gui : Time Step = float(30,5,100)
46817#@gui : Iterations = int(10,1,40)
46818#@gui : Keep Iterations as Different Layers = bool(false)
46819#@gui : sep = separator()
46820#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46821#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46822#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46823#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46824#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46825#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46826#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46827#@gui : sep = separator()
46828#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46829#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46830#@gui : sep = separator()
46831#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46832#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46833#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46834#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46835#@gui : sep = separator()
46836#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
46837fx_smooth_tv :
46838  ac "gui_parallel_overlap \"tv_flow $2,$1,$3 c 0,255\",$5,$6",$4
46839
46840fx_smooth_tv_preview :
46841  gui_split_preview "fx_smooth_tv $*",${-3--1}
46842
46843#@gui Smooth [Wavelets] : fx_smooth_haar, fx_smooth_haar_preview(0)
46844#@gui : Threshold = float(1,0,10)
46845#@gui : Iterations = int(10,1,32)
46846#@gui : Scales = int(10,2,10)
46847#@gui : sep = separator()
46848#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46849#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46850#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46851#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46852#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46853#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46854#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46855#@gui : sep = separator()
46856#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
46857#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
46858#@gui : sep = separator()
46859#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46860#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46861#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46862#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46863#@gui : sep = separator()
46864#@gui : note = note("<small>Author: <i>Jérome Boulanger and David Tschumperlé</i>.
46865#@gui :       Latest Update: <i>2013/27/08</i>.</small>")
46866fx_smooth_haar :
46867  remove_opacity
46868  ac "gui_parallel_overlap \"denoise_haar $1,$3,$2 c 0,255\",$5,$6",$4
46869
46870fx_smooth_haar_preview :
46871  gui_split_preview "fx_smooth_haar $*",${-3--1}
46872
46873#@gui Upscale [Diffusion] : fx_upscale_smart, fx_upscale_smart_preview(0)
46874#@gui : Width = text("200%")
46875#@gui : Height = text("200%")
46876#@gui : Smoothness = float(2,0,20)
46877#@gui : Anisotropy = float(0.4,0,1)
46878#@gui : Sharpness = float(50,0,100)
46879#@gui : sep = separator()
46880#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46881fx_upscale_smart :
46882  to_rgb upscale_smart $1,$2,1,$3,$4,$5 c 0,255
46883
46884fx_upscale_smart_preview :
46885  repeat $!
46886    +r. $1,$2,1,1,0
46887    if w<{-2,w}" || "h<{-2,h}  # Test for downscaling
46888      rm. /. 4
46889      0 t. "Downscaling is\nnot allowed!",5,5,20,1,255 r. ..,..,1,1,0,0,0.5,0.5
46890      -|[-2,-1]
46891    else
46892      z.. {50-50*{-2,w}/w}%,{50-50*{-2,h}/h}%,{50+50*{-2,w}/w}%,{50+50*{-2,h}/h}%
46893      rm. fx_upscale_smart. $1,$2,$3,$4,$5 c. 0,255
46894    fi
46895  mv. 0 done
46896
46897#@gui Upscale [Scale2x] : fx_scalenx, fx_scalenx_preview(0)
46898#@gui : Scaling Factor = choice("x 2","x 3","x 4","x 6","x 8","x 9","x 12","x 16","x 18","x 27")
46899#@gui : Colorbase = choice(0,"RGB","YCbCr","Lab")
46900#@gui : note = note{"\n<small><b>Note: </b>
46901#@gui : This filter re-implements the scaling algorithm described at :
46902#@gui : </small>"}
46903#@gui : url = link("http://scale2x.sourceforge.net")
46904#@gui : note = note{"<small>
46905#@gui : This filter is useful for resizing images that have very few colors
46906#@gui : (e.g. indexed images). It is generally useless for true colors images.
46907#@gui : </small>"}
46908#@gui : sep = separator()
46909#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46910fx_scalenx :
46911  repeat $! l[$>] split_opacity
46912    if $2==1 rgb2ycbcr[0] round[0]
46913    elif $2==2 rgb2lab8[0] round[0]
46914    fi
46915    if $1==0 scale2x
46916    elif $1==1 scale3x
46917    elif $1==2 scale2x scale2x
46918    elif $1==3 scale3x scale2x
46919    elif $1==4 scale2x scale2x scale2x
46920    elif $1==5 scale3x scale3x
46921    elif $1==6 scale3x scale2x scale2x
46922    elif $1==7 scale2x scale2x scale2x scale2x
46923    elif $1==8 scale3x scale3x scale2x
46924    elif $1==9 scale3x scale3x scale3x
46925    fi
46926    if $2==1 ycbcr2rgb[0]
46927    elif $2==2 lab82rgb[0]
46928    fi
46929  a c endl done
46930
46931fx_scalenx_preview :
46932  z 40%,40%,60%,60%
46933  fx_scalenx $1,$2
46934
46935#@gui Upscale [DCCI2x]: fx_scale_dcci2x, fx_scale_dcci2x_preview(0)
46936#@gui : note = note("<i>Directional Cubic Convolution Interpolation</i>"), sep = separator()
46937#@gui : Threshold = float(1.15,1,2)
46938#@gui : Exponent = int(5,1,6)
46939#@gui : Extend 1px = _bool(0)
46940#@gui : sep = separator()
46941#@gui : note = note("<small>Author: <i>Garagecoder</i>.      Latest Update : <i>2015/11/07</i>.</small>")
46942#@gui : note = note{"\n<small><b>Note: </b>
46943#@gui : This filter re-implements the scaling algorithm described at :
46944#@gui : </small>"}
46945#@gui : url = link("wikipedia.org","https://en.wikipedia.org/wiki/Directional_Cubic_Convolution_Interpolation")
46946#@gui : note = note("<small>The algorithm is intended for enlarging images while avoiding</small>")
46947#@gui : note = note("<small>artifacts, e.g. staircase artifacts.</small>")
46948#@gui : note = note("\n<small>Threshold controls edge[lower] to texture[higher] balance.</small>")
46949#@gui : note = note("<small>Exponent controls texture edge sharpness[higher].</small>")
46950#@gui : note = note("<small>Warning: highly experimental...</small>")
46951fx_scale_dcci2x : skip ${1=1.15},${2=5},${3=0}
46952  repeat $! l[$>]
46953    split_opacity scale_dcci2x ${1-3} a c c 0,255
46954  endl done
46955
46956fx_scale_dcci2x_preview :
46957  z 25%,25%,75%,75% fx_scale_dcci2x $*
46958
46959#@gui ____<b>Rendering</b>
46960#-------------------------
46961
46962# Generic function to render a 3D image, with usual rendering parameters :
46963# $1 = Width
46964# $2 = Height
46965# $3 = Object size
46966# $4 = X-angle
46967# $5 = Y-angle
46968# $6 = Z-angle
46969# $7 = FOV
46970# $8 = X-light
46971# $9 = Y-light
46972# $10 = Z-light
46973# $11 = Specular lightness
46974# $12 = Specular shininess
46975# $13 = Rendering mode.
46976# $14 = Antialiasing (0 | 1)
46977fx_render3d : skip ${14=1}
46978  width={(1+$14)*$1} height={(1+$14)*$2}
46979  n3d c3d m3d {round($13)} f3d={0.5*max($width,$height)/tan($7*pi/360)}
46980  f3d $f3d l3d {$8*$f3d},{$9*$f3d},{$10*$f3d} sl3d $11 ss3d $12
46981  repeat $! l[$>]
46982    *3d {$3*max($width,$height)} r3d 0,0,1,{-$6} r3d 0,1,0,{-$5} r3d 1,0,0,{-$4}
46983    $width,$height,1,3,-1
46984    j3d. ..,50%,50% rm..
46985    to_rgba replace_color 0,0,-1,-1,-1,255,0,0,0,0
46986    if $14
46987      r $1,$2,1,100%,2 s c,-3 +. 1e-5 /[0] [1] *[0] 255 a c
46988    fi
46989  endl done
46990
46991#@gui 3D Blocks : fx_blocks3d, fx_blocks3d(1)
46992#@gui : Resolution = int(32,1,128)
46993#@gui : Smoothness = float(0,0,40)
46994#@gui : Elevation = float(4,-10,10)
46995#@gui : Size = float(1.5,0,3)
46996#@gui : Angle = float(30,0,360)
46997#@gui : Tilt = float(60,0,90)
46998#@gui : FOV = float(45,1,90)
46999#@gui : Centering (%) = point(50,50)
47000#@gui : sep = separator()
47001#@gui : X-Light = float(0,-100,100)
47002#@gui : Y-Light = float(-50,-100,100)
47003#@gui : Z-Light = float(-100,-100,0)
47004#@gui : Specular Lightness = float(0.5,0,1)
47005#@gui : Specular Shininess = float(0.7,0,3)
47006#@gui : Use Light = bool(1)
47007#@gui : Antialiasing = bool(1)
47008#@gui : Outline Color = color(0,0,0,128)
47009#@gui : sep = separator()
47010#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/10/02</i>.</small>")
47011fx_blocks3d :
47012  repeat $! l[$>]
47013    nm=${"gui_layer_name"}
47014    W={w} H={h} M={max(w,h)}
47015    if w>h r2dx $1 else r2dy $1 fi
47016    w={w} h={h} m={max(w,h)}
47017    if $3>0 mirror y fi
47018    imageblocks3d $3,$2%
47019    -3d. {$w/2},{$h/2} f={$4*$M/($m*(2-$16))} *3d $f,$f,{$f*abs($3*$1/100)}
47020    if $3>0 r3d 1,0,0,180 fi
47021    r3d 0,0,1,$5 r3d 1,0,0,-$6
47022
47023    # Render object.
47024    if $16 {2*$M},{2*$M},1,4,-1 else $M,$M,1,4,-1 fi
47025    f3d={0.5*w/tan($7*pi/360)} f3d $f3d
47026    l3d {$10*$f3d},{$11*$f3d},{$12*$f3d} sl3d $13 ss3d $14
47027    j3d. [0],$8%,$9%,0,1,{if($15,3,2)},0,1
47028    sh. 100% +. 1 *. 255 rm.
47029
47030    # Render object outline
47031    if $20
47032      .,.,1,3,-1
47033      j3d. [0],$8%,$9%,0,1,3,0,1 rm[0]
47034      g. xy,1 +[-2,-1] norm. !=. 0
47035      +r. 100%,100%,1,3
47036      sh. 0 *. $17 rm.
47037      sh. 1 *. $18 rm.
47038      sh. 2 *. $19 rm.
47039      j[0] .,0,0,0,0,{$20/255},.. rm[-2,-1]
47040    else rm[0]
47041    fi
47042
47043    replace_color 0,0,-1,-1,-1,0,0,0,0,0
47044    if $16 r 50%,50%,1,4,2 fi
47045    c 0,255 nm name($nm)
47046  endl done
47047
47048#@gui 3D Colored Object : fx_coloredobject3d, fx_coloredobject3d_preview(1)
47049#@gui : Type = choice{1,"Plane","Box","Pyramid","Ellipsoid","Torus","Gyroid","Weird","Cup"}
47050#@gui : Color = color(128,128,128,255)
47051#@gui : sep = separator()
47052#@gui : Size-1 = float(0.5,0,3)
47053#@gui : Size-2 = float(0.5,0,3)
47054#@gui : Size-3 = float(0.5,0,3)
47055#@gui : X-Angle = float(57,0,360)
47056#@gui : Y-Angle = float(41,0,360)
47057#@gui : Z-Angle = float(21,0,360)
47058#@gui : FOV = float(45,1,90)
47059#@gui : X-Light = float(0,-100,100)
47060#@gui : Y-Light = float(0,-100,100)
47061#@gui : Z-Light = float(-100,-100,0)
47062#@gui : Specular Lightness = float(0.5,0,1)
47063#@gui : Specular Shininess = float(0.7,0,3)
47064#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
47065#@gui : Antialiasing = bool(1)
47066#@gui : sep = separator()
47067#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/16/05</i>.</small>")
47068_fx_coloredobject3d :
47069  to_rgb _fx_coloredobject3d$1$2 ${6-8} col3d. ${3-5}
47070  db3d 0
47071
47072fx_coloredobject3d :
47073  _fx_coloredobject3d "_",${1-4,6-8}
47074  repeat $!-1
47075    +fx_render3d. {$>,w},{$>,h},$6,${9--1}
47076    sh. 3 *. {$5/255} rm.
47077    blend[$>,-1] alpha
47078  done
47079  rm.
47080
47081fx_coloredobject3d_preview :
47082  _fx_coloredobject3d "_preview_",${1-4,6-8}
47083  repeat $!-1
47084    +fx_render3d. {$>,w},{$>,h},$6,${9--1}
47085    sh. 3 *. {$5/255} rm.
47086    blend[$>,-1] alpha
47087  done rm.
47088
47089_fx_coloredobject3d_0 : plane3d 1 *3d. $1,$2,1
47090_fx_coloredobject3d_1 : box3d 1 *3d. $1,$2,$3
47091_fx_coloredobject3d_2 : pyramid3d 1,1 *3d. $1,$2,$3
47092_fx_coloredobject3d_3 : sphere3d 1 *3d. 1,{2*$2},{2*$3}
47093_fx_coloredobject3d_4 : torus3d $1,{$2/2},100,50 *3d. $3,0.5,0.5
47094_fx_coloredobject3d_5 : gyroid3d 24 *3d. $1,$2,$3
47095_fx_coloredobject3d_6 : weird3d 32 *3d. $1,$2,$3
47096_fx_coloredobject3d_7 : cup3d 128 *3d. $1,$2,$3
47097_fx_coloredobject3d_preview_0 : plane3d 1 *3d. $1,$2,1
47098_fx_coloredobject3d_preview_1 : box3d 1 *3d. $1,$2,$3
47099_fx_coloredobject3d_preview_2 : pyramid3d 1,1 *3d. $1,$2,$3
47100_fx_coloredobject3d_preview_3 : sphere3d 1 *3d. 1,{2*$2},{2*$3}
47101_fx_coloredobject3d_preview_4 : torus3d $1,{$2/2},100,50 *3d. $3,0.5,0.5
47102_fx_coloredobject3d_preview_5 : gyroid3d 8 *3d. $1,$2,$3
47103_fx_coloredobject3d_preview_6 : weird3d 12 *3d. $1,$2,$3
47104_fx_coloredobject3d_preview_7 : cup3d 64 *3d. $1,$2,$3
47105
47106#@gui 3D Elevation : fx_elevation3d, fx_elevation3d_preview(1)
47107#@gui : Factor = float(100,-1000,1000)
47108#@gui : Smoothness = float(1,0,10)
47109#@gui : sep = separator()
47110#@gui : Width = _int(1024,8,4096)
47111#@gui : Height = _int(1024,8,4096)
47112#@gui : Size = float(0.8,0,3)
47113#@gui : X-Angle = float(25,0,360)
47114#@gui : Y-Angle = float(0,0,360)
47115#@gui : Z-Angle = float(21,0,360)
47116#@gui : FOV = float(45,1,90)
47117#@gui : X-Light = float(0,-100,100)
47118#@gui : Y-Light = float(0,-100,100)
47119#@gui : Z-Light = float(-100,-100,0)
47120#@gui : Specular Lightness = float(0.5,0,1)
47121#@gui : Specular Shininess = float(0.7,0,3)
47122#@gui : Rendering = choice(2,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
47123#@gui : Antialiasing = bool(1)
47124#@gui : sep = separator()
47125#@gui : note = note{"<small><b>Note:</b> Add a top layer to define object texture.</small>"}
47126#@gui : sep = separator()
47127#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47128_fx_elevation3d :
47129  repeat $!/2 l[$>,{min($>+1,$!-1)}]
47130    if $!==1 +norm else r[1] [0],3 fi
47131    n[1] 0,{abs($1)} *[1] {sign($1)} b[1] $2
47132    elevation3d[0] [1] rm[1]
47133  endl done
47134  db3d
47135
47136fx_elevation3d :
47137  _fx_elevation3d ${1-2} fx_render3d ${3--1}
47138
47139fx_elevation3d_preview :
47140  fx_elevation3d ${1-2},{w},{h},${5--1}
47141
47142#@gui 3D Extrusion : fx_extrude3d, fx_extrude3d_preview(1)
47143#@gui : Depth = float(10,1,1024)
47144#@gui : Resolution = int(512,1,1024)
47145#@gui : Smoothness = float(0.6,0,3)
47146#@gui : sep = separator()
47147#@gui : Width = _int(1024,1,4096)
47148#@gui : Height = _int(1024,1,4096)
47149#@gui : Size = float(0.5,0,3)
47150#@gui : X-Angle = float(57,0,360)
47151#@gui : Y-Angle = float(41,0,360)
47152#@gui : Z-Angle = float(21,0,360)
47153#@gui : FOV = float(45,1,90)
47154#@gui : X-Light = float(0,-100,100)
47155#@gui : Y-Light = float(0,-100,100)
47156#@gui : Z-Light = float(-100,-100,0)
47157#@gui : Specular Lightness = float(0.5,0,1)
47158#@gui : Specular Shininess = float(0.7,0,3)
47159#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
47160#@gui : Antialiasing = bool(1)
47161#@gui : sep = separator()
47162#@gui : note = note{"<small><b>Note:</b> Add a top layer to define object texture.</small>"}
47163#@gui : sep = separator()
47164#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47165_fx_extrude3d :
47166  repeat $!/2 l[$>,{min($>+1,$!-1)}]
47167    extrude3d. $1,$2,$3%
47168    if $!==2 t3d. .. rm.. fi
47169  endl done
47170  db3d 0
47171
47172fx_extrude3d :
47173  _fx_extrude3d ${1-3} fx_render3d ${4--1}
47174
47175fx_extrude3d_preview :
47176  fx_extrude3d ${1-3},{w},{h},${6--1}
47177
47178#@gui 3D Image Object : fx_imageobject3d, fx_imageobject3d_preview(1)
47179#@gui : Type = choice{1,"Plane","Cube","Pyramid","Sphere","Torus","Gyroid","Weird","Cup","Rubik"}
47180#@gui : sep = separator()
47181#@gui : Width = _int(1024,1,4096)
47182#@gui : Height = _int(1024,1,4096)
47183#@gui : Size = float(0.5,0,3)
47184#@gui : X-Angle = float(57,0,360)
47185#@gui : Y-Angle = float(41,0,360)
47186#@gui : Z-Angle = float(21,0,360)
47187#@gui : FOV = float(45,1,90)
47188#@gui : X-Light = float(0,-100,100)
47189#@gui : Y-Light = float(0,-100,100)
47190#@gui : Z-Light = float(-100,-100,0)
47191#@gui : Specular Lightness = float(0.5,0,1)
47192#@gui : Specular Shininess = float(0.7,0,3)
47193#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
47194#@gui : Antialiasing = bool(1)
47195#@gui : sep = separator()
47196#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47197_fx_imageobject3d :
47198  to_rgb repeat $! l[$>] _fx_imageobject3d$1$2 endl done
47199  db3d 0
47200
47201fx_imageobject3d :
47202  _fx_imageobject3d "_",$1 fx_render3d ${2--1}
47203
47204fx_imageobject3d_preview :
47205  w={w} h={h} _fx_imageobject3d "_preview_",$1 fx_render3d $w,$h,${4--1}
47206
47207_fx_imageobject3d_0 : imageplane3d
47208_fx_imageobject3d_1 : imagecube3d
47209_fx_imageobject3d_2 : imagepyramid3d
47210_fx_imageobject3d_3 : imagesphere3d 128,64
47211_fx_imageobject3d_4 : torus3d 100,30,100,50 t3d. .. rm..
47212_fx_imageobject3d_5 : gyroid3d 24 t3d. .. rm..
47213_fx_imageobject3d_6 : weird3d 32 t3d. .. rm..
47214_fx_imageobject3d_7 : cup3d 128 t3d. .. rm..
47215_fx_imageobject3d_8 : imagerubik3d 5,5
47216_fx_imageobject3d_preview_0 : imageplane3d
47217_fx_imageobject3d_preview_1 : imagecube3d
47218_fx_imageobject3d_preview_2 : imagepyramid3d
47219_fx_imageobject3d_preview_3 : imagesphere3d 64,32
47220_fx_imageobject3d_preview_4 : torus3d 100,30,100,50 t3d. .. rm..
47221_fx_imageobject3d_preview_5 : gyroid3d 8 c3d. n3d. t3d. .. rm..
47222_fx_imageobject3d_preview_6 : weird3d 12 t3d. .. rm..
47223_fx_imageobject3d_preview_7 : cup3d 64 t3d. .. rm..
47224_fx_imageobject3d_preview_8 : imagerubik3d 3,3,5,5
47225
47226#@gui 3D Lathing : fx_lathing3d, fx_lathing3d_preview(1)
47227#@gui : Resolution = int(76,1,1024)
47228#@gui : Smoothness = float(2,0,5)
47229#@gui : Max Angle = float(361,0,361)
47230#@gui : sep = separator()
47231#@gui : Width = _int(1024,1,4096)
47232#@gui : Height = _int(1024,1,4096)
47233#@gui : Size = float(0.5,0,3)
47234#@gui : X-Angle = float(0,0,360)
47235#@gui : Y-Angle = float(0,0,360)
47236#@gui : Z-Angle = float(0,0,360)
47237#@gui : FOV = float(45,1,90)
47238#@gui : X-Light = float(0,-100,100)
47239#@gui : Y-Light = float(0,-100,100)
47240#@gui : Z-Light = float(-100,-100,0)
47241#@gui : Specular Lightness = float(0.5,0,1)
47242#@gui : Specular Shininess = float(0.7,0,3)
47243#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
47244#@gui : Antialiasing = bool(1)
47245#@gui : sep = separator()
47246#@gui : note = note{"<small><b>Note:</b> Add a top layer to define object texture.</small>"}
47247#@gui : sep = separator()
47248#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47249_fx_lathing3d :
47250  repeat $!/2 l[$>,{min($>+1,$!-1)}]
47251    lathe3d. $1,$2%,$3
47252    if $!==2 t3d. .. rm.. fi
47253  endl done
47254  db3d 0
47255
47256fx_lathing3d :
47257  _fx_lathing3d ${1-3} fx_render3d ${4--1}
47258  nm name("[3D lathing]"),pos(0,0),mode(alpha)
47259
47260fx_lathing3d_preview :
47261  fx_lathing3d ${1-3},{w},{h},${6--1}
47262
47263#@gui 3D Random Objects : fx_random3d, fx_random3d(1)
47264#@gui : Type = choice("Cube","Cone","Cylinder","Sphere","Torus")
47265#@gui : Density = int(50,1,300)
47266#@gui : Size = float(3,1,20)
47267#@gui : Z-Range = float(100,0,300)
47268#@gui : FOV = float(45,1,90)
47269#@gui : X-Light = float(0,-100,100)
47270#@gui : Y-Light = float(0,-100,100)
47271#@gui : Z-Light = float(-100,-100,0)
47272#@gui : Specular Lightness = float(0.5,0,1)
47273#@gui : Specular Shininess = float(0.7,0,3)
47274#@gui : Rendering = choice(3,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
47275#@gui : Opacity = float(1,0,1)
47276#@gui : sep = separator()
47277#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47278fx_random3d :
47279  repeat $! l[$>]
47280    f3d={0.5*max(w,h)/tan($5*pi/360)} f3d $f3d l3d {$6*$f3d},{$7*$f3d},{$8*$f3d} sl3d $9 ss3d $10
47281    to_rgb ({w},{h},{d},{s}) /. 2 repeat $2
47282    ({1,@0}) +. {1,@1} *. $3 /. 100 _fx_random3d$1 {^} rm..
47283    r3d. 1,1,0,{u(0,360)}
47284    ({u(-1,1)}) *. {1,@0} ({u(-1,1)}) *. {1,@1}
47285    +3d... {-2,^},{^},{u(-$4,$4)} rm[-2,-1]
47286    col3d. {u(255)},{u(255)},{u(255)} done +3d[2--1] j3d[0] .,50%,50%,0,$12,$11,0,1
47287    k[0]
47288  endl done
47289
47290_fx_random3d0 : box3d $1
47291_fx_random3d1 : ($1) /. 2 cone3d {^},$1 rm..
47292_fx_random3d2 : ($1) /. 2 cylinder3d {^},$1 rm..
47293_fx_random3d3 : sphere3d $1,2
47294_fx_random3d4 : ($1) /. 3 torus3d $1,{^} rm..
47295
47296#@gui Ball : fx_ball, fx_ball_preview(0)
47297#@gui : Radius = int(128,1,1024)
47298#@gui : Specular Light = float(0.8,0,8)
47299#@gui : Specular Size = float(1,0,8)
47300#@gui : Shadow = float(1.5,0,4)
47301#@gui : Color = color(255,0,255)
47302#@gui : sep = separator()
47303#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/11</i>.</small>")
47304fx_ball :
47305  ball $1,${5-7},${2-4}
47306  if $!>1 mv. 0 nm[0] "name(Ball),pos("{0,0.5*([${-max_wh}]-[w,h])}")" else nm[0] "name(Ball)" fi
47307
47308fx_ball_preview :
47309  fx_ball $*
47310  if $!>1 rv[-2,-1] blend[-2,-1] alpha fi
47311
47312#@gui Circle Art : fx_circle_art, fx_circle_art
47313#@gui : Type = choice(1,"Random","Lissajous spiral")
47314#@gui : Density = float(15,0,100)
47315#@gui : Radius = float(0.5,0,1)
47316#@gui : Modulo = int(8,2,16)
47317#@gui : Anti-Aliasing = bool(1)
47318#@gui : Random Colors = bool(1)
47319#@gui : sep = separator()
47320#@gui : note = note("<small><b>Lissajous parameters:</b></small>")
47321#@gui : Curve Length = float(15,0,50)
47322#@gui : Curve Angle = float(0,0,360)
47323#@gui : Minimal Radius = float(0,-5,5)
47324#@gui : Maximal Radius = float(0.5,-5,5)
47325#@gui : X-Dispersion = float(1,0,4)
47326#@gui : Y-Dispersion = float(1,0,4)
47327#@gui : X-Factor = int(1,0,16)
47328#@gui : Y-Factor = int(1,0,16)
47329#@gui : sep = separator()
47330#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/22/08</i>.</small>")
47331fx_circle_art :
47332  if !$2 f 0 return fi
47333  # Generate object coordinates.
47334  if $1==0 # Random.
47335    {round(2*($2^1.5))}
47336    rand. -1,1 +rand. -1,1 +rand. -$3,$3 a[-3--1] y
47337  else # Spiral.
47338    {max(1,round($2*$7))}
47339    t0={$8*2*pi/180}
47340    rows. 0,2
47341    f. "r = x/(w-1);
47342        t = 2*pi*x/$2;
47343        if(y==0,(r^$11)*cos("$t0"+$13*t),
47344        if(y==1,(r^$12)*sin("$t0"+$14*t),
47345                 max(0,$3*($9+($10-$9)*r))))"
47346  fi
47347
47348  # Convert to 3D object.
47349  l.
47350    transpose s x,-1 h={h}
47351    i[0] ({'CImg3d'},{2*$h},$h) # Header.
47352    ++... . -[-4,-2] i .. i[-3,-1] 1,100% a[-6--1] x # Vertices.
47353    1,$h,1,1,5 1,$h,1,1,2*y ++. 1 a[-3--1] x z. 0,5 # Primitives.
47354    3,$h,1,1,1 1,$h,1,1,-1 y a y # Colors + Opacities.
47355  endl
47356
47357  # Render object on selected images.
47358  repeat $!-1 l[$>,-1]
47359    s={0,max(w,h)} rm[0]
47360    if $5 {2*$s},{2*$s} +*3d[0] $s # Anti-aliasing.
47361    else $s,$s +*3d[0] {$s/2} # No anti-aliasing.
47362    fi
47363    j3d[1] [2],50%,50%,0,1,2,0,0 rm[2]
47364    %. $4
47365    if $6 i.. 100%,100%,1,3 rand.. 0,255 plasma.. 1,1 equalize.. 256 n.. 0,255 blend[-2,-1] shapeaverage fi
47366    rv
47367  endl done
47368  rm.
47369  n 0,255
47370  if $5 r 50%,50%,1,100%,2 fi
47371
47372#@gui Equation Plot [Parametric] : fx_equation_parametric, fx_equation_parametric
47373#@gui : X(t) = text{"sin(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)"}
47374#@gui : Y(t) = text{"cos(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)"}
47375#@gui : Min-t = float(0,-1000,1000)
47376#@gui : Max-t = float(100,-1000,1000)
47377#@gui : Resolution = int(4096,2,32768)
47378#@gui : Outline Opacity = float(1,0,1)
47379#@gui : Dot Size = int(0,0,16)
47380#@gui : Start Color = color(64,0,0)
47381#@gui : End Color = color(128,0,0)
47382#@gui : Colored Outline = bool(1)
47383#@gui : Antialiasing = bool(1)
47384#@gui : Decoration = bool(1)
47385#@gui : sep = separator()
47386#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/13/11</i>.</small>")
47387fx_equation_parametric :
47388  repeat $! l[$>]
47389    w={w} h={h} rm
47390    $5,1,1,2,"t=$3+x*($4-$3)/($5-1);if(c==0,$1,$2)"
47391    channels. 0,2
47392    ($8,$11^$9,$12^$10,$13) r. {-2,w},1,1,3,3 a c
47393    display_parametric $w,$h,{$6+$14*1.001},$7,$15,$16
47394  endl done
47395
47396#@gui Equation Plot [Y=f(X)] : fx_equation_plot, fx_equation_plot
47397#@gui : F(X) = text{"X*c+10*cos(X+c+u)"}
47398#@gui : X-Min = float(-10,-100,100)
47399#@gui : X-Max = float(10,-100,100)
47400#@gui : Resolution = int(100,2,1024)
47401#@gui : Channels = int(3,1,32)
47402#@gui : Plot Type = choice(2,"None","Lines","Splines","Bars")
47403#@gui : Vertex Type = choice(0,"None","Points","Crosses 1","Crosses 2","Circles 1","Circles 2","Square 1","Square 2")
47404#@gui : sep = separator()
47405#@gui : note = note("<small><b>Note</b> :
47406#@gui : Use variable <b>X</b> instead of <b>x</b> in the above equation to take care of the X-min/max settings.
47407#@gui : Variable <b>c</b> refers to the current channel number.
47408#@gui : Variable <b>u</b> refers to a uniformly distributed random value in [0,1].
47409#@gui : Reduce resolution to be able to view
47410#@gui : separate graph vertices.</small>")
47411#@gui : sep = separator()
47412#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47413fx_equation_plot :
47414  repeat $! l[$>]
47415    w={w} h={h} rm
47416    $4,1,1,$5,"X=$2+($3-$2)*x/($4-1);$1"
47417    dg $w,$h,$6,$7,$2,$3
47418  endl done
47419
47420#@gui Gradient [Corners] : fx_corner_gradient, fx_corner_gradient
47421#@gui : Color 1 (Up/Left Corner) = color(255,255,255,128)
47422#@gui : Color 2 (Up/Right Corner) = color(255,0,0,255)
47423#@gui : Color 3 (Bottom/Left Corner) = color(0,255,0,255)
47424#@gui : Color 4 (Bottom/Right Corner) = color(0,0,255,255)
47425#@gui : sep = separator()
47426#@gui : Colorspace = choice(1,"sRGB","Linear RGB","Lab")
47427#@gui : sep = separator()
47428#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47429fx_corner_gradient : skip ${17=0}
47430  repeat $! l[$>]
47431    wh={w},{h} rm
47432    ($1,$5;$9,$13^$2,$6;$10,$14^$3,$7;$11,$15^$4,$8;$12,$16)
47433    _gb_fwd $17
47434    r. $wh,1,100%,3
47435    _gb_bwd $17
47436  endl done
47437
47438#@gui Gradient [Custom Shape] : fx_custom_gradient, fx_custom_gradient_preview(1)
47439#@gui : note = note("<small><b>Shape selection:</b></small>")
47440#@gui : Select By = choice("Auto","Dark Pixels","Bright Pixels","Opaque Pixels")
47441#@gui : Smoothness = float(0,0,10)
47442#@gui : Threshold = float(0,0,100)
47443#@gui : Preview Shape = bool(1)
47444#@gui : note = note("<small><b>Note:</b> Shapes with small strokes may lead to incorrect previews.</small>")
47445#@gui : sep = separator()
47446#@gui : note = note("<small><b>Gradient parameters:</b></small>")
47447#@gui : Number of Colors = int(4,2,10)
47448#@gui : Cycles = float(1,1,16)
47449#@gui : Offset = float(0,0,100)
47450#@gui : Shading = float(128,1,256)
47451#@gui : Inner Length = float(100,0,100)
47452#@gui : Outer Length = float(100,0,100)
47453#@gui : Spatial Metric = choice(2,"Chebyshev","Manhattan","Euclidean")
47454#@gui : Color Metric = choice("RGB","HSV","Lab")
47455#@gui : Shade Back to First Color = bool(1)
47456#@gui : Preview Gradient = bool(0)
47457#@gui : Save Gradient As = _text("")
47458#@gui : sep = separator()
47459#@gui : note = note("<small><b>Color definitions:</b></small>")
47460#@gui : Colormap Type = choice(1,"Pre-Defined","User-Defined")
47461#@gui : Pre-Defined Colormap = int(0,0,65535)
47462#@gui : 1st Color = color(0,0,0,255)
47463#@gui : 2nd Color = color(255,0,0,255)
47464#@gui : 3rd Color = color(255,255,0,255)
47465#@gui : 4th Color = color(255,255,255,255)
47466#@gui : 5th Color = color(0,255,255,255)
47467#@gui : 6th Color = color(0,255,0,255)
47468#@gui : 7th Color = color(0,0,255,255)
47469#@gui : 8th Color = color(128,128,128,255)
47470#@gui : 9th Color = color(255,0,255,255)
47471#@gui : 10th Color = color(0,0,0,0)
47472#@gui : sep = separator()
47473#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/03/10</i>.</small>")
47474fx_custom_gradient_preview : skip "${15=}"
47475  repeat $! l[$>]
47476    if $4
47477      +_fx_custom_gradient1 ${1-14},"$15",${16--1} +erode. 3 -[-2,-1] +dilate. 5 a[-2,-1] c n. 0,255
47478      fx_custom_gradient[0] ${1-14},"$15",${16--1},-1 blend alpha
47479    else fx_custom_gradient ${1-14},"$15",${16--1},-1
47480    fi
47481    if $14
47482      +_fx_custom_gradient0[] ${1-14},"$15",${16--1} r. {{0,w}-16},16,1,4 frame. 1,1,0 j[0] .,8,{0,h-25} rm.
47483    fi
47484  endl done
47485
47486fx_custom_gradient : skip "${15=}"
47487  _fx_custom_gradient0 ${1-14},"$15",${16--1}
47488  if $-1>=0" && "narg("$15")
47489    dir_ggr=${-path_gimp}gradients
47490    0 nm. ${"normalize_filename \"$15\""} name_ggr={b} rm. output_ggr. $dir_ggr/$name_ggr.ggr,"$15"
47491  fi
47492  i.. (0^0^0^0) a[-2,-1] x
47493  repeat $!-1 l[$>,-1]
47494    _fx_custom_gradient1[0] ${1-14},"$15",${16--1}
47495    +distance[0] 1,$11 +distance[0] 0,$11 *. -1 +[0] 1 +[0,-2,-1]  # Signed distance function.
47496    m={$9%*{0,im}} M={$10%*{0,iM}}
47497    -[0] $m *[0] {1,(w-2)/($M-$m)} +[0] 1
47498    round[0] map[0] .
47499  endl done
47500  rm.
47501
47502# Create colormap.
47503_fx_custom_gradient0 :
47504  if $16 4,8,1,1,${18-56} permute. yzcx
47505  else 8,1,1,3 srand $17 rand. 0,255 to_rgba.
47506  fi
47507  z. 0,{$5-1}
47508  if $13
47509    r. {200*$6}%,1,1,4,0,2
47510    __fx_custom_gradient0. $12,$8
47511    shift. {-round(w*0.5*$7%)},0,0,0,2 z. 0,{w/2-1}
47512  else
47513    __fx_custom_gradient0. $12,$8
47514    r. {100*$6}%,1,1,4,0,2 shift. {-round(w*$7%)},0,0,0,2
47515    fi
47516
47517__fx_custom_gradient0 :
47518  if $1==1 sh. 0,2 rgb2hsv. rm.
47519  elif $1==2 sh. 0,2 srgb2rgb. rgb2lab. rm.
47520  fi
47521  r. {$2*w},1,1,4,3
47522  if $1==1 sh. 0,2 hsv2rgb. rm.
47523  elif $1==2 sh. 0,2 lab2rgb. rgb2srgb. rm.
47524  fi
47525
47526# Extract shape from image.
47527_fx_custom_gradient1 :
47528  b $2%
47529  if $1==0 # Auto-mode.
47530    to_a split_opacity
47531    if iM>im+32
47532      rm.. >=[0] {100-$3}%
47533    else
47534      rm. norm n 0,1
47535      if ia>0.5 <=[0] $3% else >=[0] {100-$3}% fi
47536    fi
47537  elif $1==1 # Dark pixels.
47538    remove_opacity norm <= $3%
47539  elif $1==2 # Bright pixels.
47540    remove_opacity norm >= {100-$3}%
47541  else # Opaque pixels.
47542    to_a channels 100% >= {100-$3}%
47543  fi
47544
47545#@gui Gradient [from Line] : fx_line_gradient, fx_line_gradient_preview(1)
47546#@gui : Starting Point (%) = point(0,0,0,1,255,0,0)
47547#@gui : Ending Point (%) = point(100,100,0,1,64,128,255)
47548#@gui : Sampling = float(100,0,100)
47549#@gui : Length = int(0,0,4096)
47550#@gui : note = note("<small><b>Note:</b> Set length to <i>0</i> to release gradient length constraints.</small>")
47551#@gui : Sort Colors = choice("Don't Sort","By Red Component","By Green Component","By Blue Component",
47552#@gui : "By Luminance","By Blue Chrominance","By Red Chrominance","By Lightness")
47553#@gui : Reverse Gradient = bool(0)
47554#@gui : sep = separator()
47555#@gui : Preview Gradient = bool(1)
47556#@gui : Save Gradient As = _text("")
47557#@gui : sep = separator()
47558#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/29/06</i>.</small>")
47559fx_line_gradient : skip "${10=}"
47560  _fx_line_gradient $*
47561  if narg("$10")
47562    dir_ggr=${-path_gimp}gradients
47563    0 nm. ${"normalize_filename \"$10\""} name_ggr={b} rm. output_ggr. $dir_ggr/$name_ggr.ggr,"$10"
47564  fi
47565  repeat $! r[$>] 100%,64,1,100% done
47566
47567fx_line_gradient_preview :
47568  repeat $! l[$>]
47569    to_rgba
47570    if $9 +_fx_line_gradient $* fi
47571    l[0]
47572      line $1%,$2%,$3%,$4%,1,0xF0F0F0F0,255,255,255,255
47573      line $1%,$2%,$3%,$4%,1,0x0F0F0F0F,0,0,0,255
47574    endl
47575    if $!>1 r. {{0,w}-32},32,1,4,1 frame. 1,1,0,0,0,255 j[0] [1],16,{{0,h}-48} rm. fi
47576  endl done
47577
47578_fx_line_gradient :
47579  at_line $1%,$2%,0,$3%,$4%,0 r {max(0.1,$5)}%,1,1,100%,1
47580  m "feature1 : channels 0"
47581  m "feature2 : channels 1"
47582  m "feature3 : channels 2"
47583  m "feature4 : to_rgb luminance"
47584  m "feature5 : to_rgb rgb2ycbcr channels 1"
47585  m "feature6 : to_rgb rgb2ycbcr channels 2"
47586  m "feature7 : to_rgb srgb2rgb rgb2lab channels 0"
47587  if $7 repeat $! l[$>] +feature$7 rv a y sort +,x rows 1 endl done fi
47588  if $6 r $6,1,1,100%,3 fi
47589  if $8 mirror x fi
47590
47591#@gui Gradient [Linear] : fx_linear_gradient, fx_linear_gradient
47592#@gui : Starting Color = color(0,0,0,255)
47593#@gui : Ending Color = color(255,255,255,255)
47594#@gui : Swap Colors = bool(0)
47595#@gui : Angle = float(45,0,360)
47596#@gui : Fade Start = float(0,0,100)
47597#@gui : Fade End = float(100,0,100)
47598#@gui : sep = separator()
47599#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
47600#@gui : sep = separator()
47601#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47602fx_linear_gradient : skip ${13=0}
47603  repeat $! l[$>]
47604    wh={w},{h} rm
47605    ($1^$2^$3^$4) ($5^$6^$7^$8)
47606    if $9 rv[-2,-1] fi
47607    r $wh
47608    _gb_fwd $13
47609    fade_linear $10,$11,$12
47610    _gb_bwd $13
47611  endl done
47612
47613#@gui Gradient [Radial] : fx_radial_gradient, fx_radial_gradient
47614#@gui : Starting Color = color(0,0,0,255)
47615#@gui : Ending Color = color(255,255,255,255)
47616#@gui : Swap Colors = bool(0)
47617#@gui : Fade Start = float(0,0,100)
47618#@gui : Fade End = float(100,0,100)
47619#@gui : Center (%) = point(50,50,0,1,255)
47620#@gui : sep = separator()
47621#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
47622#@gui : sep = separator()
47623#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/29/06</i>.</small>")
47624fx_radial_gradient : skip ${14=0}
47625  repeat $! l[$>]
47626    wh={w},{h} rm
47627    ($1^$2^$3^$4) ($5^$6^$7^$8)
47628    if $9 rv[-2,-1] fi
47629    r $wh
47630    _gb_fwd $14
47631    100%,100% =. 1,$12%,$13% distance. 1 _fade $10,$11
47632    _gb_bwd $14
47633  endl done
47634
47635#@gui Gradient [Random] : fx_random_gradient, fx_random_gradient
47636#@gui : Density = int(32,1,1024)
47637#@gui : Seed = int(0,0,65535)
47638#@gui : Smoothness = float(0,0,10)
47639#@gui : Color Balance = color(128,128,128)
47640#@gui : Opacity = float(1,0,1)
47641#@gui : sep = separator()
47642#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/08/04</i>.</small>")
47643fx_random_gradient :
47644  repeat $! l[$>]
47645    to_rgba 100%,100% srand $2
47646    eval "repeat ($1,n,
47647            x = round(u(w-1));
47648            y = round(u(h-1));
47649            i(x,y) = 1;
47650            i(#0,x,y,0,0) = round(u(255));
47651            i(#0,x,y,0,1) = round(u(255));
47652            i(#0,x,y,0,2) = round(u(255));
47653            i(#0,x,y,0,3) = $7*255 + (1-$7)*round(u(255));
47654          )"
47655    if $7!=1 sh.. 100% n. 0,255 rm. fi
47656    ==. 0
47657    sh.. 0,2 srgb2rgb. rm.
47658    inpaint_pde.. [1],100%,1 rm.
47659    b $3% n 0,255
47660    sh 0,2 rgb2srgb. balance_gamma. ${4-6} rm.
47661  endl done
47662
47663#@gui Hypotrochoid : fx_hypotrochoid, fx_hypotrochoid(1)
47664#@gui : Periods = int(37,1,100)
47665#@gui : Outer Radius (%) = float(100,0,300)
47666#@gui : Inner Radius (%) = float(74,0,300)
47667#@gui : Distance to center (%) = float(80,0,300)
47668#@gui : Thickness (%) = float(0.5,0,5)
47669#@gui : Color = color(255,255,255,255)
47670#@gui : Anti-aliasing = bool(1)
47671#@gui : sep = separator()
47672#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/01/25</i>.</small>")
47673fx_hypotrochoid :
47674  {0,[w,h]*($10?1.5:1)}
47675  eval "
47676    const M = min(w,h)/2;
47677    const A = M*$2%;
47678    const B = A*$3%;
47679    const H = B*$4%;
47680    const S = M*$5%;
47681    const F = (A - B)/max(1e-5,B);
47682    const AmB = A - B;
47683    hypotrochoid(t) = (_t = t; round([ w/2 + AmB*cos(_t) + H*cos(F*_t), h/2 + AmB*sin(_t) - H*sin(F*_t) ]));
47684
47685    oX = hypotrochoid(t);
47686    dt = 1;
47687    for (t = 0, t<$1*2*pi,
47688      do (
47689        X = hypotrochoid(t + dt);
47690        dist = abs(X[0] - oX[0]) | abs(X[1] - oX[1]);
47691        !dist?(dt*=2):
47692        dist>1?(dt/=1.25):
47693        (t+=dt),
47694        dist!=1;
47695      );
47696      S<1?(I(X) = $9):ellipse(X,S,S,0,1,$9);
47697      oX = X;
47698    )"
47699  r. [0],[0],1,1,2
47700  channels. -3,0 sh. 0,2 fc. ${6-8} rm.
47701  blend[0,-1] alpha
47702
47703#@gui Lightning : fx_lightning, fx_lightning_preview
47704#@gui : note = note{"<small><b>Global parameters:</b></small>"}
47705#@gui : Number of Streaks = int(20,1,1024)
47706#@gui : Size (%) = float(90,0,150)
47707#@gui : Resolution = int(256,2,4096)
47708#@gui : Randomness = float(3,0,16)
47709#@gui : Smoothness = float(1.5,0,10)
47710#@gui : Balance = float(0.75,0,1)
47711#@gui : Color = color(255,255,255,255)
47712#@gui : Seed = int(0,0,65535)
47713#@gui : sep = separator()
47714#@gui : note = note{"<small><b>Initial streak:</b></small>"}
47715#@gui : XY-Coordinates (%) = point(50,5,0,1)
47716#@gui : Angle (deg) = float(0,-180,180)
47717#@gui : Thickness (px) = int(6,1,64)
47718#@gui : Blur = float(0.2,0,3)
47719#@gui : sep = separator()
47720#@gui : note = note{"<small><b>Auxiliary streaks:</b></small>"}
47721#@gui : Min Offset (%) = float(25,0,100)
47722#@gui : Max Offset (%) = float(60,0,100)
47723#@gui : Min Length (%) = float(95,0,200)
47724#@gui : Max Length (%) = float(100,0,200)
47725#@gui : Min Angle Deviation (deg) = float(30,0,180)
47726#@gui : Max Angle Deviation (deg) = float(40,0,180)
47727#@gui : Thickness Factor = float(-0.25,-1,1)
47728#@gui : Blur Factor = float(-0.1,-1,1)
47729#@gui : Opacity Factor = float(-0.20,-1,1)
47730#@gui : sep = separator()
47731#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/27/11</i>.</small>")
47732fx_lightning :
47733  repeat $! l[$<]
47734    100%,100% l. fact={max(w,h)/$3*$2%} srand $11
47735    repeat $1
47736      if $!<=1
47737        i=0
47738        new_level=1
47739        new_length=$3
47740        new_x=0
47741        new_y=0
47742        new_angle=$14
47743      else
47744        i={round(u(1,max(1,($!-1)*$6)))}
47745        level={$i,@-2}
47746        angle={$i,@-1}
47747        nb_points={$i,i[6]}
47748        p={round(($nb_points-2)*u($17%,$18%))}
47749        new_level={$level+1}
47750        new_length={max(2,round(($nb_points-$p)*u($19%,$20%)))}
47751        new_x={$i,i[8+3*$p]}
47752        new_y={$i,i[9+3*$p]}
47753        new_angle={$angle+u($21,$22)*if(u>0.5,1,-1)}
47754      fi
47755
47756      _fx_lightning $new_length,$4,$5
47757      r3d. 0,0,1,$new_angle
47758      +3d. $new_x,$new_y
47759      +*3d. $fact [0],[0] j3d. ..,$12%,$13%,0,1,1,0,0 rm..
47760
47761      dilation={$15*(if($23>0,1.5,10)^($23*($new_level-1)))}
47762      blur={max(0,-1+(1+$16)*(if($24>0,2,5)^($24*($new_level-1))))}
47763      opacity={min(1,$10/255*(2^($25*($new_level-1))))}
47764
47765      dilate. $dilation b. $blur% n. 0,1 *. $opacity max[0,-1]
47766      ($new_level;$new_angle) a[-2,-1] y
47767      progress {($>*100)/($1-1)}
47768    done
47769    k[0] * 255 i[0] 100%,100%,1,3 fc[0] ${7-9} a c
47770    endl
47771    rv
47772  endl done
47773
47774fx_lightning_preview :
47775  repeat $! l[$>]
47776    fx_lightning $* rv blend alpha
47777  endl done
47778
47779_fx_lightning :
47780  l[]
47781    ({'CImg3d'},$1,{$1-1})
47782    1,$1 noise. $2,1 cumulate. b. $3 shift. 0,1 1,100%,1,1,y 1,100% a[-3--1] x
47783    1,{h-1},1,1,2 +f. y ++. 1 a[-3--1] x
47784    4,100%,1,1,1
47785    y a y
47786  endl
47787
47788#@gui Lissajous : fx_lissajous, fx_lissajous(1)
47789#@gui : Resolution = int(4096,2,8192)
47790#@gui : sep = separator()
47791#@gui : X-Size = float(0.9,0,2)
47792#@gui : Y-Size = float(0.9,0,2)
47793#@gui : Z-Size = float(3,1,10)
47794#@gui : sep = separator()
47795#@gui : X-Multiplier = float(8,0,32)
47796#@gui : Y-Multiplier = float(7,0,32)
47797#@gui : Z-Multiplier = float(0,0,32)
47798#@gui : sep = separator()
47799#@gui : X-Offset = float(0,0,1)
47800#@gui : Y-Offset = float(0,0,1)
47801#@gui : Z-Offset = float(0,0,1)
47802#@gui : sep = separator()
47803#@gui : X-Angle = float(0,0,360)
47804#@gui : Y-Angle = float(0,0,360)
47805#@gui : Z-Angle = float(0,0,360)
47806#@gui : sep = separator()
47807#@gui : Thickness = float(0,0,50)
47808#@gui : Color = color(255,255,255,255)
47809#@gui : sep = separator()
47810#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/04</i>.</small>")
47811fx_lissajous :
47812  repeat $! l[$>] to_rgba
47813    {w},{h}
47814    f3d {0.5*max(w,h)/tan($4*pi/360)}
47815    lissajous3d $1,$5,$8,$6,$9,$7,$10
47816    r3d. 0,0,1,$13 r3d. 0,1,0,$12 r3d. 1,0,0,$11
47817    *3d. {0.5*$2*{-2,w}},{0.5*$3*{-2,h}},{0.5*$4*max({-2,w},{-2,h})}
47818    col3d. 1 j3d.. .,50%,50%,0,1,1,0,0 rm.
47819    distance. 1 >. $14% *.. . ==. 0
47820    r. 100%,100%,1,4
47821    sh. 0 *. $15 rm.
47822    sh. 1 *. $16 rm.
47823    sh. 2 *. $17 rm.
47824    sh. 3 *. $18 rm.
47825    +[-2,-1]
47826  endl done
47827
47828#@gui Mandelbrot - Julia Sets : fx_mandelbrot, fx_mandelbrot_preview
47829#@gui : X0 = value(-2)
47830#@gui : Y0 = value(-2)
47831#@gui : X1 = value(2)
47832#@gui : Y1 = value(2)
47833#@gui : note = note{"<small><b>Fractal Type:</b></small>"}
47834#@gui : Fractal Set = choice("Mandelbrot","Julia")
47835#@gui : Iterations = int(1024,16,65535)
47836#@gui : X-Seed (Julia) = float(0.317,-2,2)
47837#@gui : Y-Seed (Julia) = float(0.03,-2,2)
47838#@gui : sep = separator()
47839#@gui : note = note{"<small><b>Colormap:</b></small>"}
47840#@gui : Number of Colors = int(16,2,2048)
47841#@gui : Smoothness = int(8,1,256)
47842#@gui : Seed = int(255,0,65536)
47843#@gui : sep = separator()
47844#@gui : note = note{"<small><b>Navigation:</b></small>"}
47845#@gui : Zoom Center = point(50,50,0,0,255,255,255,200)
47846#@gui : Zoom Factor = float(0.25,0,1)
47847#@gui : Zoom In = button()
47848#@gui : Center = button()
47849#@gui : Zoom Out = button()
47850#@gui : Display Coordinates = bool(0)
47851#@gui : sep = separator()
47852#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/06/27</i>.</small>")
47853fx_mandelbrot :
47854  if !narg($_size) _size={max(w,h)} fi
47855  rm $_size,$_size
47856  mandelbrot ${1-4},$6,{$5?[1,$7,$8]:[0,0,0]}
47857  srand $11 $9,1,1,3 rand. 0,255 r. {$9*$10},1,1,3,3 point. 0 map.. .,3 rm.
47858
47859fx_mandelbrot_preview :
47860  _size={min(${-gui_preview_wh})}
47861  if "$15 || $16 || $17"
47862    x0,y0,x1,y1={"P0 = [${1,2}];
47863                  dP = [${3,4}] - P0;
47864                  C = P0 + [${12,13}]%*dP;
47865                  zfact = $14*($15?1:$16?0:-2);
47866                  dC = 0.5*dP*(1 - 0.98*zfact);
47867                  [C - dC,C + dC]"}
47868    status=\{$x0\}\{$y0\}\{$x1\}\{$y1\}\{$5\}\{$6\}\{$7\}\{$8\}\{$9\}\{$10\}\{$11\}\
47869           \{50,50\}\{$14\}\{0\}\{0\}\{0\}\{$18\}
47870    px,py=50
47871  else
47872    x0,y0,x1,y1=${1-4}
47873    status=
47874    px,py=${12,13}
47875  fi
47876  fx_mandelbrot $x0,$y0,$x1,$y1,${5--1}
47877
47878  x0r,y0r,x1r,y1r={"C = ["$px,$py"]%*w; dC = 0.5*w*(1 - 0.98*$14); round([C - dC, C + dC - 1])"}
47879  rectangle $x0r,$y0r,$x1r,$y1r,0.7,0xF0F0F0F0,255,255,255,255
47880  rectangle $x0r,$y0r,$x1r,$y1r,0.7,0x0F0F0F0F,0,0,0,255
47881  if $18 to "Z0 = ( "{_$x0}" , "{_$y0}" )\nZ1 = ( "{_$x1}" , "{_$y1}" )",2,2,16 fi
47882  u $status
47883
47884#@gui Neon Lightning : fx_neon_lightning, fx_neon_lightning(1)
47885#@gui : Source (%) = point(50,50)
47886#@gui : R0 = float(0,0,100)
47887#@gui : Destination (%) = point(50,50)
47888#@gui : R1 = float(100,0,100)
47889#@gui : sep = separator()
47890#@gui : Density = int(50,1,512)
47891#@gui : Glow = float(0.7,0,5)
47892#@gui : Thickness = float(3,0,20)
47893#@gui : sep = separator()
47894#@gui : Color = color(130,80,50)
47895#@gui : Color Dispersion = float(0.25,0,1)
47896#@gui : Transparency = float(0,0,1)
47897#@gui : sep = separator()
47898#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/30/06</i>.</small>")
47899fx_neon_lightning :
47900  d={$13*255}
47901  repeat $! l[$>]
47902    100%,100%,1,4 rm[0]
47903    repeat $7
47904      x0={max(0,min(w,$1+u(-$3,$3)))} y0={max(0,min(h,$2+u(-$3,$3)))}
47905      x1={max(0,min(w,$4+u(-$6,$6)))} y1={max(0,min(h,$5+u(-$6,$6)))}
47906      u0={u(0,100)} v0={u(0,100)} u1={u(0,100)} v1={u(0,100)}
47907      R={max(0,min(255,u($10-$d,$10+$d)))}
47908      G={max(0,min(255,u($11-$d,$11+$d)))}
47909      B={max(0,min(255,u($12-$d,$12+$d)))}
47910      spline $x0%,$y0%,$u0%,$v0%,$x1%,$y1%,$u1%,$v1%,1,$R,$G,$B,1
47911    done
47912    s c,-3
47913    b[0] 3%
47914    distance. 1 *. -1 c. -{$9+1e-5},0 n. 0,1 sqrt.
47915    +b. $8%,1 n. 0,1 sqrt. n[-2,-1] 0,255 max[-2,-1]
47916    . blend[0,1] value
47917    smooth 5,0,1,0.5,2,10,0
47918    /. 255 ^. $14 *. 255
47919    a c c 0,255
47920  endl done
47921
47922#@gui Newton Fractal : fx_newton_fractal, fx_newton_fractal_preview
47923#@gui : X0 = value(-2)
47924#@gui : Y0 = value(-2)
47925#@gui : X1 = value(2)
47926#@gui : Y1 = value(2)
47927#@gui : note = note{"<span color="#EE5500"><b>Fractal Type:</b></span>"}
47928#@gui : Expression = choice(2,"Custom","z^^2 - 1","z^^3 - 1","z^^5 - 1","z^^6 + z^^3 - 1","z^^8 + 15*z^^4 - 1")
47929#@gui : p(z) = text{"rot(35°)*z^^3 - z^^2 + 1"}_1
47930#@gui : p'(z) = text{"3*z^^2 - 2*z"}_1
47931#@gui : p''(z) = text{"6*z - 2"}_1
47932#@gui : Descent method = choice(1,"Secant","Newton","Householder")
47933#@gui : Max iterations = int(200,16,1024)
47934#@gui : Precision = float(2,0,12)
47935#@gui : sep = separator()
47936#@gui : note = note{"<span color="#EE5500"><b>Rendering:</b></span>"}
47937#@gui : Coloring = choice(1,"By Custom Expression","By Iteration","By Value")
47938#
47939# Color by iteration
47940#
47941#@gui : Number of Colors = int(16,2,2048)
47942#@gui : Smoothness = int(8,1,256)
47943#@gui : Seed = int(255,0,65536)
47944#
47945# Color by value
47946#
47947#@gui : Colorspace = choice(2,"HSI","HSL","HSV")_0
47948#@gui : Hue min (%) = float(100,0,500)_0
47949#@gui : Hue max (%) = float(150,0,500)_0
47950#@gui : Lightness min (%) = float(20,0,500)_0
47951#@gui : Lightness max (%) = float(400,0,500)_0
47952#
47953# Custom coloring
47954#
47955#@gui : Colorspace = choice(3,"RGB,"HSI","HSL","HSV","Lab")_0
47956#@gui : Pre-Process = choice(2,"None","Equalize","Normalize","Equalize and Normalize")_0+
47957#@gui : note = note{"<small><span color="#EE5500"><b>Tips for Custom expressions:</b></span>\n
47958#@gui : - Variables <b>i0,i1</b> stand for the real and imaginary parts of the iterated complex number.\n
47959#@gui : - Variable <b>i2</b> is the number of iterations required for convergence.\n
47960#@gui : - Variable <b>z</b> is the complex number with value <b>[ i0,i1 ]</b>.\n
47961#@gui : - Functions <b>p(z), dp(z)</b> and <b>d2p(z)</b> are the expressions used for computing the fractal.
47962#@gui : </small>"}
47963#@gui : Channel #1 = text{"carg(-z)"}_0
47964#@gui : Channel #2 = text{"(i0 + i1)/2"}_0
47965#@gui : Channel #3 = text{"10*(i2^0.4)"}_0
47966#@gui : Post-Process = choice(0,"None","Equalize","Normalize","Equalize and Normalize")_0
47967#
47968# Basic color adujstment
47969#
47970#@gui : Brightness (%) = float(0,-100,100)
47971#@gui : Contrast (%) = float(0,-100,100)
47972#@gui : Gamma (%) = float(0,-100,100)
47973#@gui : Hue (%) = float(0,-100,100)
47974#@gui : Saturation (%) = float(0,-100,100)
47975#@gui : Equalization (%) = float(0,0,100)
47976#@gui : Anti-aliasing = choice(2,"x1","x1.5","x2","x2.5","x3","x3.5","4")
47977#@gui : note = note{"<small><b>Note:</b> Anti-aliasing is applied on final rendering only, not on preview.</small>"}
47978#@gui : antialias_note = value(0)_2-
47979#@gui : sep = separator()
47980#
47981# Navigation
47982#
47983#@gui : note = note{"<span color="#EE5500"><b>Navigation:</b></span>"}
47984#@gui : Zoom Center = point(50,50,0,0,255,255,255,200)
47985#@gui : Zoom Factor = float(0.5,0,1)
47986#@gui : Angle = float(0,-180,180)
47987#@gui : Zoom In = button()
47988#@gui : Center = button()
47989#@gui : Zoom Out = button()
47990#@gui : Reset View = button()
47991#@gui : Display Coordinates on Preview Window = bool(1)
47992#@gui : Preview subsampling = choice(2,"None","x1.5","x2","x2.5","x3","x3.5","x4")
47993#@gui : sep = separator()
47994#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/01/09</i>.</small>")
47995fx_newton_fractal : skip "${6=},${7=},${8=},${22=},${25=},${28=}"
47996  repeat $! l[$>]
47997    if !narg($_size) _size={max(w,h)} fi
47998    rm
47999    antialias={arg(1+$33,1,1.5,2,2.5,3,3.5,4)}
48000    {$antialias*[$_size,$_size]}
48001    if $5==1
48002      pz="z^^2 - 1" dpz="2*zn" d2pz="2"
48003    elif $5==2
48004      pz="z^^3 - 1" dpz="3*z^^2" d2pz="6*z"
48005    elif $5==3
48006      pz="z^^5 - 1" dpz="5*z^^4" d2pz="20*z^^3"
48007    elif $5==4
48008      pz="z^^6 + z^^3 - 1" dpz="6*z^^5 + 3*z^^2" d2pz="30*z^^4 + 6*z"
48009    elif $5==5
48010      pz="z^^8 + 15*z^^4 - 1" dpz="8*z^^7 + 60*z^^3" d2pz="56*z^^6 + 180*z^^2";
48011    else
48012      pz="$6" dpz="$7" d2pz="$8"
48013    fi
48014    if !narg($pz) pz="[1,0]" fi
48015    if !narg($dpz) dpz="[1,0]" fi
48016    if !narg($d2pz) d2pz="[1,0]" fi
48017
48018    newton_fractal ${1-4},$38,$9,$10,{10^-$11},$pz,$dpz,$d2pz
48019
48020    if $12==1 # Color by iteration
48021      channels 100%
48022      srand $15 $13,1,1,3 rand. 0,255 r. {$13*$14},1,1,3,3 point. 0 map.. .,3 rm.
48023
48024    elif $12==2 # Color by value
48025      f "[ atan2(i1,i0),1,i2 ]" s c n... {[$17,$18]*360%} n. {[$19,$20]%} c. 0,1 a c
48026      ${"arg 1+$16,hsi,hsl,hsv"}2rgb
48027
48028    else # Custom coloring
48029
48030      if $22 # Pre-process values
48031        s c,-2
48032        if $22&1 equalize 1024 fi
48033        if $22&2 /[-2] {-2,max(1e-5,abs(im),abs(iM))} n. 0,1 fi
48034        a c
48035      fi
48036
48037      f "*begin(
48038            p(z) = ("$pz");
48039            dp(z) = ("$dpz");
48040            d2p(z) = ("$d2pz");
48041          );
48042          z = [ i0,i1 ];
48043          [ (0;$23),(0;$24),(0;$25) ]"
48044
48045      if $26 # Post-process values
48046        s c
48047        if $26&1 equalize 1024 fi
48048        if $26&2 normalize 0,1 fi
48049        a c
48050      fi
48051
48052      * 255 mod 256
48053      if $21 ${"arg $21,hsi8,hsl8,hsv8,lab8"}2rgb fi # Convert to RGB colors
48054    fi
48055
48056    r2dx $_size
48057
48058    if $32 ac "+equalize 1024 j.. .,0,0,0,0,{$32%} rm.",ycbcr_y fi
48059    adjust_colors ${27-31},0,0,0,255
48060  endl done
48061
48062fx_newton_fractal_preview : skip "${6=},${7=},${8=},${22=},${25=},${28=}"
48063  is_custom_expression={$5==0?2:1}
48064  is_color_by_custom={$12==0?2:0}
48065  is_color_by_iter={$12==1?2:0}
48066  is_color_by_value={$12==2?2:0}
48067  _size0={min(${-gui_preview_wh})}
48068  _size={$_size0/arg(1+$44,1,1.5,2,2.5,3,3.5,4)}
48069
48070  angle=$38
48071  if "$39 || $40 || $41"
48072    x0,y0,x1,y1={"P0 = [${1,2}];
48073                  dP = [${3,4}] - P0;
48074                  M = P0 + 0.5*dP;
48075                  C = P0 + [${35,36}]%*dP;
48076                  C = M + rot(-$38°)*(C - M);
48077                  zfact = $37*($39?1:$40?0:-2);
48078                  dC = 0.5*dP*(1 - 0.98*zfact);
48079                  [ C - dC, C + dC ]"}
48080    px,py=50
48081  elif $42
48082    x0,y0,x1,y1=-2,-2,2,2
48083    px,py=50
48084    angle=0
48085  else
48086    x0,y0,x1,y1=${1-4}
48087    px,py=${35,36}
48088  fi
48089  fx_newton_fractal $x0,$y0,$x1,$y1,$5,"$6","$7","$8",${9-22},"$23","$24","$25",${26-32},0,${34-37},$angle,${39--1}
48090
48091  repeat $! l[$>]
48092    r2dx $_size0,1
48093    x0r,y0r,x1r,y1r={"C = [ "$px,$py" ]%*w; dC = 0.5*w*(1 - 0.98*$37); round([ C - dC, C + dC - 1 ])"}
48094    rectangle $x0r,$y0r,$x1r,$y1r,0.7,0xF0F0F0F0,255,255,255,255
48095    rectangle $x0r,$y0r,$x1r,$y1r,0.7,0x0F0F0F0F,0,0,0,255
48096    if $43 to "Z0 = ( "{_$x0}" , "{_$y0}" )\nZ1 = ( "{_$x1}" , "{_$y1}" )",2,2,16 fi
48097  endl done
48098
48099  u "{"$x0"}{"$y0"}{"$x1"}{"$y1"}{$5}"\
48100    "{$6}_"$is_custom_expression\
48101    "{$7}_"$is_custom_expression\
48102    "{$8}_"$is_custom_expression\
48103    "{$9}{$10}{$11}{$12}"\
48104    "{$13}_"$is_color_by_iter\
48105    "{$14}_"$is_color_by_iter\
48106    "{$15}_"$is_color_by_iter\
48107    "{$16}_"$is_color_by_value\
48108    "{$17}_"$is_color_by_value\
48109    "{$18}_"$is_color_by_value\
48110    "{$19}_"$is_color_by_value\
48111    "{$20}_"$is_color_by_value\
48112    "{$21}_"$is_color_by_custom\
48113    "{$22}_"$is_color_by_custom\
48114    "{$23}_"$is_color_by_custom\
48115    "{$24}_"$is_color_by_custom\
48116    "{$25}_"$is_color_by_custom\
48117    "{$26}_"$is_color_by_custom\
48118    "{$27}{$28}{$29}{$30}{$31}{$32}{$33}"\
48119    "{$34}"_{$33==0?0:2}\
48120    "{"$px,$py"}{$37}{"$angle"}{0}{0}{0}{0}{$43}{$44}"
48121
48122#@gui Plasma : fx_plasma, fx_plasma(0)
48123#@gui : Alpha = float(0.5,0,5)
48124#@gui : Beta = float(0,0,100)
48125#@gui : Scale = int(8,2,10)
48126#@gui : Randomize = bool(0)
48127#@gui : Transparency = bool(0)
48128#@gui : Color Balance = color(128,128,128)
48129#@gui : sep = separator()
48130#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/20/03</i>.</small>")
48131fx_plasma :  skip ${4=0},${5=0}
48132  if $5 to_rgba else to_rgb fi
48133  if $4 rand 0,255 fi
48134  plasma $1,$2,$3 n 0,255
48135  balance_gamma ${6-8}
48136
48137#@gui Quick Copyright : fx_quick_copyright, fx_quick_copyright(0)
48138#@gui : Text = text{"\\251 G'MIC"}
48139#@gui : Size = int(27,13,128)
48140#@gui : Color = color(255,255,255,128)
48141#@gui : Outline = int(1,0,4)
48142#@gui : Position = choice(3,"Up-Left","Up-Right","Bottom-Left","Bottom-Right")
48143#@gui : Offset = int(5,0,40)
48144#@gui : Orientation = choice(1,"-90 deg.","0 deg.","+90 deg.","+180 deg.")
48145#@gui : sep = separator()
48146#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48147fx_quick_copyright :
48148  i[0] 0 t[0] "$1",0,0,$2,1,$3,$4,$5 autocrop[0] 0 r[0] {{0,w}+2*$7},{{0,h}+2*$7},1,3,0,0,0.5,0.5
48149  i[1] 0 t[1] "$1",0,0,$2,1,1 autocrop[1] 0 r[1] {{1,w}+2*$7},{{1,h}+2*$7},1,1,0,0,0.5,0.5 dilate[1] {1+2*$7}
48150  rotate[0,1] {90*($10-1)}
48151  repeat $!-2
48152  if $8==0 j. [0],$9,$9,0,0,{$6/255},[1]
48153  elif $8==1 j. [0],{w-1-{0,w}-$9},$9,0,0,{$6/255},[1]
48154  elif $8==2 j. [0],$9,{h-1-{0,h}-$9},0,0,{$6/255},[1]
48155  else j. [0],{w-1-{0,w}-$9},{h-1-{0,h}-$9},0,0,{$6/255},[1]
48156  fi
48157  mv. 2 done
48158  rm[0,1]
48159
48160#@gui Rainbow : fx_rainbow, fx_rainbow
48161#@gui : Left Position = float(80,0,100)
48162#@gui : Right Position = float(80,0,100)
48163#@gui : Left Slope = float(175,0,400)
48164#@gui : Right Slope = float(175,0,400)
48165#@gui : Thinness = float(3,0.1,8)
48166#@gui : Opacity = float(80,0,199)
48167#@gui : sep = separator()
48168#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48169fx_rainbow :
48170  repeat $! l[$>]
48171    100%,100% spline. 0,$1%,100,{-$3}%,100%,$2%,100,$4%,1,1
48172    flood. 0,0,0,0,0,1,1 flood. {w-1},0,0,0,0,1,1
48173    distance. 0 c. 0,255 n. 0,{$5*255}
48174    palette rainbow +luminance. c. 0,{min(100,200-$6)}% n. 0,255 a[-2,-1] c
48175    map.. . rm.
48176    if $6<100 sh. 3 *. {$6/100} rm. fi
48177    blend alpha
48178  endl done
48179
48180#@gui Shade Bobs : fx_shadebobs, fx_shadebobs
48181#@gui : note = note("<small>Bobs parameters :</small>")
48182#@gui : Density = int(50,1,200)
48183#@gui : Radius = int(5,1,100)
48184#@gui : Duration = int(200,1,500)
48185#@gui : Velocity = float(1,0,10)
48186#@gui : sep = separator()
48187#@gui : note = note("<small>Curve parameters :</small>")
48188#@gui : Rx = float(-1,-3,3)
48189#@gui : Ry = float(2,-3,3)
48190#@gui : Rz = float(1,-3,3)
48191#@gui : Rt = float(0.8,-3,3)
48192#@gui : Rcx = float(0,-3,3)
48193#@gui : Colormap = choice(8,"Grayscale","Standard","HSV","Lines","Hot","Cool","Jet","Flag","Cube")
48194#@gui : sep = separator()
48195#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/18/04</i>.</small>")
48196fx_shadebobs :
48197  channels 0 f 0
48198  repeat $! l[$>]
48199    t=0
48200    repeat $3
48201      repeat $1
48202        r={$6+$5*cos(6*$7*$t)+(1-$5)*sin(6*$8*$t)}
48203        a={(360*sin($7*$t)+30*$6*$>)*pi/180}
48204        ax={2*$>*pi/$1+$t}
48205        cx={(1+$9*cos($ax)+$r*cos($a))*w/2}
48206        cy={(1+$9*sin($ax)+$r*sin($a))*h/2}
48207        ellipse. $cx,$cy,$2%,$2%,0,-1,1
48208      done
48209      t+={$4%}
48210    done
48211  endl done
48212  & 255 if $10 map {$10-1} fi
48213
48214#@gui Sine Curve : fx_sine_curve, fx_sine_curve_preview
48215#@gui : note = note("<span color="#EE5500"><b>Curve parameters:</b></span>")
48216#@gui : Preset = choice{1,"Default (Circle)","Alien Rasta","All Round","Carnivorous Plant","Cat Pad","Flower",
48217#@gui : "Flower Cushion","Fly Karateka","Hearts","Moving Leaf","Radioactive Flower","Rosace","Spaceship",
48218#@gui : "Transformer","Tubular Waves","Twisted Heart","Twisted Heart 2","Twisted Tunnel","Waterslide"}
48219#@gui : Previous Preset = value(-1)
48220#@gui : Resolution (%) = float(75,0,100)
48221#@gui : Periods = float(1,0,3)
48222#@gui : sep = separator()
48223#@gui : Parameter Settings = choice(1,"Ratios","Multipliers","Offsets","Exponents","Signs","3D Angles")
48224#@gui : note = note("<small><span color="#EE0055"><b>Ratios:</b></span></small>")
48225#@gui : Xa/Xb = float(0.5,0,1)_0-
48226#@gui : Ya/Yb = float(0.5,0,1)_0
48227#@gui : Za/Zb = float(0.5,0,1)_0
48228#@gui : note = note("<small><span color="#EE0055"><b>Multipliers:</b></span></small>")
48229#@gui : Xa-Multiplier = int(1,0,1024)_2-
48230#@gui : Ya-Multiplier = int(1,0,1024)_2
48231#@gui : Za-Multiplier = int(0,0,1024)_2
48232#@gui : Xb-Multiplier = int(800,0,1024)_2
48233#@gui : Yb-Multiplier = int(800,0,1024)_2
48234#@gui : Zb-Multiplier = int(1,0,1024)_2
48235#@gui : note = note("<small><span color="#EE0055"><b>Offsets:</b></span></small>")
48236#@gui : Xa-Offset (deg.) = float(90,0,360)_0-
48237#@gui : Ya-Offset (deg.) = float(0,0,360)_0
48238#@gui : Za-Offset (deg.) = float(0,0,360)_0
48239#@gui : Xb-Offset (deg.) = float(90,0,360)_0
48240#@gui : Yb-Offset (deg.) = float(0,0,360)_0
48241#@gui : Zb-Offset (deg.) = float(0,0,360)_0
48242#@gui : note = note("<small><span color="#EE0055"><b>Exponents:</b></span></small>")
48243#@gui : Xa-Exponent = float(1,0,32)_0-
48244#@gui : Ya-Exponent = float(1,0,32)_0
48245#@gui : Za-Exponent = float(1,0,32)_0
48246#@gui : Xb-Exponent = float(1,0,32)_0
48247#@gui : Yb-Exponent = float(1,0,32)_0
48248#@gui : Zb-Exponent = float(1,0,32)_0
48249#@gui : note = note("<small><span color="#EE0055"><b>Signs:</b></span></small>")
48250#@gui : Xa-Sign = choice("Preserve","Invert","Negative","Positive")_0-
48251#@gui : Ya-Sign = choice("Preserve","Invert","Negative","Positive")_0
48252#@gui : Za-Sign = choice("Preserve","Invert","Negative","Positive")_0
48253#@gui : Xb-Sign = choice("Preserve","Invert","Negative","Positive")_0
48254#@gui : Yb-Sign = choice("Preserve","Invert","Negative","Positive")_0
48255#@gui : Zb-Sign = choice("Preserve","Invert","Negative","Positive")_0
48256#@gui : note = note("<small><span color="#EE0055"><b>3D Angles:</b></span></small>")
48257#@gui : X-Angle (deg.) = float(0,-180,180)_0-
48258#@gui : Y-Angle (deg.) = float(0,-180,180)_0
48259#@gui : Z-Angle (deg.) = float(0,-180,180)_0
48260#@gui : Zoom = float(1,0,10)_0
48261#@gui : Focale = int(8,1,20)_0
48262#@gui : sep = separator()
48263#@gui : note = note("<span color="#EE5500"><b>Rendering parameters:</b></span>")
48264#@gui : Center = point(50,50,0,1,0,238,85,-170,10)_0
48265#@gui : Old X-Center = value(50)
48266#@gui : Old Y-Center = value(50)
48267#@gui : Radius = point(68,68,0,1,238,0,85,-170,10)_0
48268#@gui : Angle = point(75,50,0,1,238,85,0,-170,10)_0
48269#@gui : Old X-Angle = value(75)
48270#@gui : Old Y-Angle = value(50)
48271#@gui : Primary radius (%) = float(3,0,100)
48272#@gui : Secondary radius (%) = float(2,0,100)
48273#@gui : Opacity (%) = float(40,0,100)
48274#@gui : Color = color(255,255,255)
48275#@gui : Anti-aliasing = choice(2,"None","× 1.25","× 1.5","× 2","× 3")
48276#@gui : sep = separator()
48277#@gui : Preview background = choice(1,"Image","Black","White")
48278#@gui : sep = separator()
48279#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/03/04</i>.</small>")
48280fx_sine_curve :
48281
48282  # Get parameters as named variables.
48283  preset,previous_preset,\
48284  resolution,periods,dp,\
48285  ratx,raty,ratz,\
48286  mxa,mya,mza,mxb,myb,mzb,\
48287  oxa,oya,oza,oxb,oyb,ozb,\
48288  pxa,pya,pza,pxb,pyb,pzb,\
48289  sxa,sya,sza,sxb,syb,szb,\
48290  rotx,roty,rotz,zoom,focale,\
48291  xc,yc,prev_xc,prev_yc,xr,yr,xa,ya,prev_xa,prev_ya,\
48292  radius1,radius2,opacity,\
48293  colR,colG,colB,\
48294  antialiasing,\
48295  preview_background=$*
48296
48297  if !narg($_is_preview) _is_preview=0 fi
48298  if [$prev_xc,$prev_yc]!=[$xc,$yc]
48299    xr,yr,xa,ya,prev_xa,prev_ya+={d=[$xc,$yc]-[$prev_xc,$prev_yc];[d,d,d]}
48300  fi
48301  if [$prev_xa,$prev_ya]!=[$xa,$ya]
48302    delta_a={"
48303      a = [ "$xa" - "$xc", "$ya" - "$yc" ];
48304      b = [ "$prev_xa" - "$xc", "$prev_ya" - "$yc" ];
48305      (atan2(a[1],a[0]) - atan2(b[1],b[0]))*180/pi;
48306    "}
48307    xr,yr={[$xc,$yc]+rot($delta_a°)*[$xr-$xc,$yr-$yc]}
48308  else delta_a=0 fi
48309  if [$colR,$colG,$colB]==[0,0,0]" && "$preview_background==1 colR,colG,colB=255
48310  elif [$colR,$colG,$colB]==[255,255,255]" && "$preview_background==2 colR,colG,colB=0
48311  fi
48312
48313  # Manage presets.
48314  update_params=0
48315  if $preset!=$previous_preset
48316
48317    # Set default parameters for presets ('Default (Circle)').
48318    periods=1
48319    ratx,raty,ratz=0.5,0.5,0
48320    mxa,mxb,mya,myb,mza,mzb=1,1,1,1,0,1
48321    oxa,oxb,oya,oyb,oza,ozb=90,90,0,0,0,0
48322    pxa,pxb,pya,pyb,pza,pzb=1
48323    sxa,sxb,sya,syb,sza,szb=0
48324    rotx,roty,rotz,zoom,focale=0,0,0,2,8
48325
48326    # Set specific values for each preset.
48327
48328    # Default (circle)
48329    if $preset==0
48330      ratx,raty,ratz=0 zoom=1
48331    # Alien Rasta
48332    elif $preset==1
48333      mxa,mxb,mya,myb=1,800,1,800 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 oxa,oxb,oya,oyb=90,90,0,60
48334    # All Round
48335    elif $preset==2
48336      mxa,mxb,mya,myb=1,200,1,150 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48337    # Carnivorous Plant
48338    elif $preset==3
48339      mxa,mxb,mya,myb=9,512,1024,9 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48340    # Cat Pad
48341    elif $preset==4
48342      mxa,mxb,mya,myb=80,1,80,1 pxa,pxb,pya,pyb=1,3,1,3
48343    # Flower
48344    elif $preset==5
48345      ratz=0.8 mza,mzb=7,1024 pza,pzb=1.6,2 rotz=45 zoom=1 focale=4
48346    # Flower Cushion
48347    elif $preset==6
48348      mxa,mxb,mya,myb=80,1,1,80 pxa,pxb,pya,pyb=1,3,1,3
48349    # Fly Karateka
48350    elif $preset==7
48351      mxa,mxb,mya,myb=150,1,1,100 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48352    # Hearts
48353    elif $preset==8
48354      mxa,mxb,mya,myb=1,80,80,80 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48355    # Moving Leaf
48356    elif $preset==9
48357      mxa,mxb,mya,myb=2,200,200,1 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48358    # Radioactive Flower
48359    elif $preset==10
48360      mxa,mxb,mya,myb=1,800,1,800 pxa,pxb,pya,pyb=1,3,1,3
48361    # Rosace
48362    elif $preset==11
48363      mxa,mxb,mya,myb=1,10,1,10
48364    # Spaceship
48365    elif $preset==12
48366      mxa,mxb,mya,myb=1,400,1,200 pxa,pxb,pya,pyb=1,3,1,3 sxa,sxb,sya,syb=1,1,0,0
48367    # Transformer
48368    elif $preset==13
48369      mxa,mxb,mya,myb=1,800,800,2 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48370    # Tubular Waves
48371    elif $preset==14
48372      mxa,mxb,mya,myb=1,30,1,60 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48373    # Twisted Heart
48374    elif $preset==15
48375      mxa,mxb,mya,myb=500,1,1,500 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48376    # Twisted Heart 2
48377    elif $preset==16
48378      mxa,mxb,mya,myb=1,80,80,1 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48379    # Twisted Tunnel
48380    elif $preset==17
48381      mxa,mxb,mya,myb=1,80,1,40 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48382    # Waterslide
48383    elif $preset==18
48384      ratx=0.6 mxa,mxb,mya,myb=9,400,200,9 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
48385    fi
48386  fi
48387
48388  # Change unit for some variables.
48389  W,H={w#0?[w#0,h#0]:$_is_preview" && "0$_preview_width?[0$_preview_width,0$_preview_height]:[1024,1024]}
48390  nresolution={max(1,round($periods*1000000*($resolution%)^2))}
48391  nfocale={arg($focale,0.05,0.1,0.2,0.3,0.4,0.5,0.75,1,1.25,1.5,1.75,2,3,4,8,16,32,128,1024,16384)}
48392  noxa,noxb,noya,noyb,noza,nozb={[$oxa,$oxb,$oya,$oyb,$oza,$ozb]*pi/180}
48393  nantialiasing={arg0($antialiasing,1,1.25,1.5,2,3)}
48394  rW,rH={[$W,$H]*$nantialiasing} # Size of the non-antialiased rendering
48395
48396  # Generate and render curve.
48397  l[]
48398
48399    # Compute curve coordinates.
48400    $nresolution,1,1,2,"*
48401      begin(
48402        const is_rot = "$rotx" || "$roty" || "$rotz";
48403        ref(rot(1,0,0,"$rotx"°),Rx);
48404        ref(rot(0,1,0,"$roty"°),Ry);
48405        ref(rot(0,0,1,"$rotz"°),Rz);
48406        R = mul(Rz,mul(Ry,Rx,3),3); # 3D rotation
48407
48408        # Variables to manage 2D rotation.
48409        const ang = atan2("$ya" - "$yc","$xa" - "$xc");
48410        const cosa = cos(ang);
48411        const sina = sin(ang);
48412
48413        # Variables to manage aspect ratio.
48414        const c = 35;
48415        const dxr0 = "$xr" - "$xc";
48416        const dyr0 = "$yr" - "$yc";
48417        const dxr = cosa*dxr0 + sina*dyr0;
48418        const dyr = -sina*dxr0 + cosa*dyr0;
48419        const _dx = abs(dxr)/c; const dx = 2.5*c*(_dx<1?_dx:_dx^3);
48420        const _dy = abs(dyr)/c; const dy = 2.5*c*(_dy<1?_dy:_dy^3);
48421      );
48422
48423      cpow(x,p,s) = (
48424        ref(x,_x);
48425        (!s?sign(_x):s==1?-sign(_x):s==2?-1:1)*abs(_x)^p
48426      );
48427
48428      t = x/w*2*pi*"$periods";
48429      X = lerp(cpow(sin("$mxa"*t + "$noxa"),"$pxa","$sxa"),
48430               cpow(sin("$mxb"*t + "$noxb"),"$pxb","$sxb"),
48431               "$ratx");
48432      Y = lerp(cpow(sin("$mya"*t + "$noya"),"$pya","$sya"),
48433               cpow(sin("$myb"*t + "$noyb"),"$pyb","$syb"),
48434               "$raty");
48435      Z = lerp(cpow(sin("$mza"*t + "$noza"),"$pza","$sza"),
48436               cpow(sin("$mzb"*t + "$nozb"),"$pzb","$szb"),
48437               "$ratz");
48438
48439      # Set aspect ratio and rotate.
48440      X*=dx%;
48441      Y*=dy%;
48442      is_rot?(P = R*[ X,Y,Z ]; X = P[0]; Y = P[1]; Z = P[2]);
48443
48444      # 2D projection.
48445      X*="$nfocale";
48446      Y*="$nfocale";
48447      Z = max(1e-5,Z + 1 + "$nfocale");
48448      pX = X/Z;
48449      pY = -Y/Z;
48450
48451      # Normalize and get display coordinates.
48452      ang?(X = cosa*pX - sina*pY; pY = sina*pX + cosa*pY; pX = X);
48453
48454      const ax = "$zoom*$rW"; const bx = "$xc*$rW"%;
48455      const ay = "$zoom*$rH"; const by = "$yc*$rH"%;
48456      [ ax*pX + bx, ay*pY + by ]"
48457
48458    # Draw curve (as an alpha-channel).
48459    $rW,$rH
48460    eval.. "*
48461      const mwh = min(w#-1,h#-1)*5%;
48462      const r1 = max(0.01,mwh*"$radius1"%);
48463      const r2 = max(0.01,mwh*"$radius2"%);
48464      const Mr = max(r1,r2);
48465      const opacity = ("$opacity"%)^3;
48466
48467      X = R; Y = G;
48468
48469      Mr<0?(
48470        i(#-1,X,Y) = lerp(i(#-1,X,Y),1,opacity);
48471      ):(
48472        pX = i(x - 1,0,0,0);
48473        pY = i(x - 1,0,0,1);
48474        dX = X - pX;
48475        dY = Y - pY;
48476        ang = atan2(dY,dX)*180/pi;
48477        ellipse(#-1,X,Y,r1,r2,ang°,opacity,255);
48478      );
48479      I"
48480    rm..
48481    r. $W,$H,1,1,2 n 0,255
48482    i[0] 100%,100%,1,3 fc[0] $colR,$colG,$colB
48483    a[-2,-1] c
48484  endl
48485
48486  if $_is_preview
48487    if $!==1 i[0] $W,$H,1,3 fi
48488    if $preview_background [0],[0] f. {$preview_background==1?0:255} to_rgb. rv[0,-1] rm. fi
48489    blend[0,-1] alpha
48490    line. $xc%,$yc%,$xr%,$yr%,0.75,0xF0F0F0F0,238,0,85
48491    line. $xc%,$yc%,$xr%,$yr%,0.75,0x0F0F0F0F,255
48492    line. $xc%,$yc%,$xa%,$ya%,0.75,0xF0F0F0F0,238,85,0
48493    line. $xc%,$yc%,$xa%,$ya%,0.75,0x0F0F0F0F,0
48494  fi
48495  mv. 0
48496  if $_output_mode k[0] fi
48497
48498
48499  # Update parameter values.
48500  r,m,o,p,s,a={d=$dp;[d==0?2:0,d==1?2:0,d==2?2:0,d==3?2:0,d==4?2:0,d==5?2:0]}
48501  u "{"$preset"}{"$preset"}{"$resolution"}{"$periods"}{"$dp"}"\
48502    "{"$ratx"}_"$r"{"$raty"}_"$r"{"$ratz"}_"$r\
48503    "{"$mxa"}_"$m"{"$mya"}_"$m"{"$mza"}_"$m"{"$mxb"}_"$m"{"$myb"}_"$m"{"$mzb"}_"$m\
48504    "{"$oxa"}_"$o"{"$oya"}_"$o"{"$oza"}_"$o"{"$oxb"}_"$o"{"$oyb"}_"$o"{"$ozb"}_"$o\
48505    "{"$pxa"}_"$p"{"$pya"}_"$p"{"$pza"}_"$p"{"$pxb"}_"$p"{"$pyb"}_"$p"{"$pzb"}_"$p\
48506    "{"$sxa"}_"$s"{"$sya"}_"$s"{"$sza"}_"$s"{"$sxb"}_"$s"{"$syb"}_"$s"{"$szb"}_"$s\
48507    "{"$rotx"}_"$a"{"$roty"}_"$a"{"$rotz"}_"$a"{"$zoom"}_"$a"{"$focale"}_"$a\
48508    "{"$xc,$yc"}{"$xc"}{"$yc"}{"$xr,$yr"}{"$xa,$ya"}{"$xa"}{"$ya"}{"$radius1"}{"$radius2"}{"$opacity"}"\
48509    "{"$colR,$colG,$colB"}{"$antialiasing"}"\
48510    "{"$preview_background"}"
48511
48512fx_sine_curve_preview :
48513  _is_preview=1
48514  fx_sine_curve $*
48515  k[0]
48516
48517#@gui Superformula : fx_superformula, fx_superformula(1)
48518#@gui : Resolution = int(4096,2,8192)
48519#@gui : sep = separator()
48520#@gui : X-Size = float(0.9,0,2)
48521#@gui : Y-Size = float(0.9,0,2)
48522#@gui : sep = separator()
48523#@gui : M = int(8,1,32)
48524#@gui : N1 = float(1,-32,32)
48525#@gui : N2 = float(5,-32,32)
48526#@gui : N3 = float(8,-32,32)
48527#@gui : sep = separator()
48528#@gui : X-Angle = float(0,0,360)
48529#@gui : Y-Angle = float(0,0,360)
48530#@gui : Z-Angle = float(0,0,360)
48531#@gui : sep = separator()
48532#@gui : Thickness = float(3,0,50)
48533#@gui : Color = color(128,255,128,255)
48534#@gui : sep = separator()
48535#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/04</i>.</small>")
48536fx_superformula :
48537  repeat $! l[$>] to_rgba
48538    {w},{h}
48539    f3d {0.5*max(w,h)/tan($4*pi/360)}
48540    superformula3d $1,${4-7}
48541    r3d. 0,0,1,$10 r3d. 0,1,0,$9 r3d. 1,0,0,$8
48542    *3d. {0.5*$2*{-2,w}},{0.5*$3*{-2,h}}
48543    col3d. 1 j3d.. .,50%,50%,0,1,1,0,0 rm.
48544    distance. 1 >. $11% *.. . ==. 0
48545    r. 100%,100%,1,4
48546    sh. 0 *. $12 rm.
48547    sh. 1 *. $13 rm.
48548    sh. 2 *. $14 rm.
48549    sh. 3 *. $15 rm.
48550    +[-2,-1]
48551  endl done
48552
48553#@gui Symmetric 2D Shape : fx_symmetric_shape2d, fx_symmetric_shape2d_preview(1)
48554#@gui : Subdivisions = int(5,2,32)
48555#@gui : Center = point(50,50,0,1,255,255,255,128)
48556#@gui : Old Center = value(50,50)
48557#@gui : Angle / Size = point(50,30,0,1,255,255,255,128)
48558#@gui : Old Angle / Size = value(50,30)
48559#@gui : sep = separator()
48560#@gui : Control Point 1 = point(50,25,1,1,255,128,0,255,4)
48561#@gui : Control Point 2 = point(56,42,1,1,255,128,0,255,4)
48562#@gui : Control Point 3 = point(52,52,-1,1,255,128,0,255,4)
48563#@gui : Control Point 4 = point(52,52,-1,1,255,128,0,255,4)
48564#@gui : Control Point 5 = point(52,52,-1,1,255,128,0,255,4)
48565#@gui : Control Point 6 = point(52,52,-1,1,255,128,0,255,4)
48566#@gui : sep = separator()
48567#@gui : Drawing Mode = choice(1,"Outlined","Filled")
48568#@gui : Color = color(255,0,255)
48569#@gui : Opacity (%) = float(100,0,100)
48570#@gui : sep = separator()
48571#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/06/17</i>.</small>")
48572fx_symmetric_shape2d :
48573  if !narg($_is_preview) _is_preview=0 fi
48574  repeat $! l[$<]
48575    100%,100%,1,4 _fx_symmetric_shape2d. $* rv
48576    if !$_is_preview" && "$_output_mode rm. fi
48577  endl done
48578
48579_fx_symmetric_shape2d :
48580  (${10-21}) f. "isnan(i)?-1024:i" discard. -1024 r. 2,{h/2},1,1,-1 permute. cyzx
48581  f. "x = R - $2; y = G - $3; [ atan2(y,x), norm(x,y) ]"
48582  l. n={h} .x{$1-1} a y f "const pi2 = 2*pi; [ (R + int(y/"$n")*pi2/$1)%pi2, G ]" sort +,y endl
48583  f. "[ $2*w#-2,$3*h#-2 ]/100 + [ G*cos(R), G*sin(R) ]*(min(w#-2,h#-2)-1)%" permute. cyzx
48584  coords={^} rm.
48585  if $22 polygon {narg($coords)/2},$coords,1,${23-25},{$26*255%}
48586  else polygon {narg($coords)/2},$coords,1,0xFFFFFFFF,${23-25},{$26*255%}
48587  fi
48588
48589fx_symmetric_shape2d_preview :
48590  _fx_symmetric_shape2d_preview $*
48591
48592_fx_symmetric_shape2d_preview :
48593  _is_preview=1
48594  cx1,cy1,cx2,cy2,cx3,cy3,cx4,cy4,cx5,cy5,cx6,cy6=${10-21}
48595  angx,angy=$6,$7
48596  if [$2,$3]!=[$4,$5]" || "[$angx,$angy]!=[$8,$9] # Center or angle has been modified
48597    dx,dy={[$2-$4,$3-$5]}
48598    repeat 6 i={1+$>}
48599      cx$i,cy$i={"
48600        const cx = "${cx$i}";
48601        const cy = "${cy$i}";
48602        dang = atan2($7 - $3,$6 - $2) - atan2($9 - $3,$8 - $2);
48603        dsca = norm($6 - $2,$7 - $3)/norm($9 - $3,$8 - $2);
48604        [ $2,$3 ] + dsca*rot(dang)*[ cx - $4, cy - $5 ]"}
48605    done
48606    angx,angy={[$6+$2-$4,$7+$3-$5]}
48607  fi
48608
48609  repeat $! l[$>]
48610    r {s=min(w,h);[s,s]},1,100%,0,0,0.5,0.5
48611    fx_symmetric_shape2d ${1-9},$cx1,$cy1,$cx2,$cy2,$cx3,$cy3,$cx4,$cy4,$cx5,$cy5,$cx6,$cy6,${22-26}
48612    rv blend alpha
48613    eval "
48614      t0 = atan2($7 - $3,$6 - $2);
48615      repeat ($1,k,
48616        const pi2 = 2*pi;
48617        const xc = $2*(w-1)%;
48618        const yc = $3*(h-1)%;
48619        x = xc + (w+h)*cos(t0 + 2*pi*k/$1);
48620        y = yc + (w+h)*sin(t0 + 2*pi*k/$1);
48621        polygon(-2,xc,yc,x,y,0.35,0xF0F0F0F0,255);
48622        polygon(-2,xc,yc,x,y,0.35,0x0F0F0F0F,0);
48623      )"
48624  endl done
48625
48626  u "{$1}"\      # Subdivisions
48627    "{$2,$3}"\   # Center
48628    "{$2,$3}"\   # Old Center
48629    "{"$angx,$angy"}"\   # Angle
48630    "{"$angx,$angy"}"\   # Old Angle
48631    "{"$cx1,$cy1"}"\ # Control point 1
48632    "{"$cx2,$cy2"}"\ # Control point 2
48633    "{"$cx3,$cy3"}"\ # Control point 3
48634    "{"$cx4,$cy4"}"\ # Control point 4
48635    "{"$cx5,$cy5"}"\ # Control point 5
48636    "{"$cx6,$cy6"}"\ # Control point 6
48637    "{$22}"\     # Drawing mode
48638    "{$23,$24,$25}"\ # Color
48639    "{$26}"      # Opacity
48640
48641#@gui Tree : fx_tree, fx_tree_preview(1)
48642#@gui : note = note("<small><b><span color="#EE5500">Global parameters:</span></b></small>")
48643#@gui : Recursion Depth = int(11,1,18)
48644#@gui : Random Seed = int(10000,0,65535)
48645#@gui : X-ratio = float(0,-1,1)
48646#@gui : Y-ratio = float(0,-1,1)
48647#@gui : note = note("<small><b><span color="#FF0055">Note:</span></b> Set <i>Random Seed</i> to <b>0</b> to make it \
48648# random as well.</small>")
48649#@gui : sep = separator(), note = note("<small><b><span color="#EE5500">Trunk:</span></b></small>")
48650#@gui : Thickness (%) = float(15,0,100)
48651#@gui : Base Thickness (%) = float(150,0,300)
48652#@gui : Angle (deg.) = float(0,-90,90)
48653#@gui : sep = separator(), note = note("<small><b><span color="#EE5500">Recursion:</span></b></small>")
48654#@gui : Avg Branching = float(2.15,1,6)
48655#@gui : Std Branching = float(0.8,0,6)
48656#@gui : Avg Left Angle (deg.) = float(-40,-90,90)
48657#@gui : Avg Right Angle (deg.) = float(40,-90,90)
48658#@gui : Std Angle (deg.) = float(10,0,90)
48659#@gui : Avg Length Factor (%) = float(75,0,200)
48660#@gui : Std Length Factor (%) = float(0,0,200)
48661#@gui : Avg Thickness Factor (%) = float(70,0,200)
48662#@gui : Std Thickness Factor (%) = float(20,0,200)
48663#@gui : sep = separator(), note = note("<small><b><span color="#EE5500">Colors / Opacity:</span></b></small>")
48664#@gui : Trunk color = color(40,25,0,255)
48665#@gui : Trunk opacity (%) = float(100,0,100)
48666#@gui : Leaf color = color(70,140,60,255)
48667#@gui : Leaf opacity (%) = float(100,0,100)
48668#@gui : Color gamma = float(0.4,-2,2)
48669#@gui : Opacity gamma = float(0.4,-2,2)
48670#@gui : sep = separator()
48671#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/03/24</i>.</small>")
48672_fx_tree:
48673  recursion_depth,random_seed,xratio,yratio,\
48674  trunk_thickness,base_thickness,trunk_angle,\
48675  avg_branching,std_branching,avg_leftangle,avg_rightangle,std_angle,avg_length,std_length,avg_thickness,std_thickness,\
48676  Rt,Gt,Bt,At,Ot,Rl,Gl,Bl,Al,Ol,gammaRGBA,gammaO=${1-28}
48677
48678  W,H,S={[w,h,min(w,h)]} l[]
48679    if $2 srand $2 fi
48680
48681    # Init trunk.
48682    1,1,1,9,"
48683      const h_thickness = "$trunk_thickness"%/2;
48684      const hb_thickness = h_thickness*"$base_thickness"%;
48685      R = rot("$trunk_angle"°);
48686      C = [ 0.5,0 ];
48687      P0 = C + R*[ -hb_thickness,0 ];
48688      P1 = C + R*[ hb_thickness,0 ];
48689      P2 = C + R*[ h_thickness,0.5 ];
48690      P3 = C + R*[ -h_thickness,0.5 ];
48691      [ P0,P1,P2,P3,0 ]"
48692
48693    # Compute tree geometry.
48694    repeat $recursion_depth
48695      1,8,1,9
48696      eval.. >${-math_lib}"
48697        const dangle = "$avg_rightangle" - "$avg_leftangle";
48698        ref(I,val);
48699        ref(val[0,2],P0);
48700        ref(val[2,2],P1);
48701        ref(val[4,2],P2);
48702        ref(val[6,2],P3);
48703        ndepth = val[8] + 1;
48704
48705        # Median axis.
48706        A = (P0 + P1)/2;
48707        B = (P2 + P3)/2;
48708        AB = B - A;
48709        thickness = norm(B - P2);
48710
48711        N = round(cut("$avg_branching" + u(-1,1)*"$std_branching",1,6));
48712        Nm1 = N<=1?1:N - 1;
48713
48714        repeat (N,n,
48715          ang = cut("$avg_leftangle" + dangle*n/Nm1 + u(-1,1)*"$std_angle",-90,90);
48716          len = cut("$avg_length" + u(-1,-1)*"$std_length",0,200);
48717          rot = rot(ang°);
48718          nB = B + len%*rot*AB;
48719          orth = (nB - B);
48720          orth/=norm(orth);
48721          orth = [ -orth[1],orth[0] ];
48722          nthickness = thickness*("$avg_thickness" + u(-1,1)*"$std_thickness")%;
48723          Q0 = nB + nthickness*orth;
48724          Q1 = nB - nthickness*orth;
48725          dar_insert(#-1,[ P3,P2,Q1,Q0,ndepth ]);
48726        );
48727        end(resize(#-1,1,dar_size(#-1),1,s#-1,0)); val"
48728    done
48729    a y
48730
48731    # Normalize coordinates to fit image size and aspect ratio.
48732    1,100%,1,4,"[ i0#-1,i2#-1,i4#-1,i6#-1 ]"
48733    1,100%,1,4,"[ i1#-2,i3#-2,i5#-2,i7#-2 ]"
48734    sx,sy={10^[$xratio,$yratio]}
48735    -.. 0.5 *.. {-2,$S*$sx/(2.1*max(abs(iM),abs(im)))} +.. {$W/2} *. {$S*$sy/(1.05*max(iM))}
48736    f... "round([ i0#-2,i0#-1,i1#-2,i1#-1,i2#-2,i2#-1,i3#-2,i3#-1,i8 ])"
48737    rm[-2,-1]
48738
48739    # Make sure trunk starts at bottom when rotated.
48740    eval "ref(I,T); P0 = T[0,2]; P1 = T[2,2]; P2 = T[4,2]; P3 = T[6,2];
48741      I[0] = [ lerp(P0,P2,-2),lerp(P1,P2,-2),P2,P3,0 ]"
48742
48743    # Draw tree.
48744    $W,$H,1,4
48745    eval.. ">
48746      begin(
48747        RGBAt = [ "$Rt,$Gt,$Bt,$At" ];
48748        RGBAl = [ "$Rl,$Gl,$Bl,$Al" ];
48749        const gRGBA = 10^"$gammaRGBA";
48750        const gO = 10^"$gammaO";
48751      );
48752      ref(I,val);
48753      t = val[8]/"$recursion_depth"; # Between [0,1]
48754      RGBA = lerp(RGBAt,RGBAl,t^gRGBA);
48755      O = lerp("$Ot,$Ol",t^gO)%;
48756      polygon(#-1,4,val[0,8],O,RGBA);
48757      (i0==i2 && i1==i3) || (i4==i6 && i5==i7)?polygon(#-1,2,i0,i1,i4,i5,O,RGBA);
48758      val"
48759    mirror xy
48760    rm..
48761  endl
48762
48763fx_tree :
48764  _fx_tree. $* mv. 0
48765  if $_output_mode k[0] fi
48766
48767fx_tree_preview :
48768  repeat $! l[$>] _fx_tree $* blend alpha endl done
48769
48770#@gui Turbulence : fx_turbulence, fx_turbulence
48771#@gui : Radius = float(128,1,1024)
48772#@gui : Octaves = int(6,1,12)
48773#@gui : Damping per Octave = float(4,1,10)
48774#@gui : Difference Mixing = float(0,-10,10)
48775#@gui : Mode = choice("Turbulence","Turbulence 2","Fractal Noise","Fractured Clouds","Stardust","Pea Soup")
48776#@gui : sep = separator()
48777#@gui : note = note("<small>Author: <i>Preben Soeberg</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48778fx_turbulence :
48779  remove_opacity turbulence ${^0}
48780
48781
48782#@gui ____<b>Sequences</b>
48783#-------------------------
48784
48785# fx_animate_preview : _command,_parameters1,_parameters2,_compute_half={ 0 | 1 },_width>=0,_height>=0
48786# Generate a preview with start/end rendering of an animation.
48787fx_animate_preview : skip ${4=1},${5=0},${6=$5}
48788  repeat $!
48789    if $5 width=$5 else width={w} fi
48790    if $6 height=$6 else height={h} fi
48791    if $4 s. x,2 else . fi
48792    -$1.. $2 -$1. $3   # Assume this is a 1->1 filter.
48793    r[-2,-1] {max(w,{-2,w})},{max(h,{-2,h})},1,100%,3
48794    if !$4 columns.. 0,50% columns. 50%,100% fi
48795    a[-2,-1] x r. $width,$height,1,100%,2 drgba.
48796    line. 50%,0,50%,100%,1,0,0,0,255
48797    to. "Start",3,-1,13,2,1,255 to. "End",{w-24},{h-18},13,2,1,255
48798  mv. 0 done
48799
48800#@gui 3D Elevation [Animated] : fx_animate_elevation3d, fx_animate_elevation3d_preview(1)
48801#@gui : Frames = _int(10,2,100)
48802#@gui : Output as Frames = _bool(1)
48803#@gui : Output as Files = _bool(0)
48804#@gui : Output Folder = _folder()
48805#@gui : note = note{"\n<b>Global parameters :</b>"}
48806#@gui : Factor = float(100,-1000,1000)
48807#@gui : Smoothness = float(1,0,10)
48808#@gui : Width = _int(1024,8,4096)
48809#@gui : Height = _int(1024,8,4096)
48810#@gui : Rendering = choice(2,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
48811#@gui : note = note{"\n<b>Starting parameters :</b>"}
48812#@gui : Size = float(0.8,0,3)
48813#@gui : X-Angle = float(35,0,360)
48814#@gui : Y-Angle = float(0,0,360)
48815#@gui : Z-Angle = float(0,0,360)
48816#@gui : FOV = float(45,1,90)
48817#@gui : X-Light = float(0,-100,100)
48818#@gui : Y-Light = float(0,-100,100)
48819#@gui : Z-Light = float(-100,-100,0)
48820#@gui : Specular Lightness = float(0.5,0,1)
48821#@gui : Specular Shininess = float(0.7,0,3)
48822#@gui : note = note{"\n<b>Ending parameters :</b>"}
48823#@gui : Size = float(0.8,0,3)
48824#@gui : X-Angle = float(35,0,1440)
48825#@gui : Y-Angle = float(0,0,1440)
48826#@gui : Z-Angle = float(360,0,1440)
48827#@gui : FOV = float(45,1,90)
48828#@gui : X-Light = float(0,-100,100)
48829#@gui : Y-Light = float(0,-100,100)
48830#@gui : Z-Light = float(-100,-100,0)
48831#@gui : Specular Lightness = float(0.5,0,1)
48832#@gui : Specular Shininess = float(0.7,0,3)
48833#@gui : sep = separator()
48834#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48835fx_animate_elevation3d : skip "${4=}"
48836  if $3 filename="$4/gmic_elevation3d.png" else filename="" fi
48837  _fx_elevation3d ${5-6}
48838  animate fx_render3d,"${7-8},${10-19},$9",\
48839                      "${7-8},${20-29},$9",$1,$2,{``$filename}
48840
48841fx_animate_elevation3d_preview : skip "${4=}"
48842  w={w} h={h}
48843  _fx_elevation3d ${5-6}
48844  fx_animate_preview fx_render3d,$w","$h",${10-19},$9",\
48845                                      $w","$h",${20-29},$9",0,$w,$h
48846
48847#@gui 3D Extrusion [Animated] : fx_animate_extrude3d, fx_animate_extrude3d_preview(1)
48848#@gui : Frames = _int(10,2,100)
48849#@gui : Output as Frames = _bool(1)
48850#@gui : Output as Files = _bool(0)
48851#@gui : Output Folder = _folder()
48852#@gui : note = note{"\n<b>Global parameters :</b>"}
48853#@gui : Depth = float(10,1,256)
48854#@gui : Resolution = int(512,1,1024)
48855#@gui : Smoothness = float(0.6,0,3)
48856#@gui : Width = _int(1024,8,4096)
48857#@gui : Height = _int(1024,8,4096)
48858#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
48859#@gui : note = note{"\n<b>Starting parameters :</b>"}
48860#@gui : Size = float(0.8,0,3)
48861#@gui : X-Angle = float(35,0,360)
48862#@gui : Y-Angle = float(0,0,360)
48863#@gui : Z-Angle = float(0,0,360)
48864#@gui : FOV = float(45,1,90)
48865#@gui : X-Light = float(0,-100,100)
48866#@gui : Y-Light = float(0,-100,100)
48867#@gui : Z-Light = float(-100,-100,0)
48868#@gui : Specular Lightness = float(0.5,0,1)
48869#@gui : Specular Shininess = float(0.7,0,3)
48870#@gui : note = note{"\n<b>Ending parameters :</b>"}
48871#@gui : Size = float(0.8,0,3)
48872#@gui : X-Angle = float(35,0,1440)
48873#@gui : Y-Angle = float(360,0,1440)
48874#@gui : Z-Angle = float(0,0,1440)
48875#@gui : FOV = float(45,1,90)
48876#@gui : X-Light = float(0,-100,100)
48877#@gui : Y-Light = float(0,-100,100)
48878#@gui : Z-Light = float(-100,-100,0)
48879#@gui : Specular Lightness = float(0.5,0,1)
48880#@gui : Specular Shininess = float(0.7,0,3)
48881#@gui : sep = separator()
48882#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48883fx_animate_extrude3d : skip "${4=}"
48884  if $3 filename="$4/gmic_extrude3d.png" else filename="" fi
48885  _fx_extrude3d ${5-7}
48886  animate fx_render3d,"${8-9},${11-20},$10",\
48887                      "${8-9},${21-30},$10",$1,$2,{``$filename}
48888
48889fx_animate_extrude3d_preview : skip "${4=}"
48890  w={w} h={h}
48891  _fx_extrude3d ${5-7}
48892  fx_animate_preview fx_render3d,$w","$h",${11-20},$10",\
48893                                      $w","$h",${21-30},$10",0,$w,$h
48894
48895#@gui 3D Image Object [Animated] : fx_animate_imageobject3d, fx_animate_imageobject3d_preview(1)
48896#@gui : Frames = _int(10,2,100)
48897#@gui : Output as Frames = _bool(1)
48898#@gui : Output as Files = _bool(0)
48899#@gui : Output Folder = _folder()
48900#@gui : note = note{"\n<b>Global parameters :</b>"}
48901#@gui : Type = choice{1,"Plane","Cube","Pyramid","Sphere","Torus","Gyroid","Weird","Cup","Rubik"}
48902#@gui : Width = _int(1024,1,4096)
48903#@gui : Height = _int(1024,1,4096)
48904#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
48905#@gui : note = note{"\n<b>Starting parameters :</b>"}
48906#@gui : Size = float(0.5,0,3)
48907#@gui : X-Angle = float(57,0,360)
48908#@gui : Y-Angle = float(41,0,360)
48909#@gui : Z-Angle = float(21,0,360)
48910#@gui : FOV = float(45,1,90)
48911#@gui : X-Light = float(0,-100,100)
48912#@gui : Y-Light = float(0,-100,100)
48913#@gui : Z-Light = float(-100,-100,0)
48914#@gui : Specular Lightness = float(0.5,0,1)
48915#@gui : Specular Shininess = float(0.7,0,3)
48916#@gui : note = note{"\n<b>Ending parameters :</b>"}
48917#@gui : Size = float(0.5,0,3)
48918#@gui : X-Angle = float(57,0,1440)
48919#@gui : Y-Angle = float(401,0,1440)
48920#@gui : Z-Angle = float(21,0,1440)
48921#@gui : FOV = float(45,1,90)
48922#@gui : X-Light = float(0,-100,100)
48923#@gui : Y-Light = float(0,-100,100)
48924#@gui : Z-Light = float(-100,-100,0)
48925#@gui : Specular Lightness = float(0.5,0,1)
48926#@gui : Specular Shininess = float(0.7,0,3)
48927#@gui : sep = separator()
48928#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48929fx_animate_imageobject3d : skip "${4=}"
48930  if $3 filename="$4/gmic_imageobject3d.png" else filename="" fi
48931  _fx_imageobject3d "_",$5
48932  animate fx_render3d,"${6-7},${9-18},$8",\
48933                      "${6-7},${19-28},$8",$1,$2,{``$filename}
48934
48935fx_animate_imageobject3d_preview : skip "${4=}"
48936  w={w} h={h}
48937  _fx_imageobject3d "_preview_",$5
48938  fx_animate_preview fx_render3d,$w","$h",${9-18},$8",\
48939                                      $w","$h",${19-28},$8",0,$w,$h
48940
48941#@gui 3D Text Pointcloud : fx_text_pointcloud3d, fx_text_pointcloud3d_preview
48942#@gui : Frames = _int(64,1,256)
48943#@gui : 1st Text = text("G'MIC")
48944#@gui : 2nd Text = text("Rocks!")
48945#@gui : Smoothness = float(1,0,5)
48946#@gui : Color = color(200,220,255)
48947#@gui : Background = color(255,255,255,255)
48948#@gui : X-Shadow= float(2,0,10)
48949#@gui : Y-Shadow= float(2,0,10)
48950#@gui : Shadow Smoothness = float(1,0,5)
48951#@gui : Stationary Frames = _int(19,1,32)
48952#@gui : sep = separator()
48953#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/01/09</i>.</small>")
48954fx_text_pointcloud3d :
48955  W={w} H={h} M={round(1.5*max(w,h))} rm
48956  text_pointcloud3d "$2","$3",$4
48957  col3d. ${5-7} *3d. {0.7*$M}
48958  f3d 4000 db3d 0 m3d
48959  repeat $1
48960    rprogress {60*$>/$1}
48961    angle={$>*360/$1}
48962    +r3d[0] 1,0,1,$angle
48963    $M,$M,1,3,-1 j3d. ..,50%,50%,0,1 rm..
48964  done
48965  rm[0] a z autocrop -1 to_rgba s z replace_color 0,0,-1,-1,-1,255,0,0,0,0
48966  if $11 N=$! repeat $! l[$>] rprogress {60+40*$>/$N}
48967    i[0] 100%,100%,1,4 fc[0] ${8-11} +channels. 3,3 +negate. b[-2,-1] $14% to_rgba.
48968    j[0] .,$12%,$13%,0,0,1,..,255 rm[-2,-1] blend alpha
48969  endl done fi
48970  if $W>$H r2dx $W else r2dy $H fi
48971  if $15>1
48972    i[{int($1/2)}] [{int($1/2)}]x{$15-1}
48973    i[0] [0]x{$15-1}
48974  fi
48975
48976fx_text_pointcloud3d_preview :
48977  fx_text_pointcloud3d 4,"$2","$3",$4,${5-7},${8-11},${12-14},1 drgba
48978  frame 1,1,0 append_tiles 2,2
48979
48980#@gui 3D Tiles : fx_transition3d, fx_transition3d_preview(0)
48981#@gui : Inter-Frames = _int(10,3,100)
48982#@gui : X-Tiles = int(8,1,64)
48983#@gui : Y-Tiles = int(8,1,64)
48984#@gui : X-Rotation = text("1")
48985#@gui : Y-Rotation = text("1")
48986#@gui : Z-Rotation = text("0")
48987#@gui : Focale = float(800,100,2000)
48988#@gui : Enable Antialiasing = bool(1)
48989#@gui : sep = separator()
48990#@gui : note = note{"<small><b>Note:</b>
48991#@gui : This filter needs two layers to work properly. Set the <i>Input layers</i> option to handle
48992#@gui : multiple input layers.
48993#@gui : </small>"}
48994#@gui : sep = separator()
48995#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/13/08</i>.</small>")
48996fx_transition3d :
48997  f3d $7
48998  transition3d $1,$2,$3,"$4","$5","$6",$8
48999
49000fx_transition3d_preview :
49001  if $!==1 gui_warning_preview "Missing input layer" return fi
49002  f3d $7
49003  k[0,1] transition3d 4,$2,$3,"$4","$5","$6",$8
49004  k[1,2]
49005  r[0] 50%,100%,1,100%,0
49006  r[1] 50%,100%,1,100%,0,0,1
49007  a x
49008  line 50%,0,50%,100%,1,0,0,0,255
49009
49010#@gui B&W Pencil [Animated] : fx_animate_pencilbw, fx_animate_pencilbw_preview(0)
49011#@gui : Frames = _int(10,2,100)
49012#@gui : Output Frames = _bool(1)
49013#@gui : Output Files = _bool(0)
49014#@gui : Output Folder = _folder()
49015#@gui : note = note{"\n<b>Starting Parameters :</b>"}
49016#@gui : Pencil Type = float(2.3,0,5)
49017#@gui : Amplitude = float(100,0,200)
49018#@gui : note = note{"\n<b>Ending Parameters :</b>"}
49019#@gui : Pencil Type = float(0.3,0,5)
49020#@gui : Amplitude = float(60,0,200)
49021#@gui : sep = separator()
49022#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49023fx_animate_pencilbw : skip "${4=}"
49024  if $3 filename="$4/gmic_pencilbw.png" else filename="" fi
49025  animate pencilbw,"${5-6}",\
49026                   "${7-8}",$1,$2,{``$filename}
49027
49028fx_animate_pencilbw_preview : skip "${4=}"
49029  fx_animate_preview pencilbw,"${5-6}",\
49030                                 "${7-8}"
49031
49032#@gui B&W Stencil [Animated] : fx_animate_stencilbw, fx_animate_stencilbw_preview(1)
49033#@gui : Frames = _int(10,2,100)
49034#@gui : Output Frames = _bool(1)
49035#@gui : Output Files = _bool(0)
49036#@gui : Output Folder = _folder()
49037#@gui : note = note{"\n<b>Starting Parameters :</b>"}
49038#@gui : Edge Threshold = float(10,0,30)
49039#@gui : Smoothness = float(10,0,30)
49040#@gui : note = note{"\n<b>Ending Parameters :</b>"}
49041#@gui : Edge Threshold = float(10,0,30)
49042#@gui : Smoothness = float(20,0,30)
49043#@gui : sep = separator()
49044#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49045fx_animate_stencilbw : skip "${4=}"
49046  if $3 filename="$4/gmic_stencilbw.png" else filename="" fi
49047  animate stencilbw,"${5-6}",\
49048                    "${7-8}",$1,$2,{``$filename}
49049
49050fx_animate_stencilbw_preview : skip "${4=}"
49051  fx_animate_preview stencilbw,"${5-6}",\
49052                                  "${7-8}"
49053
49054#@gui Cartoon [Animated] : fx_animate_cartoon, fx_animate_cartoon_preview(0)
49055#@gui : Frames = _int(10,2,100)
49056#@gui : Output Frames = _bool(1)
49057#@gui : Output Files = _bool(0)
49058#@gui : Output Folder = _folder()
49059#@gui : note = note{"\n<b>Global Parameters :</b>"}
49060#@gui : Color Quantization = int(4,2,256)
49061#@gui : note = note{"\n<b>Starting parameters :</b>"}
49062#@gui : Smoothness = float(0.5,0,2)
49063#@gui : Sharpening = float(200,0,400)
49064#@gui : Edge Threshold = float(10,1,30)
49065#@gui : Edge Thickness = float(0.1,0,1)
49066#@gui : Color Strength = float(1.5,0,3)
49067#@gui : note = note{"\n<b>Ending parameters :</b>"}
49068#@gui : Smoothness = float(3,0,2)
49069#@gui : Sharpening = float(200,0,400)
49070#@gui : Edge Threshold = float(10,1,30)
49071#@gui : Edge Thickness = float(0.1,0,1)
49072#@gui : Color Strength = float(1.5,0,3)
49073#@gui : sep = separator()
49074#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49075fx_animate_cartoon : skip "${4=}"
49076  if $3 filename="$4/gmic_cartoon.png" else filename="" fi
49077  animate cartoon,"${6-10},$5",\
49078                  "${11-15},$5",$1,$2,{``$filename}
49079
49080fx_animate_cartoon_preview : skip "${4=}"
49081  fx_animate_preview cartoon,"${6-10},$5",\
49082                                "${11-15},$5"
49083
49084#@gui Edges [Animated] : fx_animate_edges, fx_animate_edges_preview(0)
49085#@gui : Frames = _int(10,2,100)
49086#@gui : Output Frames = _bool(1)
49087#@gui : Output Files = _bool(0)
49088#@gui : Output Folder = _folder()
49089#@gui : note = note{"\n<b>Global Parameters :</b>"}
49090#@gui : Negative Colors = bool(0)
49091#@gui : note = note{"\n<b>Starting Parameters :</b>"}
49092#@gui : Smoothness = float(0,0,10)
49093#@gui : Edge Threshold = float(10,0,30)
49094#@gui : note = note{"\n<b>Ending Parameters :</b>"}
49095#@gui : Smoothness = float(0,0,10)
49096#@gui : Edge Threshold = float(30,0,30)
49097#@gui : sep = separator()
49098#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49099fx_animate_edges : skip "${4=}"
49100  if $3 filename="$4/gmic_edges.png" else filename="" fi
49101  animate fx_edges,"${6-7},$5",\
49102                   "${8-9},$5",$1,$2,{``$filename}
49103
49104fx_animate_edges_preview : skip "${4=}"
49105  fx_animate_preview fx_edges,"${6-7},$5",\
49106                                   "${8-9},$5"
49107
49108#@gui Edges on Fire : fx_fire_edges, fx_fire_edges_preview(0)
49109#@gui : Edges = float(0.7,0,3)
49110#@gui : Attenuation = float(0.25,0,1)
49111#@gui : Smoothness = float(0.5,0,5)
49112#@gui : Threshold = float(25,0,100)
49113#@gui : sep = separator()
49114#@gui : Number of Frames = _int(20,1,999)
49115#@gui : Starting Frame = int(20,0,199)
49116#@gui : Frame Skip = _int(0,0,20)
49117#@gui : sep = separator()
49118#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
49119#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
49120#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
49121#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
49122#@gui : sep = separator()
49123#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/06/07</i>.</small>")
49124fx_fire_edges :
49125  fire_edges ${1-7} rv
49126
49127fx_fire_edges_preview :
49128  gui_split_preview "fire_edges $1,$2,$3,$4,1,$6,0",${-3--1}
49129
49130#@gui Lava Lamp : fx_lavalampbw, fx_lavalampbw_preview(0)
49131#@gui : Number of Key-Frames = _int(3,2,50)
49132#@gui : Number of Inter-Frames = _int(30,2,100)
49133#@gui : Smooth Looping = _bool(1)
49134#@gui : sep = separator()
49135#@gui : Resolution = float(20,1,100)
49136#@gui : Size = float(2,0,30)
49137#@gui : Smoothness = _float(0.01,0,1)
49138#@gui : Transparent Background = bool(0)
49139#@gui : sep = separator()
49140#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/06/07</i>.</small>")
49141fx_lavalampbw :
49142  if !$! (255;100^64;16^128;0) r. 512,512,1,3,3 fi
49143  repeat $! l[$<] remove_opacity
49144    w={w} h={h}
49145    +r $4%,$4%,1,1,0 [-1]x{$1-1} rand[^0] 0,1 stencil[^0] $5,0
49146    if $3 [1] fi
49147    morph[^0] $2,$6,0
49148    stencil[^0] $5,0
49149    r[^0] $w,$h,1,1,3 b[^0] 10 >=[^0] 50% *[^0] 255
49150    r[^0] 100%,100%,1,4 j[^0] [0] rm[0]
49151    if $3 rm. fi
49152  endl done
49153  if !$7 repeat $! l[$>] split_opacity n. 0,1 *[^-1] . rm. endl done fi
49154
49155fx_lavalampbw_preview :
49156  fx_lavalampbw 2,2,1,$4,$5,$6,$7 k[0]
49157
49158#@gui Lissajous [Animated] : fx_animate_lissajous, fx_animate_lissajous_preview(1)
49159#@gui : Frames = _int(10,2,100)
49160#@gui : Output as Frames = _bool(1)
49161#@gui : Output as Files = _bool(0)
49162#@gui : Output Folder = _folder()
49163#@gui : sep = separator()
49164#@gui : note = note{"<b>Starting parameters :</b>"}
49165#@gui : Resolution = int(4096,2,8192)
49166#@gui : X-Size = float(0.9,0,2)
49167#@gui : Y-Size = float(0.9,0,2)
49168#@gui : Z-Size = float(3,1,10)
49169#@gui : X-Multiplier = float(8,0,32)
49170#@gui : Y-Multiplier = float(7,0,32)
49171#@gui : Z-Multiplier = float(0,0,32)
49172#@gui : X-Offset = float(0,0,1)
49173#@gui : Y-Offset = float(0,0,1)
49174#@gui : Z-Offset = float(0,0,1)
49175#@gui : X-Angle = float(0,0,360)
49176#@gui : Y-Angle = float(0,0,360)
49177#@gui : Z-Angle = float(0,0,360)
49178#@gui : Thickness = float(0,0,50)
49179#@gui : Color = color(255,255,255,255)
49180#@gui : sep = separator()
49181#@gui : note = note{"<b>Ending parameters :</b>"}
49182#@gui : Resolution = int(4096,2,8192)
49183#@gui : X-Size = float(0.9,0,2)
49184#@gui : Y-Size = float(0.9,0,2)
49185#@gui : Z-Size = float(3,1,10)
49186#@gui : X-Multiplier = float(8,0,32)
49187#@gui : Y-Multiplier = float(7,0,32)
49188#@gui : Z-Multiplier = float(0,0,32)
49189#@gui : X-Offset = float(0,0,1)
49190#@gui : Y-Offset = float(0,0,1)
49191#@gui : Z-Offset = float(0,0,1)
49192#@gui : X-Angle = float(0,0,360)
49193#@gui : Y-Angle = float(0,0,360)
49194#@gui : Z-Angle = float(0,0,360)
49195#@gui : Thickness = float(0,0,50)
49196#@gui : Color = color(255,255,255,255)
49197#@gui : sep = separator()
49198#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/04</i>.</small>")
49199fx_animate_lissajous : skip "${4=}"
49200  if $3 filename="$4/gmic_lissajous.png" else filename="" fi
49201  animate fx_lissajous,"${5-22}",\
49202                       "${23-40}",$1,$2,{``$filename}
49203
49204fx_animate_lissajous_preview : skip "${4=}"
49205  fx_animate_preview fx_lissajous,"${5-22}",\
49206                                  "${23-40}",0
49207
49208#@gui Moir&eacute; Animation : fx_moire, fx_moire_preview(1)+ : *
49209#@gui : Stripe orientation = choice(1,"Horizontal","Vertical")
49210#@gui : Input Transparency = choice("Replace With White","Reconstruct From Previous Frames")
49211#@gui : Output Format = choice{2,"Same as Input","A4 / 75 PPI","A4 / 100 PPI (Recommended)",
49212#@gui : "A4 / 150 PPI","A4 / 300 PPI"}
49213#@gui : Auto-Reduce Number of Frames = bool(1)
49214#@gui : Landscape = bool(1)
49215#@gui : Margin (%) = float(2,0,30)
49216#@gui : sep = separator()
49217#@gui : Print Frame Numbers = choice(1,"Disable","Top Left","Top Right","Bottom Left","Bottom Right")
49218#@gui : Size of Frame Numbers (%) = float(5,0,30)
49219#@gui : sep = separator()
49220#@gui : note = note{"<small><b><span color="#EE5500">Instructions:</span></b>\n\n
49221#@gui : This filter renders Moire Animations, as shown on
49222#@gui : <a href="https://www.youtube.com/watch?v=f5plDb_JRq4">this video</a>.\n
49223#@gui : To make the animation visible:\n\n
49224#@gui : &bull; Before running the filter, ensure that all frames are aligned and have the same size
49225#@gui : (and preferably without alpha)!\n
49226#@gui : &bull; Run the filter. It is recommended to keep the number of frames &lt;=6.\n
49227#@gui : &bull; Print the first layer (merged frames) on a A4 blank paper, at 300 PPI.\n
49228#@gui : &bull; Print the second layer (mask) on a A4 transparent sheet, at 300 PPI.\n
49229#@gui : &bull; Drag the transparent layer over the A4 paper to render the animation effect.
49230#@gui : "}
49231#@gui : sep = separator()
49232#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/03/02</i>.</small>")
49233fx_moire :
49234  if 0$_is_preview" && "!narg($_preview_width) _preview_width,_preview_height=512 fi
49235
49236  # Reconstruct frames from base image.
49237  if $2 repeat $! if $>" && "{$<,s==2||s==4} blend[$<] [{$<+1}],alpha,1,1 fi done fi
49238
49239  # Auto-reduce number of frames (4, 5 or 6).
49240  if $4
49241    skip={"
49242      l<=6?1:(
49243        const l5 = int(l/5);
49244        const l4 = int(l/4);
49245        const l6 = int(l/6);
49246        5*l5==l?l5:
49247        6*l6==l?l6:
49248        4*l4==l?l4:(
49249          const r6 = abs(6*l6 - l);
49250          const r5 = abs(5*l5 - l);
49251          const r4 = abs(4*l4 - l);
49252          r5<=r4 && r5<=r6?l5:
49253          r6<=r4 && r6<=r5?l6:
49254          l4
49255        )
49256      )"}
49257    k[^:$skip]
49258    if $!>6 rm[6--1] fi
49259  fi
49260  N=$!
49261
49262  # Blend with white background.
49263  repeat $! l[$>] if s==2||s==4 drgba 255 fi endl done
49264
49265  # Constrain size of frames.
49266  if 0$_is_preview
49267    W,H=$_preview_width,$_preview_height
49268    if $3
49269      if $W>$H W={$H/sqrt(2)} else H={$W*sqrt(2)} fi
49270      fW,fH=$W,$H
49271      W,H/={arg($3,4,3,2,1)}
49272    fi
49273  elif $3
49274    W,H,fW,fH={"$3==1?[620,877]:$3==2?[827,1169]:$3==3?[1240,1754]:[2480,3508]"},2480,3508
49275    W,H,fW,fH={round([$W,$H,$fW,$fH]*0.95)} # Take printer margins into account
49276  else
49277    W,H=${-max_wh}
49278  fi
49279
49280  if $3" && "$5 # Landscape mode
49281    W,H=$H,$W fW,fH=$fH,$fW
49282  fi
49283  if !narg($fW) fW,fH=$W,$H fi
49284  repeat $! l[$>] if [w,h]!=[$W,$H]
49285    - 255
49286    rr2d {(100-$6)%*[$W,$H]},2,2
49287    r $W,$H,1,100%,0,0,0.5,0.5
49288    + 255
49289  fi endl done
49290
49291  # Generate merged image and mask.
49292  if $1 # Vertical stripes
49293    100%,100%,1,100%,"i(#x%"$N",x,y,z,c)" rm[^-1]
49294    100%,100%,1,1,"x%"$N"?0:255"
49295  else # Horizontal stripes
49296    100%,100%,1,100%,"i(#y%"$N",x,y,z,c)" rm[^-1]
49297    100%,100%,1,1,"y%"$N"?0:255"
49298  fi
49299
49300  # Upscale with nearest-neighbor for printing.
49301  if $3
49302    ir={round(max($fW/$W,$fH/$H))*100}
49303    r $ir%,$ir%,1,100%,1
49304    r $fW,$fH,1,100%,0,1,0.5,0.5
49305  fi
49306
49307  # Insert frame number label.
49308  if $7" && "$8
49309    ax,ay={a=$7-1;[a%2?0.95:0.05,int(a/2)?0.95:0.05]}
49310    0 t. "#"$N,0,0,{0,h*$8%},1,255 autocrop. frame. 5%,15%,0 negate. to_rgb. j[^-1] .,$ax~,$ay~ rm.
49311  fi
49312  nm "name(Merged Frames),pos(0,0),opacity(100),mode(alpha)","name(Mask),pos(0,0),opacity(100),mode(alpha)"
49313  u $N
49314
49315fx_moire_preview :
49316  _is_preview=1
49317  fx_moire $*
49318
49319#@gui Rodilius [Animated] : fx_animate_rodilius, fx_animate_rodilius_preview(1)
49320#@gui : Frames = _int(10,2,100)
49321#@gui : Output as Frames = _bool(1)
49322#@gui : Output as Files = _bool(0)
49323#@gui : Output Folder = _folder()
49324#@gui : Color Mode = choice(1,"Darker","Lighter")
49325#@gui : note = note{"\n<b>Starting Parameters :</b>"}
49326#@gui : Amplitude = float(10,0,30)
49327#@gui : Thickness = float(10,0,100)
49328#@gui : Sharpness = float(300,0,1000)
49329#@gui : Orientations = int(5,2,20)
49330#@gui : Offset = float(0,0,180)
49331#@gui : note = note{"\n<b>Ending Parameters :</b>"}
49332#@gui : Amplitude = float(10,0,30)
49333#@gui : Thickness = float(10,0,100)
49334#@gui : Sharpness = float(300,0,1000)
49335#@gui : Orientations = int(5,2,20)
49336#@gui : Offset = float(180,0,180)
49337#@gui : sep = separator()
49338#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49339fx_animate_rodilius : skip "${4=}"
49340  if $3 filename="$4/gmic_rodilius.png" else filename="" fi
49341  animate rodilius,"${6-10},$5",\
49342                   "${11-15},$5",$1,$2,{``$filename}
49343
49344fx_animate_rodilius_preview : skip "${4=}"
49345  fx_animate_preview rodilius,"${6-10},$5",\
49346                              "${11-15},$5"
49347
49348#@gui Soft Glow [Animated] : fx_animate_glow, fx_animate_glow_preview(1)
49349#@gui : Frames = _int(10,2,100)
49350#@gui : Output as Frames = _bool(1)
49351#@gui : Output as Files = _bool(0)
49352#@gui : Output Folder = _folder()
49353#@gui : note = note{"\n<b>Starting Parameters :</b>"}
49354#@gui : Amplitude = float(0,0,8)
49355#@gui : note = note{"\n<b>Ending Parameters :</b>"}
49356#@gui : Amplitude = float(3,0,8)
49357#@gui : sep = separator()
49358#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49359fx_animate_glow : skip "${4=}"
49360  if $3 filename="$4/gmic_glow.png" else filename="" fi
49361  animate glow,"$5",\
49362               "$6",$1,$2,{``$filename}
49363
49364fx_animate_glow_preview : skip "${4=}"
49365  fx_animate_preview glow,"$5",\
49366                          "$6"
49367
49368#@gui Spatial Transition : fx_spatial_transition, fx_spatial_transition_preview(1)
49369#@gui : Number of Added Frames = _int(10,1,256)
49370#@gui : Shading (%) = float(0,0,100)
49371#@gui : Transition Shape = choice(7,"Bottom Layer","Top Layer","Custom Formula","Horizontal","Vertical",
49372#@gui : "Angular","Radial","Plasma")
49373#@gui : Custom Formula = text{"cos(x*y/(16+32*A))"}_1
49374#@gui : A-Value = float(0,0,1)
49375#@gui : sep = separator()
49376#@gui : Preview Type = choice(1,"Transition Map","Timed Image","Sequence x4","Sequence x6","Sequence x8")
49377#@gui : Preview Time = float(0.5,0,1)
49378#@gui : Preview = value(0)
49379#@gui : sep = separator()
49380#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/10/04</i>.</small>")
49381fx_spatial_transition :
49382  to_rgba r ${-max_wh},1,100%,0,0,0.5,0.5
49383  shape=-1 formula=
49384  if $3==0 # Do nothing.
49385  elif $3==1 shape=0
49386  elif $3==2 formula="$4"
49387  elif $3==3 formula="sin(x*0.5*pi/w*(1+100*A))"
49388  elif $3==4 formula="sin(y*0.5*pi/h*(1+100*A))"
49389  elif $3==5 formula="atan2(y-h/2,x-w/2)%((1-A)*2*pi+0.001)"
49390  elif $3==6 formula="R=0.5*sqrt(w*w+h*h);sqrt((y-h/2)^2+(x-w/2)^2)%(0.001+R*(1-A))"
49391  elif $3==7 100%,100% plasma. 1,1,{8/(1+$5)} equalize. 1024
49392  fi
49393  if narg($formula)
49394    {w},{h},1,1,"A=$5;"$formula fi
49395  if $-1 # Preview mode.
49396    if $6==0 k[$shape] norm n 0,255
49397    elif $6==1" && "$7==0 rm[$shape] rm.
49398    elif $6==1" && "$7==1 rm[$shape] rm[0]
49399    elif $6==1
49400      transition[^$shape] [$shape],$1,$2,$7*($1-1) rm[$shape] rm[0--1:2]
49401    else
49402      transition[^$shape] [$shape],{$6*2},$2 rm[$shape] to_rgba
49403    fi
49404    if $!>1 to_rgba frame 2%,2%,0,0,0,0 append_tiles , fi
49405  else # Apply mode.
49406    transition[^$shape] [$shape],$1,$2
49407    rm.
49408  fi
49409  nm name(transition),pos(0,0)
49410  if narg($formula) u "{$1}{$2}{$3}{"$formula"}_"{$3==2?2:1}"{$5}{$6}{$7}{0}" fi
49411
49412fx_spatial_transition_preview :
49413  if ($3<=1" && "$!<3)" || "($3>1" && "$!<2)
49414    gui_print_preview "Warning:",,"This filter requires more input layers to work properly."
49415    return
49416  fi
49417  fx_spatial_transition ${1-3},"$4",${5-7},1
49418
49419
49420#@gui ____<b>Silhouettes</b>
49421#---------------------------
49422
49423#@gui <i>Misc</i>
49424
49425#@gui Cupid : fx_cupid, fx_cupid_preview
49426#@gui : Size (%) = float(75,0,100)
49427#@gui : Smoothness = float(0,0,10)
49428#@gui : Color = color(255,255,255,255)
49429#@gui : Antialiasing = bool(1)
49430#@gui : sep = separator()
49431#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/08</i>.</small>")
49432fx_cupid :
49433  max_wh={$!>0?[${-max_wh}]:[512,512]}
49434  w,h={S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)]}
49435  l[]
49436    shape_cupid {($7?2:1)*min($w,$h)}
49437    if $7 r2dx 50% fi
49438    frame {2.5*$2}%,{2.5*$2}%,0 b $2% * $6 round c 0,255 autocrop
49439    100%,100%,1,3 fc. ${3-5} rv[-2,-1] a c
49440    gui_set_layer_name "Heart"
49441    gui_set_layer_pos {0.5*([$max_wh]-[w,h])}
49442  endl
49443  mv. 0
49444
49445fx_cupid_preview :
49446  fx_cupid $* blend[^0] [0],alpha rm[0]
49447
49448#@gui Gear : fx_gear, fx_gear_preview
49449#@gui : Size (%) = float(75,0,100)
49450#@gui : Number of Teeth = int(12,1,96)
49451#@gui : Elevation (%) = float(15,0,100)
49452#@gui : Angle (%) = float(0,0,100)
49453#@gui : Inner Radius (%) = float(40,0,100)
49454#@gui : Smoothness = float(0,0,10)
49455#@gui : Color = color(255,255,255,255)
49456#@gui : Antialiasing = bool(1)
49457#@gui : sep = separator()
49458#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/08</i>.</small>")
49459fx_gear :
49460  max_wh={$!>0?[${-max_wh}]:[512,512]}
49461  w,h={S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)]}
49462  l[]
49463    shape_gear {($11?2:1)*min($w,$h)},${2-5}
49464    if $11 r2dx 50% fi
49465    frame {2.5*$6}%,{2.5*$6}%,0 b $6% * $10 round c 0,255 autocrop
49466    100%,100%,1,3 fc. ${7-9} rv[-2,-1] a c
49467    gui_set_layer_name "Gear"
49468    gui_set_layer_pos {0.5*([$max_wh]-[w,h])}
49469  endl
49470  mv. 0
49471
49472fx_gear_preview :
49473  fx_gear $* blend[^0] [0],alpha rm[0]
49474
49475#@gui Heart : fx_heart, fx_heart_preview
49476#@gui : Size (%) = float(75,0,100)
49477#@gui : Smoothness = float(0,0,10)
49478#@gui : Color = color(255,255,255,255)
49479#@gui : Antialiasing = bool(1)
49480#@gui : sep = separator()
49481#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/08</i>.</small>")
49482fx_heart :
49483  max_wh={$!>0?[${-max_wh}]:[512,512]}
49484  w,h={S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)]}
49485  l[]
49486    shape_heart {($7?2:1)*min($w,$h)}
49487    if $7 r2dx 50% fi
49488    frame {2.5*$2}%,{2.5*$2}%,0 b $2% * $6 round c 0,255 autocrop
49489    100%,100%,1,3 fc. ${3-5} rv[-2,-1] a c
49490    gui_set_layer_name "Heart"
49491    gui_set_layer_pos {0.5*([$max_wh]-[w,h])}
49492  endl
49493  mv. 0
49494
49495fx_heart_preview :
49496  fx_heart $* blend[^0] [0],alpha rm[0]
49497
49498#@gui Sierpinski Triangle : fx_sierpinski, fx_sierpinski(1)
49499#@gui : Recursions = int(6,0,10)
49500#@gui : 1st X-Coord = float(50,0,100)
49501#@gui : 1st Y-Coord = float(0,0,100)
49502#@gui : 2nd X-Coord = float(0,0,100)
49503#@gui : 2nd Y-Coord = float(100,0,100)
49504#@gui : 3rd X-Coord = float(100,0,100)
49505#@gui : 3rd Y-Coord = float(100,0,100)
49506#@gui : Color = color(255,255,255)
49507#@gui : Opacity = float(1,0,1)
49508#@gui : sep = separator()
49509#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49510fx_sierpinski :
49511  repeat $! l[$>] split_opacity l[0]
49512    100%,100% sierpinski. ${1-7}
49513    +fc.. $8,$9,$10 j[0] .,0,0,0,0,$11,..,255 rm[-2,-1]
49514  endl a c endl done
49515
49516#@gui _<i>Nature</i>
49517
49518#@gui Barnsley Fern : fx_barnsley_fern, fx_barnsley_fern_preview(1)
49519#@gui : Type = choice("Asplenium Adiantum-Nigrum","Thelypteridaceae")
49520#@gui : Density (%) = float(100,0,300)
49521#@gui : Angle = float(30,-180,180)
49522#@gui : Opacity (%) = float(40,0,100)
49523#@gui : Color = color(10,178,0,255)
49524#@gui : Add as a New Layer = _bool(1)
49525#@gui : sep = separator()
49526#@gui : note = note("This filter renders the Barnsley fern fractal, described here:")
49527#@gui : url = link("https://en.wikipedia.org/wiki/Barnsley_fern")
49528#@gui : sep = separator()
49529#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/18/10</i>.</small>")
49530fx_barnsley_fern :
49531  repeat $! l[$<]
49532    shape_fern {min(w,h)},$2%,$3,{$4%},$1 *. 255
49533    100%,100%,1,3,[${5-7}]
49534    rv[-2,-1] a[-2,-1] c
49535    if !$9 blend alpha,{$8/255}
49536    else nm. "name(Barnsley Fern),opacity("{round($8*100/255)})")" rv[-2,-1]
49537    fi
49538  endl done
49539
49540fx_barnsley_fern_preview :
49541  fx_barnsley_fern ${1-8},0
49542
49543#@gui Snowflake : fx_snowflake, fx_snowflake(1)
49544#@gui : Recursions = int(5,0,6)
49545#@gui : Opacity = float(1,0,1)
49546#@gui : Color = color(255,255,255)
49547#@gui : sep = separator()
49548#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49549fx_snowflake :
49550  repeat $! l[$>] to_color split_opacity l[0]
49551    shape_snowflake {min(w,h)},$1 100%,100%,1,3,[${3-5}]
49552    j[0] .,{([w#0,h#0]-[w#1,h#1])/2},0,0,$2,.. k[0]
49553  endl a c endl done
49554
49555#@gui _<i>Others</i>
49556
49557#@gui Dragon Curve : fx_dragoncurve, fx_dragoncurve(1)
49558#@gui : Recursions = int(20,0,30)
49559#@gui : Angle = float(0,-180,180)
49560#@gui : Opacity = float(1,0,1)
49561#@gui : Color = color(255,255,255)
49562#@gui : sep = separator()
49563#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/01/29</i>.</small>")
49564fx_dragoncurve :
49565  repeat $! l[$>] to_color split_opacity l[0]
49566    shape_dragon {min(w,h)},$1,$2 100%,100%,1,3,[${4-6}]
49567    j[0] .,{([w#0,h#0]-[w#1,h#1])/2},0,0,$3,.. k[0]
49568  endl a c endl done
49569
49570#@gui ____<b>Various</b>
49571#------------------------
49572
49573#@gui Custom Code [Global] : fx_custom_code, fx_custom_code_preview(1)
49574#@gui : Code = text(1,"repeat $! l[$>]\n\n  to_rgb\n  +deform 20\n  blend_edges 3\n\nendl done\n\n\n")
49575#@gui : sep = separator()
49576#@gui : Channel(s) = choice{"None (Allows Multi-layers)","All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]",
49577#@gui : "RGB [Blue]","RGBA [Alpha]","Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]",
49578#@gui : "YCbCr [Luminance]","YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
49579#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
49580#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
49581#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
49582#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]"}
49583#@gui : Value Action = choice("None","Cut","Normalize")
49584#@gui : Display Debug Info on Preview = bool(0)
49585#@gui : Debug Font Size = choice(2,"Tiny","Small","Normal","Large")
49586#@gui : sep = separator()
49587#@gui : Preview Type = choice{"Full (Allows Multi-Layers)","Forward Horizontal","Forward Vertical",
49588#@gui : "Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom",
49589#@gui : "Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"}
49590#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
49591#@gui : sep = separator()
49592#@gui : note = note{"<small><b>Note: </b>
49593#@gui : This filter can execute any set of instructions understood by the <b>G'MIC</b> language interpreter.
49594#@gui : Here, you can then test some commands before creating your own G'MIC custom commands and plug-in
49595#@gui : menu entries.\n\n
49596#@gui : Please look at the documentation reference web page :</small>"}
49597#@gui : url = link("https://gmic.eu/reference/")
49598#@gui : note = note{"<small>
49599#@gui : to learn more about available <b>G'MIC</b> commands.
49600#@gui : </small>"}
49601#@gui : sep = separator()
49602#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/03/10</i>.</small>")
49603
49604#@gui Custom Code [Local] : fx_custom_code, fx_custom_code_preview(0)
49605#@gui : Code = text(1,"repeat $! l[$>]\n\n  to_rgb\n  +deform 20\n  blend_edges 3\n\nendl done\n\n\n")
49606#@gui : sep = separator()
49607#@gui : Channel(s) = choice{"None (Allows Multi-layers)","All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]",
49608#@gui : "RGB [Blue]","RGBA [Alpha]","Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]",
49609#@gui : "YCbCr [Luminance]","YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
49610#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
49611#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
49612#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
49613#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]"}
49614#@gui : Value Action = choice("None","Cut","Normalize")
49615#@gui : Display Debug Info on Preview = bool(0)
49616#@gui : Debug Font Size = choice(2,"Tiny","Small","Normal","Large")
49617#@gui : sep = separator()
49618#@gui : Preview Type = choice{"Full (Allows Multi-Layers)","Forward Horizontal","Forward Vertical",
49619#@gui : "Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom",
49620#@gui : "Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"}
49621#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
49622#@gui : sep = separator()
49623#@gui : note = note{"<small><b>Note: </b>
49624#@gui : This filter can execute any set of instructions understood by the <b>G'MIC</b> language interpreter.
49625#@gui : Here, you can then test some commands before creating your own G'MIC custom commands and
49626#@gui : plug-in menu entries.\n\n
49627#@gui : Please look at the documentation reference web page :</small>"}
49628#@gui : url = link("https://gmic.eu/reference/")
49629#@gui : note = note{"<small>
49630#@gui : to learn more about available <b>G'MIC</b> commands.
49631#@gui : </small>"}
49632#@gui : sep = separator()
49633#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/03/10</i>.</small>")
49634fx_custom_code : skip "${1=-skip ,}"
49635  ({'{/"$1"}'}) discard. 92,10 _gcp_arg={t} rm.
49636  m "_fx_custom_code_start : "$_gcp_arg
49637  if $4
49638    _nb_in=$!
49639    _dim_in="" sep=""
49640    repeat $! l[$>]
49641      _dim_in=$_dim_in$sep"["$>"] = "{w}x{h}x{d}x{s}", in ["{_round([im,iM],0.1)}"]" sep="\n"
49642    endl done
49643  fi
49644  if $2
49645    ac "_fx_custom_code_start _status_out=${}",{$2-1},$3
49646  else
49647    _fx_custom_code_start _status_out=${}
49648    if $3==1 c 0,255 elif $3==2 n 0,255 fi
49649  fi
49650  if $4
49651    _nb_out=$!
49652    _dim_out="" sep="" repeat $! l[$>]
49653      _dim_out=$_dim_out$sep"["$>"] = "{w}x{h}x{d}x{s}", in ["{_round([im,iM],0.1)}"]" sep="\n" endl done
49654  fi
49655  um _fx_custom_code_start
49656
49657fx_custom_code_preview : skip "${1=-skip ,}"
49658  w={w} h={h}
49659  l
49660    ({'{/"$1"}'}) discard. 92,10 _gcp_arg={t} rm.
49661    if $6 gui_split_preview "fx_custom_code $_gcp_arg,${2--2}",${-3--1}
49662    else fx_custom_code $_gcp_arg,${2--2}
49663    fi
49664  onfail
49665    error_msg=${}
49666    rr2d $w,$h,2,1
49667    gui_print_preview "Syntax error:",,{``$error_msg},20,40
49668  endl
49669  if $4 # Display debug infos on preview
49670    if !$3 % 256 fi
49671    if $_preview_mode>=4 gui_preview fi
49672    siz0=13 siz1=17 siz2=19 siz3=22
49673    if ['$_status_out']==0 _status_out=(empty) fi
49674    info="Input images: "#$_nb_in"\n"\
49675         $_dim_in"\n\n"\
49676         "Output images: "#$_nb_out"\n"\
49677         $_dim_out"\n\n"\
49678         "Output status: "$_status_out
49679    0 t. {``$info},0,0,${siz$5},1,255 expand_xy. 5,0 +dilate. 3 a[-2,-1] c
49680    rr2d[^-1] ${-max_wh},2,2
49681    r. ..,..,1,100%,0 drgba[^-1]
49682    /[^-1] 2 blend[^-1] .,alpha rm.
49683  fi
49684
49685#@gui Export RGB-565 File : fx_output_565,_none_
49686#@gui : Filename = _fileout("out565.rgb")
49687#@gui : Reverse endianness = _bool(0)
49688#@gui : sep = separator()
49689#@gui : note = note{"<b>Note:</b> This filter saves your selected layer as a raw RGB-565 file. Keep in mind that
49690#@gui : you have to remember the image dimension if you want to reload the image file afterwards!"}
49691#@gui : sep = separator()
49692#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/05/03</i>.</small>")
49693fx_output_565 :
49694  output_565 "$1",$2
49695
49696#@gui Games & Demos : fx_gmic_demos, fx_gmic_demos_preview
49697#@gui : Selection = choice("2048","Blobs Editor","Bouncing Balls","Connect-Four","Fire Effect","Fireworks",
49698#@gui : "Fish-Eye Effect","Fourier Filtering","Hanoi Tower",
49699#@gui : "Histogram","Hough Transform","Jawbreaker","Virtual Landscape","The Game of Life","Light Effect",
49700#@gui : "Mandelbrot Explorer","3D Metaballs","Minesweeper",
49701#@gui : "Minimal Path","Pacman","Paint","Plasma Effect","RGB Quantization","3D Reflection","3D Rubber Object",
49702#@gui : "Shadebobs","Spline Editor",
49703#@gui : "3D Starfield","Tetris","Tic-Tac-Toe","3D Waves","Fractal Whirl")
49704#@gui : sep = separator()
49705#@gui : note = note("<small><b>Note:</b> This filter proposes a showcase of some interactive demos, all written
49706#@gui : as G'MIC scripts.</small>")
49707#@gui : note = note{"<small>On most demos, you can use the keyboard shortcut <b>CTRL+D</b> to double the window
49708#@gui : size (and <b>CTRL+C</b> to go back to the original size).
49709#@gui : Also, feel free to use the mouse buttons, as they are often used to perform an action.
49710#@gui : </small>"}
49711#@gui : sep = separator()
49712#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/10/09</i>.</small>")
49713fx_gmic_demos :
49714  coms=2048,blobs,bouncing,connect4,fire,fireworks,fisheye,fourier,hanoi,histogram,hough,jawbreaker,landscape,life,\
49715       light,mandelbrot,metaballs3d,minesweeper,minimal_path,pacman,paint,plasma,quantize_rgb,reflection3d,\
49716       rubber3d,shadebobs,spline,starfield3d,tetris,tictactoe,waves,whirl
49717  com=${arg\ {1+$1},$coms}
49718  if $!>0 sel=0 else sel= fi
49719  +l[$sel] m "foo : x_"$com foo rm um foo endl
49720
49721fx_gmic_demos_preview :
49722  rm filename=${-path_tmp}gmic_demos.cimgz
49723  if isfile(['{/$filename}']) $filename
49724  else l[] https://gmic.eu/img/gmic_demos.cimgz o $filename endl
49725  fi
49726  k[$1,-1] rows. $1 map[0] [1] k[0]
49727
49728#@gui Histogram Analysis : _none_, fx_display_histogram(1)
49729#@gui : Number of Clusters = int(256,2,1024)
49730#@gui : sep = separator()
49731#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
49732#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
49733#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
49734#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
49735#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
49736#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
49737#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
49738#@gui : sep = separator()
49739#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
49740fx_display_histogram :
49741  mode=${arg\ 1+$2,all,rgba,rgb,rgb_r,rgb_g,rgb_b,rgba_a,\
49742             lrgb,lrgb_r,lrgb_g,lrgb_b,\
49743             ycbcr_y,ycbcr_cbcr,ycbcr_cb,ycbcr_cr,ycbcr_cg,\
49744             lab_l,lab_ab,lab_a,lab_b,\
49745             lch_ch,lch_c,lch_h,\
49746             hsv_h,hsv_s,hsv_v,hsi_i,hsl_l,\
49747             cmyk_c,cmyk_m,cmyk_y,cmyk_k,\
49748             yiq_y,yiq_iq}
49749  _ac_$mode m "_ac_precond : "$_p m "_ac_forward : "$_f m "_ac_backward : "$_b
49750  repeat $! l[$>]
49751    _ac_precond _ac_forward[0] channels $_s
49752    display_histogram {w},{h},$1,0,255
49753    if s==2" || "s==4 channels 0,2 fi
49754  endl done
49755
49756#@gui Import Data : fx_import_image, gui_no_preview
49757#@gui : Filename = filein()
49758#@gui : Normalize = bool(1)
49759#@gui : note = note{"\n<small><b>Note: </b>
49760#@gui : This filter can import any image data read by the <b>G'MIC</b> language interpreter.
49761#@gui : It includes exotic formats as : <i>Pandore, CImg, Inrimage, AVI/MPEG (requires FFMPEG installed), ...</i>
49762#@gui : </small>"}
49763#@gui : sep = separator()
49764#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49765fx_import_image : skip "${1=}"
49766  rm i "$1" s z if $2 n 0,255 else c 0,255 fi
49767
49768#@gui Import RGB-565 File : fx_input_565
49769#@gui : Filename = filein()
49770#@gui : Width = text("800")
49771#@gui : Height = text("600")
49772#@gui : Reverse endianness = bool(0)
49773#@gui : sep = separator()
49774#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/05/03</i>.</small>")
49775fx_input_565 : skip "${1=}"
49776  l[] check "isint($2) && $2>0 && isint($3) && $3>0"
49777  onfail error "Invalid Specified Dimensions"
49778  endl
49779  if ['"$1"']==0 gui_warning_preview "Choose a filename"
49780  elif !isfile(['"$1"']) gui_warning_preview "Filename not found!"
49781  else input_565 "$1",${2-4} mv. 0
49782  fi
49783
49784#@gui Intarsia : fx_intarsia, fx_intarsia_preview
49785#@gui : note = note{"<small><b>Note:</b>
49786#@gui : Intarsia is a method of Crochet/Knitting with a number of colours, in which a separate ball of yarn
49787#@gui : is used for each area of colour.
49788#@gui : This filter creates a HTML version of a graph chart which is solely used for this purpose
49789#@gui : </small>"}
49790#@gui : sep = separator()
49791#@gui : Output Directory = _folder("")
49792#@gui : Output HTML File = _text("intarsia.html")
49793#@gui : sep = separator()
49794#@gui : Maximum Image Size = int(512,2,1024)
49795#@gui : Maximum Number of Image Colors = _int(12,2,64)
49796#@gui : Starting Point = choice(1,"Top Left","Top Right","Bottom Left","Bottom Right")
49797#@gui : Loop Method = choice("Row by Row","Column by Column")
49798#@gui : sep = separator()
49799#@gui : Add Comment Area in HTML Page = _bool(1)
49800#@gui : Preview Progress (%) = float(100,0,100)
49801#@gui : sep = separator()
49802#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/09/07</i>.</small>")
49803fx_intarsia :
49804  to_rgb repeat $! nm=${gui_layer_name[$>]} +l[$>]
49805
49806    # Constrain image for size and number of colors, and index it with colormap.
49807    if max(w,h)>$3 rr2d $3,$3,0 fi
49808    +colormap 0
49809    if w>$4 rm. +colormap $4,1 fi
49810    round[1] index[0] [1]
49811
49812    # Output header and title.
49813    0 nm. $nm ('{b}') f. 'if(x,i,if(i>=97&&i<=122,i-32,i))' image_basename={t} rm[-2,-1]
49814    ('"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\
49815       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\
49816       <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\
49817       <head></head><body bgcolor=\"#FFFDFF\"><center><font size=\"-1\">\n\
49818       <h2>"$image_basename" ("{0,w}x{0,h}")</h2>\n\
49819       <table cellpadding=\"8\"><tr><td>\n\
49820       <table cellpadding=\"4\">\n"')
49821
49822    # Render image of colors.
49823    0 nm. "$2" image_name={b} rm.
49824    nb_cols={1,w}
49825    repeat $nb_cols
49826      color={1,I($>)}
49827      R={arg(1,$color)} G={arg(2,$color)} B={arg(3,$color)}
49828      ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'}
49829      f. if(i>=_'a'" && "i<=_'z',i+_'A'-_'a',i)
49830      hcolor={t} rm.
49831      48,32,1,4 fc. $color,255 frame. 1,1,0,0,0,255 o. "$1/"${image_name}_$>.png rm.
49832      ('"<tr><td><b>Colour "$>"</b></td><td><img src=\""${image_name}_$>.png"\" /></td><td>#"$hcolor"</td></tr>\n"')
49833    done
49834    ('"</table>\n</td><td>"')
49835
49836    # Render result and overview images.
49837    starting=${"arg {1+$5},\"Top left\",\"Top right\",\"Bottom left\",\"Bottom right\""}
49838    label=${"arg {1+$6},Row,Column"}
49839    if $6 dir0="T &#8594; B" dir1="B &#8594; T" else dir0="L &#8594; R" dir1="R &#8594; L" fi
49840    dir={arg(1+2*$5+$6,0,0,1,0,0,1,1,1)}
49841
49842    +map[0] [1]
49843    +fx_intarsia_preview. ${1-7},63 drgba.
49844    rr2d.. 200,200,1,1
49845    to_rgba[-2,-1] frame[-2,-1] 1,1,0,0,0,255 frame[-2,-1] 0,20,0,0,0,0
49846    t.. "Result",0,0,16,1,0,0,0,255
49847    t. "Ordering overview",0,0,16,1,0,0,0,255
49848    frame[-2,-1] 20,20,0,0,0,0
49849
49850    o.. "$1/"${image_name}_A.png
49851    o. "$1/"${image_name}_B.png
49852    rm[-2,-1]
49853
49854    ('"<table><tr><td><img src=\""${image_name}_A.png"\" /></td></tr><tr><td>"\
49855      "<img src=\""${image_name}_B.png"\" /></td></tr></table></td></tr></table>\n"')
49856    if $7 ('"<p><b>Additional comments:</b><br/><textarea cols=\"80\" rows=\"10\"
49857             placeholder=\"Enter comments here...\"></textarea></p>\n"') fi
49858    ('"<p><b>Starting point:</b> "$starting"\
49859                 <b>Orientation:</b> "$label" by "$label"</p>\n"')
49860    rm[1]
49861
49862    # Output geometry.
49863    ('"<table cellspacing=\"0\" cellpadding=\"4\" border=\"1\">\n"')
49864
49865    _fx_intarsia[0] $5,$6,0
49866    +l[0]
49867      s y
49868      repeat $! l[$>]
49869        if $>%2 mirror. x fi
49870        im={im} compress_rle 0,0 rows 6,100%
49871        ('"<tr><td valign=\"top\"><b>"$label" "{1+$>}"</b></td><td valign=\"top\">"${dir$dir}"</td><td>\n"')
49872        i=0 n=0 do
49873          val={0,i[$i]} i+=1
49874          if $val>=0 occ=1
49875          else
49876            occ={-$val}
49877            val={0,i[$i]}
49878            if $val<0 val=0 else i+=1 fi
49879          fi
49880          val+=$im
49881          ('"colour:<b>"$val"</b> "$occ')
49882          if {0,$i<h} ('", "') fi
49883          n+=1
49884          if !($n%8) ('"<br/>\n"') fi
49885        while $i<h#0
49886        ('"</td></tr>\n"')
49887        rm[0] a x
49888        dir={!$dir}
49889      endl done
49890      a x
49891    endl
49892    rm[0]
49893
49894    ('"</table>\n</font></center>\n</body>"')
49895    a x ot "$1/$2"
49896    rm
49897  endl done
49898
49899fx_intarsia_preview :
49900  to_rgb repeat $! l[$>]
49901    if max(w,h)>$3 rr2d $3,$3,0 fi
49902    to_rgba
49903    _fx_intarsia $5,$6,0
49904    100%,100%,1,1,'if(y%2,y*w+w-1-x,y*w+x)<$8*wh/100' *
49905    if min(w,h)<140 rr2d 140,140,1,1 fi
49906    expand_xy 16,0
49907
49908    100%,100% circle. 16,16,1%,1,1
49909    arrow3d. 0,0,0,{w/4},0,0,2%,15%,10% col3d. 1 j3d.. .,16,16,0,1,2,0,0 rm.
49910    +dilate. 3 r.. 100%,100%,1,3,0,0,0,0,0,0.5 a[-2,-1] c *. 255
49911    blend alpha
49912
49913    _fx_intarsia $5,$6,1
49914  endl done
49915
49916_fx_intarsia :
49917  if $3" && "$2 transpose fi
49918  if $1==0 # Start from top left.
49919  elif $1==1 # Start from top right.
49920    mirror x
49921  elif $1==2 # Start from bottom left.
49922    mirror y
49923  elif $1==3 # Start from bottom right.
49924    mirror xy
49925  fi
49926  if !$3" && "$2 transpose fi
49927
49928#@gui Sample Image : fx_image_sample, fx_image_sample_preview
49929#@gui : Input = choice{"Random","Apples","Balloons","Barbara","Boats","Bottles","Butterfly","Cameraman","Car","Cat",
49930#@gui : "Chick","Cliff","Colorful","David","Dog","Duck","Eagle","Elephant","Earth","Flower","Fruits",
49931#@gui : "Gmicky (Deevad)","Gmicky (Mahvin)","Gmicky & Wilber","Greece","Gummy","House","Inside","Landscape","Leaf",
49932#@gui : "Lena","Leno","Lion","Mandrill","Mona Lisa","Monkey","Parrots","Pencils","Peppers","Portrait0","Portrait1",
49933#@gui : "Portrait2","Portrait3","Portrait4","Portrait5","Portrait6","Portrait7","Portrait8","Portrait9",
49934#@gui : "Roddy","Rooster","Rose","Square","Swan","Teddy","Tiger","Tulips","Wall","Waterfall","Zelda"}
49935#@gui : note = note("<small>Choosing <b>0</b> for parameters <i>Width</i> or <i>Height</i> means <i>Automatic</i>.
49936#@gui : </small>")
49937#@gui : Width = _int(0,0,1024)
49938#@gui : Height = _int(0,0,1024)
49939#@gui : sep = separator()
49940#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/16/01</i>.</small>")
49941fx_image_sample :
49942  if $1 sp {$1-1},$2,$3 else sp ?,$2,$3 fi
49943  mv. 0
49944
49945fx_image_sample_preview :
49946  w={w} h={h} rm
49947  fx_image_sample $1,{$w>$h?$w:0},{$h>$w?$h:0}
49948
49949#@gui Solve Maze : fx_solve_maze, fx_solve_maze_preview(1)
49950#@gui : Starting Point (%) = point(5,5)
49951#@gui : Ending Point (%) = point(95,95)
49952#@gui : Smoothness = float(0.1,0,1)
49953#@gui : Thickness = int(3,1,10)
49954#@gui : Color = color(255,0,0)
49955#@gui : Maze Type = choice("Dark Walls","White Walls")
49956#@gui : sep = separator()
49957#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/01/09</i>.</small>")
49958fx_solve_maze :
49959  repeat $!
49960    +norm. >=. 50%
49961    if !$10 negate. fi
49962    *. 255 +b. $5% *.. 1e10 +[-2,-1]
49963    minimal_path. $1%,$2%,0,$3%,$4%,0
49964    pointcloud. 0 dilate. $6 r. ..,..,1,1,0
49965    to_rgba.
49966    replace_color. 0,0,1,1,1,255,${7-9},255
49967    replace_color. 0,0,0,0,0,255,0,0,0,0
49968    ellipse. $1%,$2%,5,5,0,1,${7-9},255
49969    ellipse. $3%,$4%,5,5,0,1,${7-9},255
49970    rv[-2,-1]
49971  mv[-2,-1] 0 done
49972
49973fx_solve_maze_preview :
49974  drgba
49975  line $1%,$2%,$3%,$4%,1,0xCCCCCCCC,${7-9}
49976  ellipse $1%,$2%,3,3,0,1,${7-9}
49977  ellipse $1%,$2%,3,3,0,1,0x1,0
49978  ellipse $3%,$4%,3,3,0,1,${7-9}
49979  ellipse $3%,$4%,3,3,0,1,0x1,0
49980
49981#@gui _
49982
49983#---------------------------------
49984#
49985#@cli :: Additional Gallery Images
49986#
49987#---------------------------------
49988#@cli _gallery_arrays
49989#@cli : This entry defines some examples of array filters for the G'MIC gallery page.
49990#@cli : $ image.jpg fx_frame_blur 30,30,0,5,0,0,128,128,128,0,5,255,255,255,2,2,1,0,0.5,0.5,0 _label="Frame~[blur]"
49991#@cli : $ sample tiger,leno,monkey,duck,eagle frame 3,3,0 frame 3,3,255 montage A _label="Montage"
49992#@cli : $ image.jpg fx_puzzle 5,5,0.5,0,0,0.3,100,0.2,255,100,0,0,0,0,0,0 _label="Puzzle"
49993
49994#@cli _gallery_artistic
49995#@cli : This entry defines some examples of artistic filters for the G'MIC gallery page.
49996#@cli : $ image.jpg fx_engrave 0.5,4,0,7.68,15.2,0,0,1,10,1,0,0,0,1,0 gui_merge_layers _label="Engrave"
49997#@cli : $ image.jpg fx_bokeh 3,8,0,30,8,4,0.3,0.2,210,210,80,160,0.7,30,20,20,1,2,170,130,20,110,0.15,0 _label="Bokeh"
49998#@cli : $ image.jpg fx_8bits 25,800,16,0 _label="Oldschool~8bits"
49999#@cli : $ image.jpg fire_edges 0.7,0.25,0.5,25,20 _fps=6 _label="Edges~on~fire"
50000#@cli : $ image.jpg fx_diffusiontensors 10,5,3,1,0.15,1,0,3,0 _label="Diffusion~tensors"
50001#@cli : $ image.jpg fx_dreamsmooth 3,1,1,0.8,0,0.8,1,24,0 _label="Dream~smoothing"
50002#@cli : $ image.jpg fx_feltpen 300,50,1,0.1,20,5,0 _label="Felt~pen"
50003#@cli : $ image.jpg gtutor_fpaint 0.5,0.5,0,0,45,0.5,0.5,0.5,0 _label="Finger~paint"
50004#@cli : $ image.jpg fx_graphic_novelfxl 0,2,6,5,20,0,0.62,14,0,1,0.5,0.78,1.92,0,0,0,1,1,1,0.5,0.8,1.28 \
50005# _label="Novel~FX"
50006#@cli : $ image.jpg fx_illustration_look 100,0,0,0,0 _label="Illustration~look"
50007#@cli : $ image.jpg fx_lylejk_painting 10,2,4,10,0 _label="Lylejk~painting"
50008#@cli : $ image.jpg fx_painting 5,2.5,1.5,50,1,0 _label="Painting"
50009#@cli : $ image.jpg fx_posterize 150,30,1,6,0,0,1,0 _label="Posterize"
50010#@cli : $ image.jpg fx_quadtree 2,1024,1.05,0,2.33,0.68,0.39,1,0 _label="Quadtree~variations"
50011#@cli : $ image.jpg fx_vector_painting 9.37,0 _label="Vector~painting"
50012
50013#@cli _gallery_blackandwhite
50014#@cli : This entry defines some examples of black-and-white filters for the G'MIC gallery page.
50015#@cli : $ image.jpg fx_freaky_bw 90,20,0,0,0,0 _label="Freaky~B&amp;W"
50016#@cli : $ image.jpg fx_engrave 0.5,50,0,8,40,0,0,0,10,1,0,0,0,1,0 _label="Engrave"
50017#@cli : $ image.jpg fx_gcd_layeretch 11,4,12,0.12,100,8.5,5,0,0,3,1,1,0 _label="Multi-layer~etch"
50018#@cli : $ image.jpg fx_pencil_portraitbw 30,120,1,0.5,144,79,21,0 _label="Pencil~portrait"
50019#@cli : $ image.jpg fx_gcd_etch 125,153,171,185,0.1,50,80,50,10,15,12,20,0,1,0.3,1,0,0 _label="Threshold~etch"
50020
50021#@cli _gallery_colors
50022#@cli : This entry defines some examples of color filters for the G'MIC gallery page.
50023#@cli : $ image.jpg fx_color_abstraction 1,10,0.2,0 _label="Color~abstraction"
50024#@cli : $ image.jpg fx_boost_chroma 90,0,0 _label="Boost~chromaticity"
50025#@cli : $ image.jpg fx_retrofade 20,6,40,0 _label="Retro~fade"
50026#@cli : $ image.jpg fx_tk_vintage 2,0.85,0.7,80,200,5,147,26,161,0.3,235,220,176,0.4,190,181,108,0.2,\
50027# 0,0,100,0,0.3,25,0,0 _label="Vintage~style"
50028
50029#@cli _gallery_deformations
50030#@cli : This entry defines some examples of deformation filters for the G'MIC gallery page.
50031#@cli : $ image.jpg animate "flower","30,10,0,0","30,10,0,360",10 rm. _fps=6 _label="flower"
50032#@cli : $ image.jpg fx_conformal_maps 8,1,0,"((1.1 + i*z/6)/(1.04 - i*z/6))^6.2",0,0,0,0,0,3,0,0,"1024","1024" \
50033# _label="Conformal~maps"
50034#@cli : $ image.jpg souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,1,1,0,0,0,0,0,1,0,0 \
50035# _label="Continuous~droste"
50036#@cli : $ image.jpg fx_crease 30,10,3 _label="Crease"
50037#@cli : $ image.jpg fx_distort_lens 0.29,0,0.23,50,50,0,0 _label="Distort~lens"
50038#@cli : $ image.jpg fx_drop_water 0,20,2,80,0,3,35,10,1,0.5,0.25,0.5,0.75,0.05,0.15,1 gui_merge_layers \
50039# _label="Drop~water"
50040#@cli : $ image.jpg fx_reflect 50,1,110,160,190,64,0,1.5,0,-3.3,7,1.5 _label="Reflection"
50041#@cli : $ image.jpg fx_square_circle 0,1,0,0,0,0,0,0 _label="Square~to~circle"
50042#@cli : $ image.jpg fx_textured_glass 40,40,1,1,0,2,0,0 _label="Textured~glass"
50043#@cli : $ sample lena,leno,320 morph 40 _fps=5 _label="morph"
50044
50045#@cli _gallery_filtering
50046#@cli : This entry defines some examples of filters for the G'MIC gallery page.
50047#@cli : $ image.jpg fx_gcd_crt 1.8,1.8,0,0 equalize 256 _label="CRT~sub-pixels"
50048#@cli : $ image.jpg fx_dirty 30,1,0,0,0 _label="Dirty"
50049#@cli : $ image.jpg fx_freaky_details 2,10,1,11,0,32,0 _label="Freaky~details"
50050#@cli : $ image.jpg jeje_normalize_local_variance 50,5,5,1,0,0 _label="Local~variance~normalization"
50051#@cli : $ image.jpg fx_mighty_details 25,1,25,1,11,0 _label="Mighty~details"
50052
50053#@cli _gallery_patterns
50054#@cli : This entry defines some examples of pattern filters for the G'MIC gallery page.
50055#@cli : $ image.jpg fx_rain 65,10,50,0.1,1,1,0 gui_merge_layers _label="Rain~&amp;~snow"
50056#@cli : $ 400,400,1,3 fx_camouflage 9,12,100,30,46,33,75,90,65,179,189,117,255,246,158 _label="Camouflage"
50057#@cli : $ image.jpg jeje_clouds 50,0.5 _label="Clouds"
50058#@cli : $ image.jpg fx_crystal 50,0.2,20,0 _label="Crystal"
50059#@cli : $ 400,400,1,3 fx_crystal_background 10,25,0,100,1 _label="Crystal~background"
50060#@cli : $ image.jpg fx_marble 0.5,1,0,0,0.4,0.6,0.6,1.1,0,100 _label="Marble"
50061#@cli : $ image.jpg fx_mineral_mosaic 1,2,1,100,0 _label="Mineral~mosaic"
50062#@cli : $ image.jpg fx_shapes 1,16,10,2,5,106.8,2,0,0,1,0 _label="Op~art"
50063#@cli : $ 400,400,1,3 fx_satin 20,1,0,0,0,0,255,255,255,255,255,0,0,0,-50,0,0 _label="Satin"
50064#@cli : $ 400,400,1,3 fx_seamless_turbulence 15,20,0,1,3,1 _label="Seamless~turbulence"
50065#@cli : $ image.jpg fx_shockwaves 10,10,20,0,0 _label="Shock~waves"
50066#@cli : $ 400,400,1,3 fx_equation_parametric "sin(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)",\
50067# "cos(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)",0,100,4096,1,0,64,0,0,128,0,0,1,1,1 _label="Equation~plot~[parametric]"
50068#@cli : $ 400,400,1,3 KittyRings 30,8,0,1,113,0,113,0,255,0 _label="Kitaoka~spin~illusion"
50069#@cli : $ 400,400,1,3 fx_neon_lightning 50,50,0,50,50,100,50,0.7,3,130,80,50,0.25,0 _label="Neon~lighting"
50070#@cli : $ image.jpg fx_lava 8,5,3,0,0 _label="Lava"
50071#@cli : $ sample monkey,lion,monkey 100%,100% plasma. equalize. 256 transition[0,1,2] [3],10 rm. _fps=10 \
50072# _label="transition"
50073#@cli : $ image.jpg fx_shapeism 2,7,0.38,0,1,5,32,8,3,1,5,0.5,1,0,0,0,255 _label="Shapeism"
50074
50075#@cli _gallery_3dmeshes
50076#@cli : This entry defines some examples of 3D rendering filters for the G'MIC gallery page.
50077#@cli : $ sample leno,lion,leno resize 400,400 transition3d 20,5,5 rm. _fps=10 _label="transition3d"
50078#@cli : $ 256,192 fx_text_pointcloud3d 64,"G'MIC","Rocks!",1,200,220,255,255,255,255,255,2,2,1,19 _fps=10 \
50079# _label="3D~text~pointcloud"
50080
50081#@cli _gallery_stylization
50082#@cli : This entry defines some examples of image stylization for the G'MIC gallery page.
50083#@cli : $ sample car _fx_stylize starrynight _output_mode=1 \
50084# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 _label="from~Van~Gogh:~Starry~Night"
50085#@cli : $ sample car _fx_stylize graytree _output_mode=1 \
50086# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,20,2,1.85,0 _label="from~Mondrian:~Gray~Tree"
50087#@cli : $ sample car _fx_stylize yellowredblue _output_mode=1 \
50088# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 \
50089# _label="from~Kandinsky:~Yellow-Red-Blue"
50090#@cli : $ sample car _fx_stylize littlebayatlaciotat _output_mode=1 \
50091# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,2,2,1.85,0 \
50092# _label="from~Braque:~Little~Bay~at~La~Ciotat"
50093#@cli : $ sample car _fx_stylize leviaducalestaque _output_mode=1 \
50094# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,0,2,1.85,0 \
50095# _label="from~Braque:~Le~Viaduc~a~l'Estaque"
50096#@cli : $ sample car _fx_stylize greatwave _output_mode=1 \
50097# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Hokusai:~The~Great~Wave"
50098#@cli : $ sample elephant input ../img/hatching.png _output_mode=1 \
50099# +fx_stylize 1,4,0,0,1,2,3,0.5,0.1,3,3,0,0.7,0,0,1,0,5,5,7,1,30,0,2,1.85,0 _label="from~Hatch~Drawing"
50100#@cli : $ sample cat input ../img/hatching.png _output_mode=1 \
50101# +fx_stylize 1,4,0,0,1,2,3,0.5,0.1,3,3,0,0.7,0,0,1,0,5,5,7,1,30,0,2,1.85,0 _label="from~Hatch~Drawing"
50102#@cli : $ sample bottles _fx_stylize starrynight _output_mode=1 \
50103# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Van~Gogh:~Starry~Night"
50104#@cli : $ sample cat _fx_stylize summertime9a _output_mode=1 \
50105# +fx_stylize 1,6,0,0,2,0,1,0.5,0.1,3,3,0,0.7,0,2,1,0,5,5,7,1,130,100,2,1.85,0 _label="from~Pollock:~Summertime~No~9A"
50106#@cli : $ sample cat _fx_stylize greatwave _output_mode=1 \
50107# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Hokusai:~The~Great~Wave"
50108#@cli : $ sample dog _fx_stylize convergence _output_mode=1 \
50109# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,50,2,1.85,0 _label="from~Pollock:~Convergence"
50110#@cli : $ sample dog _fx_stylize irises _output_mode=1 \
50111# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,1,5,0,0.7,1,0,1,0,5,5,7,1,30,200,2,1.85,0 _label="from~Van~Gogh:~Irises"
50112#@cli : $ sample mandrill _fx_stylize themandola _output_mode=1 \
50113# +fx_stylize 1,5,0,0,0,3,1,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 _label="from~Braque:~The~Mandola"
50114#@cli : $ sample square _fx_stylize orientalpleasuregardenanagoria _output_mode=1 \
50115# +fx_stylize 1,6,0,0,0.52,0.5,3,0.14,0.1,3,3,0,0.7,3.39,0,1,0,5,5,7,5,30,40,2,1.85,0 \
50116# _label="from~Klee:~Oriental~Pleasure~Garden~Anagoria"
50117#@cli : $ sample monalisa _fx_stylize squareswithconcentriccircles _output_mode=1 \
50118# +fx_stylize 1,4,0,0,0.15,3,2,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 \
50119# _label="from~Kandisnky:~Squares~with~Concentric~Circles"
50120#@cli : $ sample monalisa _fx_stylize inthestyleofkairouan _output_mode=1 \
50121# +fx_stylize 1,4,2,0,0.15,3,2,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,50,2,1.85,0 \
50122# _label="from~Klee:~In~the~Style~of~Kairouan"
50123#@cli : $ sample square _fx_stylize polyphony2 _output_mode=1 \
50124# +fx_stylize 1,6,0,0,0.15,3,2,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 _label="from~Klee:~Polyphony~2"
50125#@cli : $ sample square _fx_stylize wheatstacksendofsummer _output_mode=1 \
50126# +fx_stylize 1,6,0,0,0.15,3,2,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 \
50127# _label="from~Monet:~Wheatstacks~-~End~of~Summer"
50128#@cli : $ sample square _fx_stylize portraitdemetzinger _output_mode=1 \
50129# +fx_stylize 1,5,0,0,0.1,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,500,2,1.85,0 \
50130# _label="from~Delaunay:~Portrait~de~Metzinger"
50131#@cli : $ sample monalisa input ../img/mandelbrot.jpg _output_mode=1 \
50132# +fx_stylize 1,3,3,0,0.15,4,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,50,2,1.85,0 _label="from~Mandelbrot~Fractal~Set"
50133#@cli : $ sample bottles _fx_stylize redtree _output_mode=1 \
50134# +fx_stylize 1,5,0,0,2.12,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,5,2,1.85,0 \
50135# _label="from~Mondrian:~Evening;~Red~Tree"
50136#@cli : $ sample bottles _fx_stylize redwaistcoat _output_mode=1 \
50137# +fx_stylize 1,4,0,0,0.67,3.17,3,0.5,0.06,3,3,0,0.7,5,0,2,0,5,5,7,1,30,0,2.05,1.85,0 \
50138# _label="from~Klee:~Red~Waistcoat"
50139#@cli : $ sample bottles _fx_stylize reservoirhortadeebro _output_mode=1 \
50140# +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 _label="from~Picasso:~The~Reservoir"
50141#@cli : $ sample bottles _fx_stylize almondblossom _output_mode=1 \
50142# +fx_stylize 1,6,0,0,0,3,3,0.5,0.1,3,3,0,0.7,5,0,2,0,5,5,7,1,30,10,2,1.85,0 _label="from~Van~Gogh:~Almond~Blossom"
50143#@cli : $ sample bottles _fx_stylize landscapenearantwerp _output_mode=1 \
50144# +fx_stylize 1,6,0,0,2.17,3.65,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 \
50145# _label="from~Braque:~Landscape~near~Antwerp"
50146#@cli : $ sample bottles _fx_stylize wheatfieldwithcrows _output_mode=1 \
50147# +fx_stylize 1,6,0,0,3.86,2,3,0.5,0.1,3,3,0,0.7,3.35,1,1,0,5,5,7,1,30,10,5,1.85,0 \
50148# _label="from~Van~Gogh:~Wheat~Field~with~Crows"
50149
50150#@cli _gallery_codesamples
50151#@cli : This entry defines some examples of coding fun filters for the G'MIC gallery page.
50152#@cli : $ https://gmic.eu/samples/lissajous.gmic go _fps=10 _label="Lissajous"
50153#@cli : $ https://gmic.eu/samples/torus3d.gmic go _fps=10 _label="3D~torus"
50154#@cli : $ https://gmic.eu/samples/pacman.gmic go _fps=25 _label="Pacman"
50155#@cli : $ https://gmic.eu/samples/scrolling.gmic go _fps=25 _label="Scrolling"
50156#@cli : $ https://gmic.eu/samples/landscape.gmic go _fps=12 _label="Landscape"
50157#@cli : $ https://gmic.eu/samples/mandelbrot.gmic go _fps=8 _label="Mandelbrot"
50158#@cli : $ https://gmic.eu/samples/heart.gmic go _fps=15 _label="Heart"
50159#@cli : $ https://gmic.eu/samples/distortion.gmic go _fps=20 _label="Distortion"
50160#@cli : $ https://gmic.eu/samples/rotozoom.gmic go _fps=15 _label="Rotozoom"
50161
50162#---------------------------
50163# Test programs for NN lib.
50164#---------------------------
50165
50166#------------------------------
50167# Test program for FC networks.
50168#------------------------------
50169nn_test_fc : check "${1=1e-4}>0 && isbool(${2=1})"
50170
50171  # Build neural network.
50172  #----------------------
50173  nn_init
50174  nn_layer_input X,2
50175  nn_layer_batchnorm BN,X
50176  nn_layer_fcnl FC1,BN,100
50177  nn_layer_fcnl FC2,FC1,50
50178  nn_layer_fcnl FC3,FC2,50
50179  nn_layer_fcnl FC4,FC3,30
50180  nn_layer_fcnl FC5,FC4,30
50181  nn_layer_fcnl FC6,FC5,3
50182  nn_layer_rename OUT,FC6
50183  nn_loss_mse L,OUT,Y,$1,$2
50184  sp lena,128 nm. img
50185
50186  # Optimize network.
50187  #-------------------
50188  size_batch=1024
50189  repeat inf,_nn_iter
50190    e[] "ITER = "$_nn_iter
50191
50192    # Build mini-batch.
50193    $size_batch,1,1,5,"
50194      const ind = $img;
50195      px = u(0,w#ind)%w#ind;
50196      py = u(0,h#ind)%h#ind;
50197      val = I(#$img,px,py,0,0,1);
50198      [ px,py,val ]"
50199    nm. batch
50200
50201    # Mini-batch stochastic gradient descent.
50202    _nn_batch_iteration=0
50203    _nn_is_training=1
50204    repeat 128,batch_iter
50205      eval[batch] *${-nn_lib}"
50206        batch = I;
50207        X = batch[0,2]; # Input
50208        Y = batch[2,3]; "\ # Ground-truth
50209        ${_nn_forward}${_nn_backward}${_nn_update}"
50210        end(
50211          !($batch_iter%20)?print([L_out,nn_learning_rate,L_trend,L_trend_moment]);
50212        ); I"
50213    done
50214    rm[batch]
50215
50216    # Render image from network.
50217    _nn_is_training=0
50218    {img,[w,h]},1,3,*${-nn_lib}"
50219      X = [ x,y ];
50220      "${_nn_forward}"
50221      cut(OUT_out,0,255)"
50222#    on. frame.png,$_nn_iter
50223    w. 600,600 rm.
50224
50225  done
50226
50227#--------------------------------
50228# Test program for CONV networks.
50229#--------------------------------
50230nn_test_conv :
50231
50232  # Build neural network.
50233  #----------------------
50234  nn_init
50235  nn_layer_input X,32,32,1,3
50236  nn_layer_conv2dnl L1,X,3
50237  nn_layer_rename OUT,L1
50238  nn_loss_mse L,OUT,Y
50239  sp lena,32 nm. img
50240  [img] nm. res
50241
50242  # Optimize network.
50243  #-------------------
50244  size_batch=1
50245  repeat inf,_nn_iter
50246    e[] "ITER = "$_nn_iter
50247
50248    _nn_batch_iteration=0
50249    repeat 10,batch_iter
50250      eval ${-nn_lib}"
50251        X = crop(#$img); # Input
50252        Y = crop(#$res); # Ground-truth
50253        "${_nn_forward}${_nn_backward}${_nn_update}"
50254        store(X,'vX',32,32,1,3);
50255        store(Y,'vY',32,32,1,3);
50256        store(OUT_out,'vOUT',32,32,1,3);
50257        run('l[] $vOUT r2dx 200,1 a x w. rm. endl');
50258
50259        end(
50260          print([L_out,nn_learning_rate]);
50261        ); I"
50262    done
50263  done
50264
50265# nn_layer_res : name,in
50266# Define a custom residual CONV module.
50267nn_layer_res : nn_check_layer "$2" check ${"is_variable_name $1"}" && isint(${3=16}) && $3>0"
50268  nn_layer_clone $1_CL0,$1_CL1,$2
50269  nn_layer_conv2dnl $1_CONV1,$1_CL0,$3
50270  nn_layer_conv2d $1_CONV2,$1_CONV1,{[$_nn_$2_out_size][3]}
50271  nn_layer_add $1_ADD,$1_CONV2,$1_CL1
50272  nn_layer_nl $1,$1_ADD
50273
50274nn_layer_res2 : nn_check_layer "$2" check ${"is_variable_name $1"}" && isint(${3=16}) && $3>0"
50275  nn_layer_clone $1_CL0,$1_CL1,$2
50276  nn_layer_conv2dnl $1_CONV1,$1_CL0,$3
50277  nn_layer_conv2dnl $1_CONV2,$1_CONV1,$3
50278  nn_layer_conv2d $1_CONV3,$1_CONV2,{[$_nn_$2_out_size][3]}
50279  nn_layer_add $1_ADD,$1_CONV3,$1_CL1
50280  nn_layer_nl $1,$1_ADD
50281
50282# Main test procedure.
50283nn_test : skip ${1=1e-4}
50284
50285  # Build neural network.
50286  #----------------------
50287  if isfile('network.gmz') nn_load network.gmz
50288  else
50289    nn_init
50290    nn_layer_input X,32,32,1,1
50291    nn_unet
50292    nn_loss_mse L,OUT,Y,$1
50293  fi
50294
50295  l[] images.gmz s z luminance n 0,255 +mirror x +mirror y  a z endl nm. res
50296  +l[res] s c noise 0 c 0,255 a z endl nm. img
50297
50298  if isfile('stats.gmz') stats.gmz else 0 fi nm. stats
50299
50300  _nn_optimizer=2
50301
50302  # Optimize network.
50303  #------------------
50304  size_batch=1
50305  repeat inf,iter
50306    e[] "ITER = "$iter
50307
50308    srand 100
50309
50310    # Build mini-batch.
50311    32,32,$size_batch,1 nm. batch_X
50312    32,32,$size_batch,1 nm. batch_Y
50313    1,1,$size_batch,1,">
50314      const img = $img;
50315      const res = $res;
50316      const siz = 32*32;
50317      p = u<0.15?0:30;
50318      do (
50319        ni = int(u(d#img))%d#img;
50320        nx = int(u(0,w#img - 1 - 32));
50321        ny = int(u(0,h#img - 1 - 32));
50322        X = crop(#img,nx,ny,0,0,32,32,1,1);
50323        Y = crop(#res,nx,ny,0,0,32,32,1,1);
50324      _(while); std(Y)<p);
50325      draw(#$batch_X,X,0,0,z,0,32,32,1,1);
50326      draw(#$batch_Y,Y,0,0,z,0,32,32,1,1)"
50327    rm.
50328
50329    # Mini-batch stochastic gradient.
50330    _nn_is_training=1
50331    _nn_batch_iteration=0
50332    repeat 10,batch_iter
50333      1,1,{batch_X,d},1,*${-nn_lib}"
50334        X = crop(#$batch_X,0,0,z,0,32,32,1,1);
50335        Y = crop(#$batch_Y,0,0,z,0,32,32,1,1);
50336        "${_nn_forward}"
50337        "${_nn_backward}"
50338        "${_nn_update}"
50339        z<4 && !("$batch_iter"%10)?(
50340          var = string(#256,'IN',t);
50341          store(X,var,32,32,1,1);
50342          var = string(#256,'OUT',t);
50343          store(OUT_out,var,32,32,1,1);
50344          var = string(#256,'REF',t);
50345          store(Y,var,32,32,1,1);
50346          run('l[] $IN',t,' $OUT',t,' $REF',t,' c 0,255 a x w',z+1,'. 600,200,0 rm endl');
50347        );
50348        end(
50349          print([ sqrt(L_out),nn_learning_rate ]);
50350          set(L_out,'loss');
50351          set(nn_learning_rate,'nn_learning_rate');
50352        ); I"
50353      rm.
50354    done
50355    rm[batch_X,batch_Y]
50356
50357    if !($iter%20) nn_save network.gmz fi
50358    l[stats]
50359      ($loss^$nn_learning_rate)
50360      a x
50361      o stats.gmz
50362    endl
50363
50364    # Visualize convolution kernels.
50365#    +z[CONV1_0] 0,{CONV1_0,w-2} l. s y n 0,255 repeat $! l[$>] s x,-9 N=$! r {v=sqrt(w);[v,v]},1,1,-1
50366# r2dx 128,1 frame 1,1,0 frame 2,2,200 append_tiles $N endl done a y w6. rm endl
50367
50368  done
50369
50370
50371nn_foo :
50372  l[]
50373    nn_init
50374    network.gmz
50375    nn_layer_input X,64,64,1,1
50376    nn_unet
50377    store network
50378  endl
50379  repeat $! l[$>]
50380    w,h,s={[w,h,s]}
50381
50382    # Extract overlapping 64x64 tiles.
50383    nx,ny={ceil([w,h]/60)}
50384    64,64,{$nx*$ny},$s
50385    1,1,{d},1,"*
50386      const boundary = 1;
50387      ix = z%$nx;
50388      iy = int(z/$nx);
50389      X = round(lerp(-1,$w - 64,ix/($nx-1)));
50390      Y = round(lerp(-1,$h - 64,iy/($ny-1)));
50391      V = crop(#0,X,Y,0,0,64,64,1,"$s");
50392      draw(#-1,V,0,0,z,0,64,64,1,"$s")"
50393    k..
50394
50395    # Denoise 64x64 tiles.
50396    $network
50397    1,1,{0,d},1,*${-nn_lib}${_nn_init_forward}"
50398    repeat ("$s",c,
50399      X = crop(#0,0,0,z,c,64,64,1,1);"${_nn_forward}"
50400      draw(#0,OUT_out,0,0,z,c,64,64,1,1);
50401    )"
50402    k[0]
50403
50404    # Reconstruct image from tiles.
50405    $w,$h,1,{$s+1}
50406    1,1,{0,d},1,">begin(one = vector(#(64 - 2)*(64 - 2),1));
50407      const boundary = 1;
50408      ix = z%$nx;
50409      iy = int(z/$nx);
50410      X = round(lerp(-1,$w - 64,ix/($nx-1)));
50411      Y = round(lerp(-1,$h - 64,iy/($ny-1)));
50412      V = crop(#0,1,1,z,0,64 - 2,64 - 2,1,"$s");
50413      draw(#1,V,X + 1,Y + 1,0,0,64 - 2,64 - 2,1,"$s",-1);
50414      draw(#1,one,X + 1,Y + 1,0,"$s",64 - 2,64 - 2,1,1,-1)"
50415    k..
50416    s c,-$s
50417    max. 1 /
50418    c 0,255
50419
50420  endl done
50421
50422nn_unet :
50423  nn_layer_conv2dnl lA,X,64
50424  nn_layer_conv2dnl lB,lA,64
50425  nn_layer_clone lB0,lB1,lB
50426
50427  nn_layer_maxpool2d lC,lB0
50428  nn_layer_conv2dnl lD,lC,128
50429  nn_layer_conv2dnl lE,lD,128
50430  nn_layer_clone lE0,lE1,lE
50431
50432  nn_layer_maxpool2d lF,lE0
50433  nn_layer_conv2dnl lG,lF,256
50434  nn_layer_conv2dnl lH,lG,256
50435
50436  nn_layer_upsample2d lI,lH,1
50437  nn_layer_append lJ,lI,lE1
50438  nn_layer_conv2dnl lK,lJ,128
50439  nn_layer_conv2dnl lL,lK,128
50440
50441  nn_layer_upsample2d lM,lL,1
50442  nn_layer_append lN,lM,lB1
50443  nn_layer_conv2dnl lO,lN,64
50444  nn_layer_conv2dnl lP,lO,64
50445  nn_layer_conv2d OUT,lP,1,1
50446
50447
50448nn_unet_small :
50449  nn_layer_conv2dnl lA,X,8
50450  nn_layer_conv2dnl lB,lA,8
50451  nn_layer_clone lB0,lB1,lB
50452
50453  nn_layer_maxpool2d lC,lB0
50454  nn_layer_conv2dnl lD,lC,16
50455  nn_layer_conv2dnl lE,lD,16
50456  nn_layer_clone lE0,lE1,lE
50457
50458  nn_layer_maxpool2d lF,lE0
50459  nn_layer_conv2dnl lG,lF,32
50460  nn_layer_conv2dnl lH,lG,32
50461
50462  nn_layer_upsample2d lI,lH,1
50463  nn_layer_append lJ,lI,lE1
50464  nn_layer_conv2dnl lK,lJ,16
50465  nn_layer_conv2dnl lL,lK,16
50466
50467  nn_layer_upsample2d lM,lL,1
50468  nn_layer_append lN,lM,lB1
50469  nn_layer_conv2dnl lO,lN,8
50470  nn_layer_conv2dnl lP,lO,8
50471  nn_layer_conv2d OUT,lP,1,1
50472
50473nn_resnet :
50474  nn_layer_conv2d lA,X,16
50475  nn_layer_res lB,lA,16
50476  nn_layer_res lC,lB,16
50477  nn_layer_res lD,lC,16
50478  nn_layer_res lE,lD,16
50479  nn_layer_conv2d lF,lE,1
50480  nn_layer_rename OUT,lF
50481
50482
50483# Local Variables:
50484# mode: sh
50485# End:
50486#
50487# (End of G'MIC custom commands)
50488