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"  - 'u': screen width (actually independent on the window size)."\n\
603"  - 'v': screen height (actually independent on the window size)."\n\
604"  - 'uv': screen width x screen 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"  - 'w': display width (i.e. width of the display area managed by the window)."\n\
609"  - 'h': display height (i.e. height of the display area managed by the window)."\n\
610"  - 'wh': display width x display height."\n\
611"  - 'i': X-coordinate of the display window."\n\
612"  - 'j': Y-coordinate of the display window."\n\
613"  - 'n': current normalization type of the instant display."\n\
614"  - 't': window title of the instant display."\n\
615"  - 'x': X-coordinate of the mouse position (or -1, if outside the display area)."\n\
616"  - 'y': Y-coordinate of the mouse position (or -1, if outside the display area)."\n\
617"  - 'b': state of the mouse buttons { 1=left-but. | 2=right-but. | 4=middle-but. }."\n\
618"  - 'o': state of the mouse wheel."\n\
619"  - 'k': decimal code of the pressed key if any, 0 otherwise."\n\
620"  - 'c': boolean (0 or 1) telling if the instant display has been closed recently."\n\
621"  - 'r': boolean telling if the instant display has been resized recently."\n\
622"  - 'm': boolean telling if the instant display has been moved recently."\n\
623"  - Any other 'feature' stands for a keycode name (in capital letters), and is substituted "\
624"by a boolean describing the current key state { 0=pressed | 1=released }."\n\
625"  - You can also prepend a hyphen '-' to a 'feature' (that supports it) to flush the "\
626"corresponding event immediately after reading its state (works for keys, mouse and window events)."\n\
627\n\
628"* Item substitution is __never__ performed in items between double quotes. One must break the quotes "\
629"to enable substitution if needed, as in '\"3+8 kg = \"{3+8}\" kg\"'. Using double quotes is then "\
630"a convenient way to disable the substitutions mechanism in items, when necessary."\n\
631"* One can also disable the substitution mechanism on items outside double quotes, by escaping the "\
632"`{`, `}` or `$` characters, as in `\\{3+4\\}\\ doesn\47t\\ evaluate`."
633
634  _section "Mathematical Expressions"
635  _text \
636"* \\G'MIC has an embedded __mathematical parser__, used to evaluate (possibly complex) math expressions "\
637"specified inside braces '{}', or formulas in commands that may take one as an argument (e.g. ''fill'' or ''eval'')."\n\
638"* When the context allows it, a formula is evaluated __for each pixel__ of the selected images "\
639"(e.g. ''fill'' or ''eval'')."\n\
640"* A math expression may return a __scalar__ or a __vector-valued__ result (with a fixed number of components)."\n\
641"The mathematical parser understands the following set of functions, operators and variables:"\n\
642"## Usual operators:"\n\
643"'||' (logical or), '&&' (logical and), '|' (bitwise or), '&' (bitwise and), "\
644"'!=', '==', '<=', '>=', '<', '>', '<<' (left bitwise shift), '>>' (right bitwise shift), '-', '+', '*', '/', "\
645"'%' (modulo), '^' (power), '!' (logical not), '~' (bitwise not), '++', '--', '+=', '-=', '*=', '/=', '%=', "\
646"'&=', '|=', '^=', '>>', '<<=' (in-place operators)."\n\
647"## Usual math functions:"\n\
648"'abs()', 'acos()', 'acosh()', 'arg()', 'arg0()', 'argkth()', 'argmax()', 'argmaxabs()', "\
649"'argmin()', 'argminabs()', 'asin()', 'asinh()', 'atan()', 'atan2()', 'atanh()', 'avg()', 'bool()', 'cbrt()', "\
650"'ceil()', 'cos()', 'cosh()', 'cut()', 'deg2rad()', 'erf()', 'erfinv()', 'exp()', 'fact()', 'fibo()', 'floor()', "\
651"'gauss()', 'gcd()', 'int()', 'isnan()', 'isnum()', 'isinf()', 'isint()', 'isbool()', 'isexpr()', 'isfile()', "\
652"'isdir()', 'isin()', 'kth()', 'log()', 'log2()', 'log10()', 'max()', 'maxabs()', 'med()', 'min()', 'minabs()', "\
653"'narg()', 'prod()', 'rad2deg()', 'rol()' (left bit rotation), 'ror()' (right bit rotation), 'round()', 'sign()', "\
654"'sin()', 'sinc()', 'sinh()', 'sqrt()', 'std()', 'srand(_seed)', 'sum()', 'tan()', 'tanh()', 'var()', 'xor()'."\n\
655\n\
656"* 'atan2(y,x)' is the version of 'atan()' with two arguments 'y' and 'x' (as in C/C++)."\n\
657"* 'permut(k,n,with_order)' computes the number of permutations of 'k' objects from a set of 'n' objects."\n\
658"* 'gauss(x,_sigma,_is_normalized)' returns `exp(-x^2/(2*s^2))/(is_normalized?sqrt(2*pi*sigma^2):1)`."\n\
659"* 'cut(value,min,max)' returns 'value' if it is in range '[min,max]', or 'min' or 'max' otherwise."\n\
660"* 'narg(a_1,...,a_N)' returns the number of specified arguments (here, 'N')."\n\
661"* 'arg(i,a_1,..,a_N)' returns the `i`-th argument 'a_i'."\n\
662"* 'isnum()', 'isnan()', 'isinf()', 'isint()', 'isbool()' test the type of the given number or expression, "\
663"and return '0' (false) or '1' (true)."\n\
664"* 'isfile(\\'path\\')' (resp. 'isdir('path')') returns '0' (false) or '1' (true) whether its string argument is a "\
665"path to an existing file (resp. to a directory) or not."\n\
666"* 'isvarname(\\'str\\')' returns '0' (false) or '1' (true) whether its string argument would be a valid to name "\
667"a variable or not."\n\
668"* 'isin(v,a_1,...,a_n)' returns '0' (false) or '1' (true) whether the first value 'v' appears in the set of other "\
669"values 'a_i'."\n\
670"* 'inrange(value,m,M,include_m,include_M)' returns '0' (false) or '1' (true) whether the specified value lies in "\
671"range '[m,M]' or not ('include_m' and 'includeM' tells how boundaries 'm' and 'M' are considered)."\n\
672"* 'argkth()', 'argmin()', 'argmax()', 'argminabs()', 'argmaxabs()'', 'avg()', 'kth()', 'min()', 'max()', 'minabs()', "\
673"'maxabs()', 'med()', 'prod()', 'std()', 'sum()' and 'var()' can be called with an arbitrary number of scalar/vector "\
674"arguments."\n\
675"* 'vargkth()', 'vargmin()', 'vargmax()', 'vargminabs()', 'vargmaxabs()', 'vavg()', 'vkth()', 'vmin()', "\
676"'vmax()', 'vminabs()', 'vmaxabs()', 'vmed()', 'vprod()', 'vstd()', 'vsum()' and 'vvar()' are the versions of the "\
677"previous function with vector-valued arguments."\n\
678"* 'round(value,rounding_value,direction)' returns a rounded value. 'direction' can be "\
679"{ -1=to-lowest | 0=to-nearest | 1=to-highest }."\n\
680"* 'lerp(a,b,t)' returns 'a*(1-t)+b*t'."\n\
681"* 'swap(a,b)' swaps the values of the given arguments."\n\
682"## Variable names:"\n\
683"Variable names below are pre-defined. They can be overridden."\n\
684"* 'l': length of the associated list of images."\n\
685"* 'k': index of the associated image, in '[0,l-1]'."\n\
686"* 'w': width of the associated image, if any ('0' otherwise)."\n\
687"* 'h': height of the associated image, if any ('0' otherwise)."\n\
688"* 'd': depth of the associated image, if any ('0' otherwise)."\n\
689"* 's': spectrum of the associated image, if any ('0' otherwise)."\n\
690"* 'r': shared state of the associated image, if any ('0' otherwise)."\n\
691"* 'wh': shortcut for width x height."\n\
692"* 'whd': shortcut for width x height x depth."\n\
693"* 'whds': shortcut for width x height x depth x spectrum (i.e. number of image values)."\n\
694"* 'im', 'iM', 'ia', 'iv', 'is', 'ip', 'ic', 'in': Respectively the minimum, maximum, average, variance, sum, "\
695"product, median value and L2-norm of the associated image, if any ('0' otherwise)."\n\
696"* 'xm', 'ym', 'zm', 'cm': The pixel coordinates of the minimum value in the associated image, "\
697"if any ('0' otherwise)."\n\
698"* 'xM', 'yM', 'zM', 'cM': The pixel coordinates of the maximum value in the associated image, "\
699"if any ('0' otherwise)."\n\
700"* All these variables are considered as __constant values__ by the math parser (for optimization purposes) "\
701"which is indeed the case most of the time. Anyway, this might not be the case, if function 'resize(#ind,..)' "\
702"is used in the math expression. If so, it is safer to invoke functions 'l()', 'w(_#ind)', 'h(_#ind)', ... 's(_#ind)' "\
703"and 'in(_#ind)' instead of the corresponding named variables."\n\
704"* 'i': current processed pixel value (i.e. value located at `(x,y,z,c)`) in the associated image, "\
705"if any ('0' otherwise)."\n\
706"* 'iN': N-th channel value of current processed pixel (i.e. value located at `(x,y,z,N)` in the associated image, "\
707"if any ('0' otherwise). 'N' must be an integer in range '[0,9]'."\n\
708"* 'R', 'G', 'B' and 'A' are equivalent to 'i0', 'i1', 'i2' and 'i3' respectively."\n\
709"* 'I': current vector-valued processed pixel in the associated image, if any ('0' otherwise). "\
710"The number of vector components is equal to the number of image channels (e.g. 'I' = `[ R,G,B ]` for a "\
711"`RGB` image)."\n\
712"* You may add '#ind' to any of the variable name above to retrieve the information for any "\
713"numbered image '[ind]' of the list (when this makes sense). For instance 'ia#0' denotes the average value of the "\
714"first image of the list)."\n\
715"* 'x': current processed column of the associated image, if any ('0' otherwise)."\n\
716"* 'y': current processed row of the associated image, if any ('0' otherwise)."\n\
717"* 'z': current processed slice of the associated image, if any ('0' otherwise)."\n\
718"* 'c': current processed channel of the associated image, if any ('0' otherwise)."\n\
719"* 't': thread id when an expression is evaluated with multiple threads ('0' means __master thread__)."\n\
720"* 'n': maximum number of threads when expression is evaluated in parallel (so that 't' goes from '0' to 'n-1')."\n\
721"* 'e': value of e, i.e. `2.71828...`."\n\
722"* 'pi': value of pi, i.e. `3.1415926...`."\n\
723"* 'u': a random value between '[0,1]', following a uniform distribution."\n\
724"* 'g': a random value, following a gaussian distribution of variance 1 (roughly in '[-6,6]')."\n\
725"* 'interpolation': value of the default interpolation mode used when reading pixel values with the pixel access "\
726"operators (i.e. when the interpolation argument is not explicitly specified, see below for more details on pixel "\
727"access operators). Its initial default value is '0'."\n\
728"* 'boundary': value of the default boundary conditions used when reading pixel values with the pixel access "\
729"operators (i.e. when the boundary condition argument is not explicitly specified, see below for more details "\
730"on pixel access operators). Its initial default value is '0'."\n\
731"* The last image of the list is always associated to the evaluations of 'expressions', e.g. G'MIC sequence "\
732"\n~~~\n256,128 fill {w}\n~~~\n will create a 256x128 image filled with value 256."\n\
733"## Vector calculus:"\n\
734"Most operators are also able to work with vector-valued elements."\n\
735"* '[a0,a1,...,aN-1]' defines a 'N'-dimensional vector with scalar coefficients 'ak'."\n\
736"* 'vectorN(a0,a1,,...,aN-1)' does the same, with the 'ak' being repeated periodically if only a few are specified."\n\
737"* 'vector(#N,a0,a1,,...,aN-1)' does the same, and can be used for any constant expression 'N'."\n\
738"* In previous expressions, the 'ak' can be vectors themselves, to be concatenated into a single vector."\n\
739"* The scalar element 'ak' of a vector 'X' is retrieved by 'X[k]'."\n\
740"* 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\
741"* 'expr(formula,_w,_h,_d,_s)' outputs a vector of size 'w*h*d*s' with values generated from "\
742"the specified formula, as if one were filling an image with dimensions '(w,h,d,s)'."\n\
743"* Equality/inequality comparisons between two vectors is done with operators '==' and '!='."\n\
744"* Some vector-specific functions can be used on vector values: "\
745"'cross(X,Y)' (cross product), 'dot(X,Y)' (dot product), 'size(X)' (vector dimension), "\
746"'sort(X,_is_increasing,_nb_elts,_size_elt)' (sorted values), 'reverse(A)' (reverse order of components), "\
747"'shift(A,_length,_boundary_conditions)' and 'same(A,B,_nb_vals,_is_case_sensitive)' (vector equality test)."\n\
748"* Function 'normP(u1,...,un)' computes the LP-norm of the specified vector ('P' being an `unsigned integer` constant "\
749"or 'inf'). If 'P' is omitted, the L2 norm is calculated."\n\
750"* Function 'resize(A,size,_interpolation,_boundary_conditions)' returns a resized version of a vector 'A' with "\
751"specified interpolation mode. 'interpolation' can be "\
752"{ -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos }, and "\
753"'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }."\n\
754"* Function 'resize(A,ow,oh,od,os,nw,_,nh,_nd,_ns,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac)' is an "\
755"extended version of the previous function. It allows to resize the vector 'A', seen as an image of size "\
756"'ow x oh x od x os' as a new image of size 'nw x nh x nd x ns', with specified resizing options."\n\
757"* Function 'find(A,B,_starting_index,_search_step)' returns the index where sub-vector 'B' appears in vector 'A', "\
758"(or '-1' if 'B' is not contained in 'A'). Argument 'A' can be also replaced by an image index '#ind'."\n\
759"* A `2`-dimensional vector may be seen as a complex number and used in those particular functions/operators: "\
760"'**' (complex multiplication), '//' (complex division), '^^' (complex exponentiation), "\
761"'**=' (complex self-multiplication), '//=' (complex self-division), '^^=' (complex self-exponentiation), "\
762"'cabs()' (complex modulus), 'carg()' (complex argument), 'cconj()' (complex conjugate), "\
763"'cexp()' (complex exponential), 'clog()' (complex logarithm),  'ccos()' (complex cosine), "\
764"'csin()' (complex sine), 'ctan()' (complex tangent), 'ccosh()' (complex hyperpolic cosine), "\
765"'csinh()' (complex hyperbolic sine) and 'ctanh()' (complex hyperbolic tangent)."\n\
766"* A `MN`-dimensional vector may be seen as a `M` x `N` matrix and used in those particular functions/operators: "\
767"'*' (matrix-vector multiplication), 'det(A)' (determinant), 'diag(V)' (diagonal matrix from a vector), "\
768"'eig(A)' (eigenvalues/eigenvectors), 'eye(n)' (n x n identity matrix), 'invert(A,_solver)' (matrix inverse), "\
769"'mul(A,B,_nb_colsB)' (matrix-matrix multiplication), 'pseudoinvert(A,_nb_colsA,_solver)', "\
770"'rot(u,v,w,angle)' (3D rotation matrix), 'rot(angle)' (2D rotation matrix), "\
771"'solve(A,B,_nb_colsB)' (solver of linear system A.X = B), 'svd(A,_nb_colsA)' (singular value decomposition), "\
772"'trace(A)' (matrix trace) and 'transpose(A,nb_colsA)' (matrix transpose). Argument 'nb_colsB' may be omitted if "\
773"it is equal to `1`".\n\
774"* 'mproj(S,nb_colsS,D,nb_colsD,method,max_iter,max_residual)' projects a matrix 'S' onto a dictionary (matrix) "\
775"'D'. Equivalent to command ''mproj'' but inside the math evaluator."\n\
776"* Specifying a vector-valued math expression as an argument of a command that operates on image values "\
777"(e.g. 'fill') modifies the whole spectrum range of the processed image(s), for each spatial coordinates `(x,y,z)`. "\
778"The command does not loop over the `c`-axis in this case."\n\
779"## String manipulation:"\n\
780"Character strings are defined and managed as vectors objects. "\
781"Dedicated functions and initializers to manage strings are:"\n\
782"* `['string']` and `'string'` define a vector whose values are the character codes of the "\
783"specified `character string` (e.g. `'foo'` is equal to `[ 102,111,111 ]`)."\n\
784"* `_'character'` returns the (scalar) byte code of the specified character (e.g. `_'A'` is equal to '65')."\n\
785"* A special case happens for __empty__ strings: Values of both expressions `['']` and `''` are '0'."\n\
786"* Functions 'lowercase()' and 'uppercase()' return string with all string characters lowercased or uppercased."\n\
787"* Function 'stov(str,_starting_index,_is_strict)' parses specified string 'str' and returns the value contained "\
788"in it."\n\
789"* Function 'vtos(expr,_nb_digits,_siz)' returns a vector of size 'siz' which contains the character representation "\
790"of values described by expression 'expr'. "\
791"'nb_digits' can be { -1=auto-reduced | 0=all | >0=max number of digits }."\n\
792"* Function 'echo(str1,str2,...,strN)' prints the concatenation of given string arguments on the console."\n\
793"* Function 'string(_#siz,str1,str2,...,strN)' generates a vector corresponding to the concatenation of given "\
794"string/number arguments."\n\
795"## Dynamic arrays:"\n\
796"A dynamic array is defined as a one-column (or empty) image '[ind]' in the image list. "\
797"It allows elements to be added or removed, each element having the same dimension "\
798"(which is actually the number of channels of image '[ind]'). "\
799"Dynamic arrays adapt their size to the number of elements they contain."\n\n\
800"A dynamic array can be manipulated in a math expression, with the following functions:"\n\
801"* 'da_size(_#ind)': Return the number of elements in dynamic array '[ind]'."\n\
802"* 'da_back(_#ind)': Return the last element of the dynamic array '[ind]'."\n\
803"* 'da_insert(_#ind,pos,elt_1,_elt_2,...,_elt_N)': Insert 'N' new elements 'elt_k' starting from index 'pos' "\
804"in dynamic array '[ind]'."\n\
805"* 'da_push(_#ind,elt1,_elt2,...,_eltN)': Insert 'N' new elements 'elt_k' at the end of dynamic array '[ind]'."\n\
806"* 'da_pop(_#ind)': Same as 'da_back()' but also remove last element from the dynamic array '[ind]'."\n\
807"* 'da_remove(_#ind,_start,_end)': Remove elements located between indices 'start' and 'end' (included) "\
808"in dynamic array '[ind]'."\n\
809"* The value of the k-th element of dynamic array '[ind]' is retrieved with 'i[_#ind,k]' (if the element is a "\
810"scalar value), or 'I[_#ind,k]' (if the element is a vector)."\n\n\
811"In the functions above, argument '#ind' may be omitted in which case it is assumed to be '#-1'."\n\
812"## Special operators:"\n\
813"* ';': expression separator. The returned value is always the last encountered expression. "\
814"For instance expression '1;2;pi' is evaluated as 'pi'."\n\
815"* '=': variable assignment. Variables in mathematical parser can only refer to numerical "\
816"values (vectors or scalars). Variable names are case-sensitive. Use this operator in conjunction with ';' to define "\
817"more complex evaluable expressions, such as \n~~~\nt = cos(x); 3*t^2 + 2*t + 1\n~~~\n"\
818"These variables remain __local__ to the mathematical parser and cannot be accessed outside the evaluated "\
819"expression."\n\
820"* Variables defined in math parser may have a __constant__ property, by specifying keyword 'const' before the "\
821"variable name (e.g. 'const foo = pi/4;'). The value set to such a variable must be indeed a __constant scalar__. "\
822"Constant variables allows certain types of optimizations in the math JIT compiler."\n\
823\
824"## Specific functions:"\n\
825"* 'addr(expr)': return the pointer address to the specified expression 'expr'. "\n\
826"* 'fill(target,expr)' or 'fill(target,index_name,expr)' fill the content of the specified target "\
827"(often vector-valued) using a given expression, e.g. `V = vector16(); fill(V,k,k^2 + k + 1);`. "\
828"For a vector-valued target, it is basically equivalent to: "\
829"`for (index_name = 0, index_name<size(target), ++index_name, target[index_name] = expr);`."\n\
830"* 'u(max)' or 'u(min,max)': return a random value between '[0,max]' or '[min,max]', following a uniform "\
831"distribution."\n\
832"* 'f2ui(value)' and 'ui2f(value)': Convert a large unsigned integer as a negative floating point value "\
833"(and vice-versa), so that 32bits floats can be used to store large integers while keeping a unitary precision."\n\
834"* 'i(_a,_b,_c,_d,_interpolation_type,_boundary_conditions)': return the value of the pixel located at position "\
835"`(a,b,c,d)` in the associated image, if any ('0' otherwise). "\
836"'interpolation_type' can be { 0=nearest neighbor | 1=linear | 2=cubic }. "\
837"'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. "\
838"Omitted coordinates are replaced by their default values which are respectively 'x', 'y', 'z', 'c', 'interpolation' "\
839"and 'boundary'. For instance command \n~~~\nfill 0.5*(i(x+1)-i(x-1))\n~~~\n will estimate the X-derivative of an "\
840"image with a classical finite difference scheme."\n\
841"* 'j(_dx,_dy,_dz,_dc,_interpolation_type,_boundary_conditions)' does the same for the pixel located at position "\
842"`(x+dx,y+dy,z+dz,c+dc)` (pixel access relative to the current coordinates)."\n\
843"* 'i[offset,_boundary_conditions]' returns the value of the pixel located at specified 'offset' in the associated "\
844"image buffer (or '0' if offset is out-of-bounds)."\n\
845"* 'j[offset,_boundary_conditions]' does the same for an offset relative to the current pixel coordinates "\
846"`(x,y,z,c)`."\n\
847"* 'i(#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)', "\
848"'j(#ind,_dx,_dy,_dz,_dc,_interpolation,_boundary_conditions)', 'i[#ind,offset,_boundary_conditions]' and "\
849"'i[offset,_boundary_conditions]' are similar expressions used to access pixel values for any numbered image '[ind]' "\
850"of the list."\n\
851"* 'I/J[offset,_boundary_conditions]' and 'I/J(#ind,_x,_y,_z,_interpolation,_boundary_conditions)' do the same as "\
852"'i/j[offset,_boundary_conditions]' and 'i/j(#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)' but return a "\
853"vector instead of a scalar (e.g. a vector `[ R,G,B ]` for a pixel at `(a,b,c)` in a color image)."\n\
854"* 'crop(_#ind,_x,_y,_z,_c,_dx,_dy,_dz,_dc,_boundary_conditions)' returns a vector whose values come from the "\
855"cropped region of image '[ind]' (or from default image selected if 'ind' is not specified). Cropped region starts "\
856"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 "\
857"if they are not ambiguous (e.g. 'crop(#ind,x,y,dx,dy)' is a valid invocation of this function)."\n\
858"* 'draw(_#ind,S,x,y,z,c,dx,_dy,_dz,_dc,_opacity,_M,_max_M)' draws a sprite 'S' in image '[ind]' "\
859"(or in default image selected if 'ind' is not specified) at coordinates `(x,y,z,c)`. "\
860"The size of the sprite `dx x dy x dz x dc` must be specified. You can also specify a corresponding opacity mask "\
861"'M' if its size matches 'S'."\n\
862"* 'polygon(_#ind,nb_vertices,coords,_opacity,_color)' draws a filled polygon in image '[ind]' (or in default image "\
863"selected if 'ind' is not specified) at specified coordinates. It draws a single line if 'nb_vertices' is set to 2."\n\
864"* 'polygon(_#ind,-nb_vertices,coords,_opacity,_pattern,_color)' draws a outlined polygon in image '[ind]' (or in "\
865"default image selected if 'ind' is not specified) at specified coordinates and with specified line pattern. "\
866"It draws a single line if 'nb_vertices' is set to 2."\n\
867"* 'ellipse(_#ind,xc,yc,radius1,_radius2,_angle,_opacity,_color)' draws a filled ellipse in image '[ind]' "\
868"(or in default image selected if 'ind' is not specified) with specified coordinates."\n\
869"* 'ellipse(_#ind,xc,yc,-radius1,-_radius2,_angle,_opacity,_pattern,_color)' draws an outlined ellipse in image "\
870"'[ind]' (or in default image selected if 'ind' is not specified)."\n\
871"* 'resize(#ind,w,_h,_d,_s,_interp,_boundary_conditions,_cx,_cy,_cz,_cc)' resizes an image of the associated list "\
872"with specified dimension and interpolation method. When using this function, you should consider retrieving the "\
873"(non-constant) image dimensions using the dynamic functions 'w(_#ind)', 'h(_#ind)', 'd(_#ind)', 's(_#ind)', "\
874"'wh(_#ind)', 'whd(_#ind)' and 'whds(_#ind)' instead of the corresponding constant variables."\n\
875"* 'if(condition,expr_then,_expr_else)': return value of 'expr_then' or 'expr_else', depending on the value of "\
876"'condition' { 0=false | other=true }. 'expr_else' can be omitted in which case '0' is returned if the condition "\
877"does not hold. Using the ternary operator 'condition?expr_then[:expr_else]' gives an equivalent expression. "\
878"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 "\
879"vertical lines on every 10th column of an image."\n\
880"* 'do(expression,_condition)' repeats the evaluation of 'expression' until 'condition' vanishes "\
881"(or until 'expression' vanishes if no 'condition' is specified). For instance, the expression: "\
882"\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 "\
883"sequence, for 'N>=0' (e.g., '46368' for 'N=24'). 'do(expression,condition)' always evaluates the specified "\
884"expression at least once, then check for the loop condition. When done, it returns the last value of 'expression'."\n\
885"* 'for(init,condition,_procedure,body)' first evaluates the expression 'init', then iteratively evaluates 'body' "\
886"(followed by 'procedure' if specified) while 'condition' holds (i.e. not zero). It may happen that no iterations are "\
887"done, in which case the function returns 'nan'. Otherwise, it returns the last value of 'body'. "\
888"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 "\
889"returns the 'N'-th value of the Fibonacci sequence, for 'N>=0' (e.g., '46368' for 'N=24')."\n\
890"* 'while(condition,expression)' is exactly the same as 'for(init,condition,expression)' without the specification of "\
891"an initializing expression."\n\
892"* 'repeat(nb_iters,expr)' or 'fill(nb_iters,iter_name,expr)' run 'nb_iters' iterations of the specified expression "\
893"'expr', e.g. `V = vector16(); repeat(16,k,V[k] = k^2 + k + 1);`. "\
894"It is basically equivalent to: "\
895"`for (iter_name = 0, iter_name<nb_iters, ++iter_name, expr);`."\n\
896"* 'break()' and 'continue()' respectively breaks and continues the current running bloc "\
897"(loop, init or main environment)."\n\
898"* 'fsize('filename')' returns the size of the specified 'filename' (or '-1' if file does not exist)."\n\
899"* 'date(attr,'path')' returns the date attribute for the given 'path' (file or directory), "\
900"with 'attr' being { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }, or a vector "\
901"of those values."\n\
902"* 'date(_attr)' returns the specified attribute for the current (locale) date (attributes being "\
903"{ 0...6=same meaning as above | 7=milliseconds })."\n\
904"* 'print(expr1,expr2,...)' or 'print(#ind)' prints the value of the specified expressions (or image information) "\
905"on the console, and returns the value of the last expression (or 'nan' in case of an image). "\
906"Function 'prints(expr)' also prints the string composed of the character codes defined by the vector-valued "\
907"expression (e.g. 'prints('Hello')')."\n\
908"* 'debug(expression)' prints detailed debug info about the sequence of operations done by the math parser to "\
909"evaluate the expression (and returns its value)."\n\
910"* 'display(_X,_w,_h,_d,_s)' or 'display(#ind)' display the contents of the vector 'X' (or specified image) and "\
911"wait for user events. if no arguments are provided, a memory snapshot of the math parser environment is displayed "\
912"instead."\n\
913"* 'begin(expression)' and 'end(expression)' evaluates the specified expressions only once, respectively at the "\
914"beginning and end of the evaluation procedure, and this, even when multiple evaluations are required "\
915"(e.g. in 'fill \">begin(foo = 0); ++foo\"')."\n\
916"* 'copy(dest,src,_nb_elts,_inc_d,_inc_s,_opacity)' copies an entire memory block of 'nb_elts' elements starting "\
917"from a source value 'src' to a specified destination 'dest', with increments defined by 'inc_d' and 'inc_s' "\
918"respectively for the destination and source pointers."\n\
919"* 'stats(_#ind)' returns the statistics vector of the running image '[ind]', i.e the vector "\
920"`[ im,iM,ia,iv,xm,ym,zm,cm,xM,yM,zM,cM,is,ip ]` (14 values)."\n\
921"* 'ref(expr,a)' references specified expression 'expr' as variable name 'a'."\n\
922"* 'unref(a,b,...)' destroys references to the named variable given as arguments."\n\
923"* 'breakpoint()' inserts a possible computation breakpoint (useless with the cli interface)."\n\
924"* '_(comment) expr' just returns expression 'expr' (useful for inserting inline comments in math expressions)."\n\
925"* 'run('pipeline')' executes the specified G'MIC pipeline as if it was called outside the currently evaluated "\
926"expression."\n\
927"* 'set(A,\\'variable_name\\')' set the G'MIC variable '$variable_name' with the value of expression 'A'. If 'A' is"\
928" a vector-valued variable, it is assumed to encode a string."\n\
929"* 'store(A,\\'variable_name\\',_w,_h,_d,_s,_is_compressed)' transfers the data of vector 'A' as a "\
930"`w x h x d x s` image to the G'MIC variable '$variable_name'. Thus, the data becomes available outside the math "\
931"expression (that is equivalent to using the regular command ''store'', but directly in the math expression)."\n\
932"* 'get(\\'variable_name\\',_size,_return_as_string)' returns the value of the specified variable, as a vector of "\
933"'size' values, or as a scalar (if 'size' is zero or not specified)."\n\
934"* 'name(_#ind,size)' returns a vector of size 'size', whose values are the characters codes of the name of image "\
935"'[ind]' (or default image selected if 'ind' is not specified)."\n\
936"* 'correlate(I,wI,hI,dI,sI,K,wK,hK,dK,sK,_boundary_conditions,_is_normalized,_channel_mode,_xcenter,_ycenter,"\
937"_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,_xdilation,_ydilation,_zdilation,"\
938"_interpolation_type)' returns the correlation, unrolled as a vector, of the `wI x hI x dI x sI`-sized image 'I' "\
939"with the `wK x hK x dK x sK`-sized kernel 'K' (the meaning of the other arguments are the same as in command "\
940"'correlate'). Similar function 'convolve(...)' is also defined for computing the convolution between 'I' and 'K'."\n\
941\
942"## User-defined macros:"\n\
943"* Custom macro functions can be defined in a math expression, using the assignment operator "\
944"'=', e.g. \n~~~\nfoo(x,y) = cos(x + y); result = foo(1,2) + foo(2,3)\n~~~\n"\n\
945"* Trying to override a built-in function (e.g. 'abs()') has no effect."\n\
946"* Overloading macros with different number of arguments is possible. Re-defining a previously defined macro with "\
947"the same number of arguments discards its previous definition."\n\
948"* Macro functions are indeed processed as __macros__ by the mathematical evaluator. You should avoid invoking them "\
949"with arguments that are themselves results of assignments or self-operations. "\
950"For instance, \n~~~\nfoo(x) = x + x; z = 0; foo(++z)\n~~~\n returns '4' rather than expected value '2'."\n\
951"* When substituted, macro arguments are placed inside parentheses, except if a number sign "\
952"'#' is located just before or after the argument name. For instance, expression \n"\
953"~~~\nfoo(x,y) = x*y; foo(1+2,3)\n~~~\n "\
954"returns '9' (being substituted as '(1+2)*(3)'), while expression \n~~~\nfoo(x,y) = x#*y#; foo(1+2,3)\n~~~\n "\
955"returns '7' (being substituted as '1+2*3')."\n\
956"* Number signs appearing between macro arguments function actually count for __empty__ separators. They may be used "\
957"to force the substitution of macro arguments in unusual places, e.g. as in \n~~~\nstr(N) = ['I like N#'];\n~~~\n"\
958\
959"## Multi-threaded and in-place evaluation:"\n\
960"* If your image data are large enough and you have several CPUs available, it is likely that the math expression "\
961"passed to a 'fill', 'eval' or 'input' commands is evaluated in parallel, using multiple computation threads."\n\
962"* Starting an expression with ':' or '*' forces the evaluations required for an image to be run in parallel, "\
963"even if the amount of data to process is small (beware, it may be slower to evaluate in this case!). "\
964"Specify ':' (rather than '*') to avoid possible image copy done before evaluating the expression "\
965"(this saves memory, but do this only if you are sure this step is not required!)"\n\
966"* If the specified expression starts with '>' or '<', the pixel access operators 'i()', 'i[]', 'j()' and 'j[]' "\
967"return values of the image being currently modified, in forward ('>') or backward ('<') order. "\
968"The multi-threading evaluation of the expression is disabled in this case."\n\
969"* Function 'critical(expr)' forces the execution of the given expression in a single thread at a time."\n\
970"* 'begin_t(expr)' and 'end_t(expr)' evaluates the specified expression once for each running thread "\
971"(so possibly several times) at the beginning and the end of the evaluation procedure."\n\
972"* 'merge(variable,operator)' tells to merge the local variable value computed by threads, with the specified "\
973"operator, when all threads have finished computing."\n\
974"* Expressions 'i(_#ind,x,_y,_z,_c)=value', 'j(_#ind,x,_y,_z,_c)=value', 'i[_#ind,offset]=value' and "\
975"'j[_#ind,offset]=value' set a pixel value at a different location than the running one in the image '[ind]' "\
976"(or in the associated image if argument '#ind' is omitted), either with global coordinates/offsets "\
977"(with 'i(...)' and 'i[...]'), or relatively to the current position `(x,y,z,c)` (with 'j(...)' and 'j[...]'). "\
978"These expressions always return 'value'."
979
980  _section "Image and Data Viewers"
981  _text \
982"* \\G'MIC has some very handy embedded __visualization modules__, for 1D signals (command ''plot''), "\
983"1D/2D/3D images (command ''display'') and 3D vector objects (command ''display3d''). It manages interactive views "\
984"of the selected image data."\n\
985"* The following actions are available in the interactive viewers:"\n\
986"  - `(mousewheel)`: Zoom in/out."\n\
987"  - `ESC`: Close window."\n\
988"  - `CTRL+D`: Increase window size."\n\
989"  - `CTRL+C`: Decrease window size."\n\
990"  - `CTRL+R`: Reset window size."\n\
991"  - `CTRL+F`: Toggle fullscreen mode."\n\
992"  - `CTRL+S`: Save current view as a numbered file 'gmic_xxxx.ext'."\n\
993"  - `CTRL+O`: Save copy of the viewed data, as a numbered file 'gmic_xxxx.ext'."\n\
994\n\
995"* Actions specific to the __1D/2D image viewer__ (command ''display'') are:"\n\
996"  - `Left mouse button`: Create an image selection and zoom into it."\n\
997"  - `Middle mouse button`, or `CTRL+left mouse button`: Move image."\n\
998"  - `Mouse wheel` or `PADD+/-`: Zoom in/out."\n\
999"  - `Arrow keys`: Move image left/right/up/down."\n\
1000"  - `CTRL+A`: Enable/disable transparency (show alpha channel)."\n\
1001"  - `CTRL+N`: Change normalization mode (can be { none | normal | channel-by-channel })."\n\
1002"  - `CTRL+SPACE`: Reset view."\n\
1003"  - `CTRL+X`: Show/hide axes."\n\
1004"  - `CTRL+Z`: Hold/release aspect ratio."\n\
1005\n\
1006"* Actions specific to the __3D volumetric image viewer__ (command ''display'') are:"\n\
1007"  - `CTRL+P`: Play z-stack of frames as a movie."\n\
1008"  - `CTRL+V`: Show/hide 3D view on bottom right zone."\n\
1009"  - `CTRL+X`: Show/hide axes."\n\
1010"  - `CTRL+(mousewheel)`: Go up/down."\n\
1011"  - `SHIFT+(mousewheel)`: Go left/right."\n\
1012"  - `Numeric PAD`: Zoom in/out (`+`/`-`) and move through zoomed image (digits)."\n\
1013"  - `BACKSPACE`: Reset zoom scale."\n\
1014\n\
1015"* Actions specific to the __3D object viewer__ (command ''display3d'') are:"\n\
1016"  - `(mouse)+(left mouse button)`: Rotate 3D object."\n\
1017"  - `(mouse)+(right mouse button)`: Zoom 3D object."\n\
1018"  - `(mouse)+(middle mouse button)`: Shift 3D object."\n\
1019"  - `F1 ... F6`: Toggle between different 3D rendering modes."\n\
1020"  - `F7/F8`: Decrease/increase focale."\n\
1021"  - `F9`: Select animation mode."\n\
1022"  - `F10`: Select animation speed."\n\
1023"  - `SPACE`: Start/stop animation."\n\
1024"  - `CTRL+A`: Show/hide 3D axes."\n\
1025"  - `CTRL+B`: Switch between available background."\n\
1026"  - `CTRL+G`: Save 3D object, as numbered file 'gmic_xxxx.obj'."\n\
1027"  - `CTRL+L`: Show/hide outline."\n\
1028"  - `CTRL+P`: Print current 3D pose on stderr."\n\
1029"  - `CTRL+T`: Switch between single/double-sided 3D modes."\n\
1030"  - `CTRL+V`: Start animation with video output."\n\
1031"  - `CTRL+X`: Show/hide 3D bounding box."\n\
1032"  - `CTRL+Z`: Enable/disable z-buffered rendering."
1033
1034  _section "Adding Custom Commands"
1035  _text \
1036"* New custom commands can be added by the user, through the use of \\G'MIC __custom commands files__."\n\
1037"* A command file is a simple text file, where each line starts either by "\
1038"\n~~~\ncommand_name: command_definition\n~~~\n or \n~~~\ncommand_definition (continuation)\n~~~\n"\n\
1039"* At startup, G'MIC automatically includes user's command file '$HOME/.gmic' (on __Unix__) or "\
1040"'%APPDATA%/user.gmic' (on __Windows__). The CLI tool 'gmic' automatically runs the command "\
1041"'cli_start' if defined."\n\
1042"* Custom command names must use character set `[a-zA-Z0-9_]` and cannot start with a number."\n\
1043"* Any `# comment` expression found in a custom commands file is discarded by the G'MIC parser, "\
1044"wherever it is located in a line."\n\
1045"* In a custom command, the following '$-expressions' are recognized and substituted:"\n\
1046"  - '$""\*' is substituted by a copy of the specified string of arguments."\n\
1047"  - '$\"*\"' is substituted by a copy of the specified string of arguments, each being double-quoted."\n\
1048"  - '$""#' is substituted by the maximum index of known arguments (either specified by the user or set to a default "\
1049"value in the custom command)."\n\
1050"  - '$""[]' is substituted by the list of selected image indices that have been specified in the command "\
1051"invocation."\n\
1052"  - '$""?' is substituted by a printable version of '$""[]' to be used in command descriptions."\n\
1053"  - '$i' and '${i}' are both substituted by the `i`-th specified argument. Negative indices such as '${-j}' are "\
1054"allowed and refer to the `j`-th latest argument. '$""0' is substituted by the custom command name."\n\
1055"  - '${i=default}' is substituted by the value of '$i' (if defined) or by its new value set to 'default' otherwise "\
1056"('default' may be a `$-expression` as well)."\n\
1057"  - '${subset}' is substituted by the argument values (separated by commas ',') of a specified argument subset. "\
1058"For instance expression '$""{2--2}' is substituted by all specified command arguments except the first and the last "\
1059"one. Expression '$""{^0}' is then substituted by all arguments of the invoked command (eq. to '$""*' if all "\
1060"arguments have been indeed specified)."\n\
1061"  - '$""=var' is substituted by the set of instructions that will assign each argument '$i' to the named variable "\
1062"'var$i' (for i in '[0...$""#]'. This is particularly useful when a custom command want to manage variable numbers "\
1063"of arguments. Variables names must use character set `[a-zA-Z0-9_]` and cannot start with a number."\n\
1064\n\
1065"* These particular `$-expressions` for custom commands are __always substituted__, even in "\
1066"double-quoted items or when the dollar sign '$' is escaped with a backslash '\\$'. To avoid substitution, place an "\
1067"empty double quoted string just after the '$' (as in '$\"\"1')."\n\
1068"* Specifying arguments may be skipped when invoking a custom command, by replacing them by commas ',' as in "\
1069"expression \n~~~\nflower ,,3\n~~~\n Omitted arguments are set to their default values, which must be thus explicitly "\
1070"defined in the code of the corresponding custom command (using default argument expressions as '$""{1=default}')."\n\
1071"* If one numbered argument required by a custom command misses a value, an error is thrown by the G'MIC "\
1072"interpreter."\n\
1073"* It is possible to specialize the invokation of a '+command' by defining it as "\
1074"\n~~~\n+command_name: command_definition\n~~~\n"\
1075"* A +-specialization takes priority over the regular command definition when the command is invoked with a "\
1076"prepended '+'."\n\
1077"* When only a +-specialization of a command is defined, invoking 'command' is actually equivalent to '+command'."
1078
1079  _section "List of Commands"
1080  _text \
1081"All available \\G'MIC commands are listed below, by categories. An argument specified between '[]' "\
1082"or starting by '_' is optional except when standing for an existing image '[image]', where 'image' "\
1083"can be either an index number or an image name. In this case, the '[]' characters are mandatory when writing the "\
1084"item. Note that all images that serve as illustrations in this reference documentation are normalized in "\
1085"range `[0,255]` before being displayed. You may need to do this explicitly (command `normalize 0,255`) if you want "\
1086"to save and view images with the same aspect than those illustrated in the example codes."
1087
1088  # Insert list of commands.
1089  l reference_list_of_commands_$1 onfail endl
1090
1091  # Insert additional sections if specified.
1092  xfolder="$2"
1093  if "['$1']=='html' && ['$2']==0" xfolder=$HOME/work/src/gmic-community/reference fi
1094
1095  if ['$xfolder']!=0
1096    files $xfolder/*.gmd files=${}
1097    repeat narg({/$files})
1098      arg 1+$>,$files file=${}
1099      l[] it $file s={b} t={t} rm endl
1100      _section {/$s}
1101      _text {/$t}
1102    done
1103  fi
1104
1105  _section "Examples of Use"
1106  _text \
1107"`gmic` is a generic image processing tool which can be used in a wide variety of situations. "\
1108"The few examples below illustrate possible uses of this tool:"\n\
1109"### View a list of images: "\n\
1110"\n~~~\n$ gmic file1.bmp file2.jpeg\n~~~"\n\n\
1111"### Convert an image file: "\n\
1112"\n~~~\n$ gmic input.bmp output output.jpg\n~~~"\n\n\
1113"### Create a volumetric image from a movie sequence: "\n\
1114"\n~~~\n$ gmic input.mpg append z output output.hdr\n~~~"\n\n\
1115"### Compute image gradient norm: "\n\
1116"\n~~~\n$ gmic input.bmp gradient_norm\n~~~"\n\n\
1117"### Denoise a color image: "\n\
1118"\n~~~\n$ gmic image.jpg denoise 30,10 output denoised.jpg\n~~~"\n\n\
1119"### Compose two images using overlay layer blending: "\n\
1120"\n~~~\n$ gmic image1.jpg image2.jpg blend overlay output blended.jpg\n~~~"\n\n\
1121"### Evaluate a mathematical expression: "\n\
1122"\n~~~\n$ gmic echo \"cos(pi/4)^2+sin(pi/4)^2={cos(pi/4)^2+sin(pi/4)^2}\"\n~~~"\n\n\
1123"### Plot a 2D function: "\n\
1124"\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\
1125"===\n![](../img/example_plot.png)\n==="\n\n\
1126"### Plot a 3D elevated function in random colors: "\n\
1127"\n~~~\n$ gmic 128,128,1,3,\"u(0,255)\" plasma 10,3 blur 4 sharpen 10000 n 0,255 "\
1128"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\
1129"===\n![](../img/example_elevation3d.png)\n==="\n\n\
1130"### Plot the isosurface of a 3D volume: "\n\
1131"\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\
1132"===\n![](../img/example_isosurface3d.png)\n==="\n\n\
1133"### Render a G'MIC 3D logo: "\n\
1134"\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 "\
1135"elevation3d -0.1 moded3d 4\n~~~"\n\
1136"===\n![](../img/example_logo.png)\n==="\n\n\
1137"### Generate a 3D ring of torii: "\n\
1138"\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}\" "\
1139"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\
1140"===\n![](../img/example_torii.png)\n==="\n\n\
1141"### Create a vase from a 3D isosurface: "\n\
1142"\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 "\
1143"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 "\
1144"color3d[-3] 255,128,0 add3d\n~~~"\n\
1145"===\n![](../img/example_vase.png)\n==="\n\n\
1146"### Launch a set of interactive demos: "\n\
1147"\n~~~\n$ gmic demos\n~~~\n"
1148
1149  l reference_footer_$1 reference_end_$1 onfail endl
1150  um _section,_text
1151  rm
1152
1153#
1154# Implement output mode 'ascii' for command 'reference'.
1155#
1156reference_begin_ascii :
1157  use_vt100
1158  if !narg($_shell_cols) _shell_cols={${-shell_cols}-5} fi
1159  _section=0
1160  +e[] ""
1161
1162reference_header_ascii :
1163  if $_prerelease strprerelease=" (pre-release ""#"$_prerelease")" else strprerelease="" fi
1164  str=\n\
1165      "  "${_vt100_b}"gmic: GREYC\'s Magic for Image Computing:"$_vt100_n" command-line interface"\n\
1166      "        "${_vt100_c}${_vt100_b}"Version "${-strver}$strprerelease$_vt100_n\n\
1167      "        "$_vt100_g$_vt100_u"(https://gmic.eu)"$_vt100_n\n\
1168      \n\
1169      "        Copyright (c) Since 2008, David Tschumperlé / GREYC / CNRS."\n\
1170      "        "$_vt100_g$_vt100_u"(https://www.greyc.fr)"$_vt100_n
1171  +e[] $str
1172
1173reference_section_ascii :
1174  _section+=1
1175  +e[] ""
1176  ('$_section." "') ('"$*"') +f.. {'" "'} +f.. {'-'} a[-4,-3] x a[-2,-1] x
1177  +e[] "  "$_vt100_r$_vt100_b{-2,t}$_vt100_n
1178  +e[] "  "$_vt100_r{t}$_vt100_n\n
1179  rm[-2,-1]
1180
1181reference_text_ascii :
1182  l[]
1183    ('"$*"')
1184    gmd2ascii $_shell_cols,1
1185
1186    # Ensure output text contains no more than two consecutive newlines.
1187    # Also add a 2-chars left margin on each line.
1188    s +,{'\n'}
1189    eval "repeat (l,p,
1190            i(#p)==_'\n' && h(#p)>2?resize(#p,1,2,1,1,0):
1191            (resize(#p,1,h#p + 2,1,1,0,0,0,1); copy(i[#p,0],_' ',2,1,0)))"
1192    a y
1193    +e[] {/{t}}
1194    rm
1195  endl
1196
1197reference_list_of_commands_ascii :
1198  l
1199    if !$! it $_path_rc/update$_version.gmic fi
1200    parse_cli ascii
1201  onfail
1202    rm
1203    +e[] \n"  "$_vt100_r${_vt100_b}"No command descriptions available!"$_vt100_n
1204    +e[] "  "${_vt100_r}"Try updating your command files, with command "$_vt100_b"'update'."$_vt100_n
1205  endl
1206
1207reference_footer_ascii :
1208  +e[] \n"  "$_vt100_r$_vt100_b"** G\47MIC comes with ABSOLUTELY NO WARRANTY; "\
1209       "for details visit: https://gmic.eu **"$_vt100_n
1210
1211#
1212# Implement output mode 'html' for command 'reference'.
1213#
1214reference_section_html :
1215  name="$*"
1216  reference_end_section_html
1217  ('"<!DOCTYPE html>"\n\
1218"<html lang=\"en\">"\n\
1219"  <head>"\n\
1220"    <meta charset=\"utf-8\">"\n\
1221"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
1222"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
1223"- "$name"</title>"\n\
1224"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
1225"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
1226"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
1227"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\
1228"  </head>"\n\n\
1229"  <body>"\n\
1230"    <div id=\"include_header\"></div>"\n\n\
1231"    <div class=\"section_title\"><a href=\"index.html\"><p>Reference</p></a></div><div class=\"section_content\">"\n\
1232"    <a name=\"top\"></a>"\n\
1233"<!-- ref_navigation_top -->"\n\n\
1234"<!-- begin_content -->"\n\n\
1235"      <h1 class=\"ref_h1\">"$name"</h1>"\n':y)
1236  nm. $name
1237
1238reference_end_section_html :
1239  if $!   # End previous section
1240    ('"    <br/>"\n\n\
1241      "<!-- end_content -->"\n\n\
1242      "<!-- ref_navigation_bottom -->"\n\
1243      "    </div><div class=\"section_end\"></div>"\n\
1244      "    <div id=\"include_footer\"></div>"\n\
1245      "  </body>"':y)
1246    a[-2,-1] y
1247  fi
1248
1249reference_text_html :
1250  ('"$*"':y) gmd2html. 0
1251  if $!>1 a[-2,-1] y fi
1252
1253reference_footer_html :
1254  reference_end_section_html
1255
1256reference_finalize_html :
1257
1258  # Generate table of contents.
1259  if $_prerelease strprerelease=" (pre-release ""#"$_prerelease")" else strprerelease="" fi
1260  html="<!DOCTYPE html>"\n\
1261"<html lang=\"en\">"\n\
1262"  <head>"\n\
1263"    <meta charset=\"utf-8\">"\n\
1264"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
1265"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
1266"- Table of Contents</title>"\n\
1267"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
1268"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
1269"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
1270"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
1271"  </head>"\n\n\
1272"  <body>"\n\
1273"    <div id=\"include_header\"></div>"\n\n\
1274"    <div class=\"section_title\"><a href=\"index.html\"><p>Reference</p></a></div><div class=\"section_content\">"\n\n\
1275"    <a name=\"top\"></a>"\n\
1276"<!-- ref_navigation_top -->"\n\n\
1277"<!-- begin_content -->"\n\n\
1278"      <h1 class=\"ref_h1\">Preamble</h1>"\n\
1279"        <ul><li>This document is distributed under the "\
1280"<a target=\"_blank\" href=\"http://www.gnu.org/licenses/fdl-1.3.txt\">GNU Free Documentation License</a>, "\
1281"version 1.3.</li>"\n\
1282"        <li>A <a href=\"gmic_reference.pdf\">.pdf version</a> of this document is available.</li>"\n
1283
1284  if !0$_pdf_output
1285     html.="        <li>Quick access to the <a class=\"ref_loc\" "\
1286           "href=\"https://gmic.eu/reference/list_of_commands.html\">"\
1287           "List of Commands</a>.</li>"\n
1288  fi
1289
1290  html.="</ul>"\n\
1291"      <h1 class=\"ref_h1\">Version</h1>"\n\
1292"      <div class=\"ref_header\">"\n\
1293"        <span class=\"gmd_gmic\">G&apos;MIC</span>: "\
1294"<span class=\"gmd_monospace\">GREYC's Magic for Image Computing</span><br/>"\n\
1295"        <a href=\"https://gmic.eu\">https://gmic.eu</a><br/>"\n\
1296"        Version <b>"${-strver}"</b>"$strprerelease"<br/><br/>"\n\
1297"        Copyright &copy; Since 2008, "\
1298"<a target=\"_blank\" href=\"https://tschumperle.users.greyc.fr/\">David Tschumperlé</a> / "\
1299"<a target=\"_blank\" href=\"https://www.greyc.fr\">GREYC</a> / "\
1300"<a target=\"_blank\" href=\"http://www.cnrs.fr/en\">CNRS</a><br/>"\n\
1301"        <a target=\"_blank\" href=\"https://www.greyc.fr\">https://www.greyc.fr</a><br/>"\n\
1302"      </div>"\n\
1303"      <h1 class=\"ref_h1\">Table of Contents</h1>"\n\
1304"      <ul>"\n
1305
1306  ind_loc=${"-nmd 1,\"List of Commands\""}
1307  if narg($ind_loc)
1308    repeat $! l[$>]
1309      name={n} strvar $name url=${}.html
1310      if $>==$ind_loc
1311         html.="<li><a class=\"ref_loc\" href=\""$url"#top\">"$name"</a></li>"
1312      else
1313        html.="<li><a href=\""$url"#top\">"$name"</a></li>"
1314      fi
1315    endl done
1316    html.="\n    </ul>"\n\n\
1317          "<!-- end_content -->"\n\n\
1318          "<!-- ref_navigation_bottom -->"\n\
1319          "    </div><div class=\"section_end\"></div>"\n\
1320          "    <div id=\"include_footer\"></div>"\n\
1321          "  </body>"
1322
1323    i[0] ({'$html'}:y) nm[0] "Table of Contents"
1324    ind_loc+=1
1325    if narg($ind_loc)" && "isfile('list_of_commands.html') # Merge with existing 'List of commands' page if it exists
1326      it list_of_commands.html
1327      if find(crop(),'"<!-- merged_content -->"')<0 l[$ind_loc,-1]
1328        s[0] -,{'"<!-- end_content -->"'}
1329        s. -,{'"<!-- begin_content -->"'}
1330        i[1] ('"<!-- merged_content -->"':y)
1331        k[0,1,-1] a y
1332      endl else nm. "List of Commands" rv[$ind_loc,-1] rm. fi
1333    fi
1334  fi
1335
1336  # Insert top and bottom navigation bar into pages.
1337  if !0$_pdf_output
1338    repeat $!
1339      current={$>,n} strvar[] $current url_current=${}.html
1340      if $>>1 previous={{$>-1},n} strvar[] $previous url_previous=${}.html else previous= fi
1341      if $< next={{$>+1},n} strvar[] $next url_next=${}.html else next= fi
1342      html_top="    <table class=\"ref_navigation_top\"><tr><td>"\
1343                "<a href=\"index.html\">Table of Contents</a>"
1344      if $> html_top.="&nbsp;&nbsp;&#9656;&nbsp;&nbsp;<a href=\""$url_current"#top\">"$current"</a>" fi
1345      html_top.="</td><td>"
1346      if ['$previous']!=0 html_top.="<a href=\""$url_previous"#top\">&#9664;&nbsp;&nbsp;"$previous"</a>" fi
1347      if ['$previous']!=0" && "['$next']!=0 html_top.="&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;" fi
1348      if ['$next']!=0 html_top.="<a href=\""$url_next"#top\">"$next"&nbsp;&nbsp;&#9654;</a>" fi
1349      html_top.="</td></tr></table>"
1350
1351      html_bottom="<div class=\"ref_navigation_bottom\">"
1352      if ['$previous']!=0 html_bottom.="<a href=\""$url_previous"#top\">&#9664;&nbsp;&nbsp;"$previous"</a>" fi
1353      if ['$previous']!=0" && "['$next']!=0 html_bottom.="&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;" fi
1354      if ['$next']!=0 html_bottom.="<a href=\""$url_next"#top\">"$next"&nbsp;&nbsp;&#9654;</a>" fi
1355      html_bottom.="</div>"
1356
1357      replace_str[$>] "<!-- ref_navigation_top -->",$html_top
1358      replace_str[$>] "<!-- ref_navigation_bottom -->",$html_bottom
1359    done
1360  fi
1361
1362reference_end_html :
1363  reference_finalize_html
1364
1365  # Save html pages.
1366  repeat $! l[$>] strvar {n} ot ${}.html endl done
1367  if isfile('table_of_contents.html') x "ln -fs table_of_contents.html index.html" fi
1368  rm
1369
1370#
1371# Implement output mode 'man' for command 'reference'.
1372# (It is based on the 'ascii' output with some tricks to generate a roff file).
1373#
1374reference_begin_man :
1375  _section=0
1376  +e[] ".TH G\47MIC 1\n\
1377        .SH NAME\n\
1378        gmic \\- Perform image processing operations using the G\47MIC framework.\n\
1379        \n\
1380        .SH HELP\n"
1381
1382reference_header_man :
1383  _vt100_b="\\fB"
1384  _vt100_c="\\fB"
1385  _vt100_g="\\fB"
1386  _vt100_m=
1387  _vt100_n="\\fR"
1388  _vt100_r="\\fB"
1389  _vt100_u="\\fI"
1390  _prerelease=
1391  reference_header_ascii
1392
1393reference_section_man :
1394  reference_section_ascii "$*"
1395
1396reference_text_man :
1397  reference_text_ascii "$*"
1398
1399reference_list_of_commands_man :
1400  _vt100_c=
1401  reference_list_of_commands_ascii
1402  _vt100_m="\\fB"
1403  _vt100_b=
1404
1405reference_footer_man :
1406  reference_footer_ascii "$*"
1407
1408#
1409# Implement output mode 'pdf' for command 'reference'.
1410# (It is based on the 'html' output with some tricks to generate a pdf file with 'wkhtmltopdf').
1411#
1412reference_begin_pdf :
1413  _pdf_output=1
1414
1415reference_section_pdf :
1416  reference_section_html "$*"
1417
1418reference_text_pdf :
1419  reference_text_html "$*"
1420
1421reference_footer_pdf :
1422  reference_end_section_html
1423
1424reference_end_pdf :
1425  1024,4,1,3 o. reference_pdf.png rm.
1426  reference_finalize_html
1427
1428  # Load existing html page for each command.
1429  l[]
1430    it ../../src/gmic_stdlib.gmic +parse_cli. list loc=${}
1431    parse_cli html
1432    repeat narg($loc)
1433      command=${"arg "1+$>","$loc}
1434      l[] if ['$command'][0]!=_'_' it $command.html fi onfail endl
1435    done
1436    sort_list +,n
1437  endl
1438  s +,{'"<!-- begin_content -->"'}
1439  s +,{'"<!-- end_content -->"'}
1440  k[2--1:5]
1441
1442  # Add specific header and footer.
1443  if $_prerelease strprerelease=" (pre-release ""#"$_prerelease")" else strprerelease="" fi
1444  i[0] ('"<!DOCTYPE html>"\n\
1445"<html lang=\"en\">"\n\
1446"  <head>"\n\
1447"    <meta charset=\"utf-8\">"\n\
1448"    <link rel=\"stylesheet\" href=\"style.css\">"\n\
1449"    <title>reference_pdf</title>"\n\
1450"  <style>"\n\
1451"    body { background-color: white; font-size: 22px; }"\n\
1452"    .gmd_code_block { font-size: 1em; }"\n\
1453"  </style>"\n\
1454"  </head>"\n\n\
1455"  <body>"\n\
1456"    <div style=\"border: 3px solid black; text-align: center; width: 100%; margin-left: auto; "\
1457"margin-right: auto; margin-top: 9cm; break-after: page\">"\n\
1458"      <img style=\"width: 98%\" src=\"../img/gmic_banner.jpg\" />"\n\
1459"      <h1 class=\"ref_h1\">The Handbook</h1>"\n\
1460"      <h3>Version "${strver[]}$strprerelease"</h3>"\n\
1461"      <p>&copy; David Tschumperlé / GREYC / CNRS</p>"\n\
1462"      <p>"{date(0)}"/"{date(1)}"/"{date(2)}"</p>"\n\
1463"    </div>"':y)
1464  ('"<img style=\"margin-top: 2em; width: 100%;\" src=\"reference_pdf.png\"/>"\n\
1465    "<p style=\"text-align: center;\">&squ; End of document</p>"\n\
1466    "</body>"':y) a y
1467  ot reference_pdf.html
1468  rm
1469
1470  # Copy css to current folder.
1471  it ../style.css ot. style.css rm
1472
1473  # Use 'wkhtmltopdf' to convert html to a pdf file.
1474  # (There is a bug in wkhtmltopdf that prevents all images to appear in the generated document!
1475  #  I have to print the .pdf manually 'in a file' first).
1476  delete reference_pdf.pdf
1477  v 0 e[] "  > Waiting for file 'reference_pdf.pdf'."
1478  for !isfile('reference_pdf.pdf') wait 5000 done
1479  e[] "  > Removing links in file 'reference_pdf.pdf'."
1480  x "pdfjam reference_pdf.pdf"
1481  e[] "  > Compressing file 'reference_pdf.pdf' to 'gmic_reference.pdf'."
1482  x "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=gmic_reference.pdf reference_pdf-pdfjam.pdf"
1483  e[] "  > Clean temporary files."
1484  delete style.css,reference_pdf.html,reference_pdf.png,reference_pdf.pdf,reference_pdf-pdfjam.pdf
1485  e[] "  > Upload file 'gmic_reference.pdf' to G'MIC server."
1486  x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"put -O /www/gmic/reference gmic_reference.pdf; "\
1487    "quit\" >/dev/null"
1488
1489# use_vt100
1490# This command defines some global variables used to output colored text on VT100 terminals.
1491use_vt100 :
1492  if !0$_vt100" || "['$_vt100_n']!=0 return fi
1493  _vt100_b="\33[1m"         # Bold
1494  _vt100_c="\33[0;36;59m"   # Cyan
1495  _vt100_g="\33[0;32;59m"   # Green
1496  _vt100_i="\33[3m"         # Italic
1497  _vt100_m="\33[0;35;59m"   # Magenta
1498  _vt100_n="\33[0;0;0m"     # Normal
1499  _vt100_r="\33[0;31;59m"   # Red
1500  _vt100_s="\33[9m"         # Strikethrough
1501  _vt100_u="\33[4m"         # Underline
1502
1503#@cli version
1504#@cli : Display current version number on stdout.
1505version :
1506  use_vt100
1507  reference_header_ascii[]
1508  if !0$_cli_noarg +e[] "\n" fi
1509
1510v : # Allow 'gmic +v' to get the version number.
1511  version
1512
1513#-------------------------------
1514#
1515#@cli :: Input / Output
1516#
1517#-------------------------------
1518
1519#@cli camera : _camera_index>=0,_nb_frames>0,_skip_frames>=0,_capture_width>=0,_capture_height>=0 : (+)
1520#@cli : Insert one or several frames from specified camera.
1521#@cli : When 'nb_frames==0', the camera stream is released instead of capturing new images.
1522#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
1523#@cli : Default values: 'camera_index=0' (default camera), 'nb_frames=1', 'skip_frames=0' and \
1524# 'capture_width=capture_height=0' (default size).
1525
1526#@cli clut : "clut_name",_resolution>0,_cut_and_round={ 0=no | 1=yes }
1527#@cli : Insert one of the 958 pre-defined CLUTs at the end of the image list.\n
1528#@cli : 'clut_name' can be { 2-strip-process | 60s | 60s_faded | 60s_faded_alt | 7drk_21 | action_magenta_01 | \
1529# action_red_01 | adventure_1453 | agfa_apx_100 | agfa_apx_25 | agfa_precisa_100 | agfa_ultra_color_100 | \
1530# agfa_vista_200 | agressive_highligjtes_recovery_5 | alberto_street | alien_green | amstragram | amstragram+ | \
1531# analog_film_1 | analogfx_anno_1870_color | analogfx_old_style_i | analogfx_old_style_ii | analogfx_old_style_iii | \
1532# analogfx_sepia_color | analogfx_soft_sepia_i | analogfx_soft_sepia_ii | anime | apocalypse_this_very_moment | \
1533# aqua | aqua_and_orange_dark | arabica_12 | atomic_pink | autumn | ava_614 | avalanche | azrael_93 | bboyz_2 | \
1534# bc_darkum | beach_aqua_orange | beach_faded_analog | berlin_sky | black_and_white | black_star | black_white_01 | \
1535# black_white_02 | black_white_03 | black_white_04 | black_white_05 | black_white_06 | blade_runner | bleach_bypass | \
1536# bleachbypass_1 | bleachbypass_2 | bleachbypass_3 | bleachbypass_4 | bleech_bypass_green | bleech_bypass_yellow_01 | \
1537# blue_cold_fade | blue_dark | blue_house | blue_ice | blue_mono | blue_shadows_01 | blues | bob_ford | bourbon_64 | \
1538# bright_green_01 | bright_teal_orange | bright_warm | brightgreen | brown_mobster | brownbm | brownish | bw_1 | \
1539# 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 | \
1540# chemical_168 | chrome_01 | cineblue | cinebm_4k | cinema | cinema_2 | cinema_3 | cinema_4 | cinema_5 | \
1541# cinema_noir | cinematic-1 | cinematic-10 | cinematic-2 | cinematic-3 | cinematic-4 | cinematic-5 | cinematic-6 | \
1542# cinematic-7 | cinematic-8 | cinematic-9 | cinematic_01 | cinematic_02 | cinematic_03 | cinematic_for_flog | \
1543# cinematic_forest | cinematic_lady_bird | cinematic_mexico | city | city_7 | city_dust | classic_films_01 | \
1544# classic_films_02 | classic_films_03 | classic_films_04 | classic_films_05 | classic_teal_and_orange | clayton_33 | \
1545# clear_teal_fade | clouseau_54 | cobi_3 | coffee_44 | cold_clear_blue | cold_clear_blue_1 | cold_ice | \
1546# cold_simplicity_2 | color_rich | colorful_0209 | colornegative | conflict_01 | contrail_35 | \
1547# contrast_with_highlights_protection | contrasty_afternoon | contrasty_green | crispromance | crispwarm | \
1548# crispwinter | cross_process_cp_130 | cross_process_cp_14 | cross_process_cp_15 | cross_process_cp_16 | \
1549# cross_process_cp_18 | cross_process_cp_3 | cross_process_cp_4 | cross_process_cp_6 | crushin | cubicle_99 | d_o_1 | \
1550# dark_blues_in_sunlight | dark_green_02 | dark_green_1 | dark_man_x | dark_orange_teal | dark_place_01 | darkness | \
1551# date_39 | day_4nite | day_for_night | day_to_night_kings_blue | deep | deep_blue | deep_dark_warm | \
1552# deep_high_contrast | deep_teal_fade | deep_warm_fade | deepskintones_2 | deepskintones_3 | delicatessen | \
1553# denoiser_simple_40 | desert_gold_37 | dimension | directions_23 | django_25 | domingo_145 | dream_1 | dream_85 | \
1554# drop_green_tint_14 | dropblues | duotone_blue_red | earth_tone_boost | edgyember | elegance_38 | enchanted | \
1555# eterna_for_flog | expired_69 | expired_fade | expired_polaroid | extreme | fade | fade_to_green | faded | \
1556# faded_47 | faded_alt | faded_analog | faded_extreme | faded_green | faded_pink-ish | faded_print | faded_retro_01 | \
1557# faded_retro_02 | faded_vivid | fadedlook | fallcolors | faux_infrared | faux_infrared_bw_1 | \
1558# faux_infrared_color_p_2 | faux_infrared_color_p_3 | faux_infrared_color_r_0a | faux_infrared_color_r_0b | \
1559# faux_infrared_color_yp_1 | fgcinebasic | fgcinebright | fgcinecold | fgcinedrama | fgcinetealorange_1 | \
1560# fgcinetealorange_2 | fgcinevibrant | fgcinewarm | film_0987 | film_9879 | film_gb-19 | film_high_contrast | \
1561# film_print_01 | film_print_02 | filmic | flat_30 | flat_blue_moon | flavin | foggynight | folger_50 | formula_b | \
1562# french_comedy | frosted | frostedbeachpicnic | fuji_160c | fuji_160c_+ | fuji_160c_++ | fuji_160c_- | \
1563# fuji_3510_constlclip | fuji_3510_constlmap | fuji_3510_cuspclip | fuji_3513_constlclip | fuji_3513_constlmap | \
1564# fuji_3513_cuspclip | fuji_400h | fuji_400h_+ | fuji_400h_++ | fuji_400h_- | fuji_800z | fuji_800z_+ | \
1565# fuji_800z_++ | fuji_800z_- | fuji_astia_100_generic | fuji_astia_100f | fuji_fp-100c | fuji_fp-100c_+ | \
1566# fuji_fp-100c_++ | fuji_fp-100c_+++ | fuji_fp-100c_++_alt | fuji_fp-100c_- | fuji_fp-100c_-- | fuji_fp-100c_alt | \
1567# fuji_fp-100c_cool | fuji_fp-100c_cool_+ | fuji_fp-100c_cool_++ | fuji_fp-100c_cool_- | fuji_fp-100c_cool_-- | \
1568# fuji_fp-100c_negative | fuji_fp-100c_negative_+ | fuji_fp-100c_negative_++ | fuji_fp-100c_negative_+++ | \
1569# fuji_fp-100c_negative_++_alt | fuji_fp-100c_negative_- | fuji_fp-100c_negative_-- | fuji_fp-3000b | \
1570# fuji_fp-3000b_+ | fuji_fp-3000b_++ | fuji_fp-3000b_+++ | fuji_fp-3000b_- | fuji_fp-3000b_-- | fuji_fp-3000b_hc | \
1571# fuji_fp-3000b_negative | fuji_fp-3000b_negative_+ | fuji_fp-3000b_negative_++ | fuji_fp-3000b_negative_+++ | \
1572# fuji_fp-3000b_negative_- | fuji_fp-3000b_negative_-- | fuji_fp-3000b_negative_early | fuji_fp_100c | fuji_hdr | \
1573# fuji_neopan_1600 | fuji_neopan_1600_+ | fuji_neopan_1600_++ | fuji_neopan_1600_- | fuji_neopan_acros_100 | \
1574# fuji_provia_100_generic | fuji_provia_100f | fuji_provia_400f | fuji_provia_400x | fuji_sensia_100 | \
1575# fuji_superia_100 | fuji_superia_100_+ | fuji_superia_100_++ | fuji_superia_100_- | fuji_superia_1600 | \
1576# fuji_superia_1600_+ | fuji_superia_1600_++ | fuji_superia_1600_- | fuji_superia_200 | fuji_superia_200_xpro | \
1577# fuji_superia_400 | fuji_superia_400_+ | fuji_superia_400_++ | fuji_superia_400_- | fuji_superia_800 | \
1578# fuji_superia_800_+ | fuji_superia_800_++ | fuji_superia_800_- | fuji_superia_hg_1600 | fuji_superia_reala_100 | \
1579# fuji_superia_x-tra_800 | fuji_velvia_100_generic | fuji_velvia_50 | fuji_xtrans_iii_acros | \
1580# fuji_xtrans_iii_acros+g | fuji_xtrans_iii_acros+r | fuji_xtrans_iii_acros+ye | fuji_xtrans_iii_astia | \
1581# fuji_xtrans_iii_classic_chrome | fuji_xtrans_iii_mono | fuji_xtrans_iii_mono+g | fuji_xtrans_iii_mono+r | \
1582# fuji_xtrans_iii_mono+ye | fuji_xtrans_iii_pro_neg_hi | fuji_xtrans_iii_pro_neg_std | fuji_xtrans_iii_provia | \
1583# fuji_xtrans_iii_sepia | fuji_xtrans_iii_velvia | fusion_88 | futuristicbleak_1 | futuristicbleak_2 | \
1584# futuristicbleak_3 | futuristicbleak_4 | going_for_a_walk | golden | golden_bright | golden_fade | golden_mono | \
1585# golden_night_softner_43 | golden_sony_37 | golden_vibrant | goldengate | goldentime | goldfx_bright_spring_breeze | \
1586# goldfx_bright_summer_heat | goldfx_hot_summer_heat | goldfx_perfect_sunset_01min | goldfx_perfect_sunset_05min | \
1587# goldfx_perfect_sunset_10min | goldfx_spring_breeze | goldfx_summer_heat | good_morning | green_15 | green_2025 | \
1588# green_action | green_afternoon | green_and_orange | green_blues | green_conflict | green_day_01 | green_day_02 | \
1589# green_g_09 | green_indoor | green_light | green_mono | green_yellow | greenish_contrasty | greenish_fade | \
1590# greenish_fade_1 | gremerta | hackmanite | hallowen_dark | happyness_133 | hard_teal_orange | harsh_day | \
1591# harsh_sunset | helios | herderite | heulandite | hiddenite | highlights_protection | hilutite | hitman | hlg_1_1 | \
1592# honey_light | hong_kong | horrorblue | howlite | hydracore | hyla_68 | hypersthene | hypnosis | hypressen | \
1593# ilford_delta_100 | ilford_delta_3200 | ilford_delta_3200_+ | ilford_delta_3200_++ | ilford_delta_3200_- | \
1594# ilford_delta_400 | ilford_fp_4_plus_125 | ilford_hp_5 | ilford_hp_5_+ | ilford_hp_5_++ | ilford_hp_5_- | \
1595# ilford_hp_5_plus_400 | ilford_hps_800 | ilford_pan_f_plus_50 | ilford_xp_2 | indoor_blue | industrial_33 | \
1596# infrared_-_dust_pink | instantc | justpeachy | jwick_21 | k_tone_vintage_kodachrome | kh_1 | kh_10 | kh_2 | kh_3 | \
1597# kh_4 | kh_5 | kh_6 | kh_7 | kh_8 | kh_9 | killstreak | kodak_2383_constlclip | kodak_2383_constlmap | \
1598# kodak_2383_cuspclip | kodak_2393_constlclip | kodak_2393_constlmap | kodak_2393_cuspclip | kodak_bw_400_cn | \
1599# kodak_e-100_gx_ektachrome_100 | kodak_ektachrome_100_vs | kodak_ektachrome_100_vs_generic | kodak_ektar_100 | \
1600# kodak_elite_100_xpro | kodak_elite_chrome_200 | kodak_elite_chrome_400 | kodak_elite_color_200 | \
1601# kodak_elite_color_400 | kodak_elite_extracolor_100 | kodak_hie_hs_infra | kodak_kodachrome_200 | \
1602# kodak_kodachrome_25 | kodak_kodachrome_64 | kodak_kodachrome_64_generic | kodak_portra_160 | kodak_portra_160_+ | \
1603# kodak_portra_160_++ | kodak_portra_160_- | kodak_portra_160_nc | kodak_portra_160_nc_+ | kodak_portra_160_nc_++ | \
1604# kodak_portra_160_nc_- | kodak_portra_160_vc | kodak_portra_160_vc_+ | kodak_portra_160_vc_++ | \
1605# kodak_portra_160_vc_- | kodak_portra_400 | kodak_portra_400_+ | kodak_portra_400_++ | kodak_portra_400_- | \
1606# kodak_portra_400_nc | kodak_portra_400_nc_+ | kodak_portra_400_nc_++ | kodak_portra_400_nc_- | \
1607# kodak_portra_400_uc | kodak_portra_400_uc_+ | kodak_portra_400_uc_++ | kodak_portra_400_uc_- | \
1608# kodak_portra_400_vc | kodak_portra_400_vc_+ | kodak_portra_400_vc_++ | kodak_portra_400_vc_- | kodak_portra_800 | \
1609# kodak_portra_800_+ | kodak_portra_800_++ | kodak_portra_800_- | kodak_portra_800_hc | kodak_t-max_100 | \
1610# kodak_t-max_3200 | kodak_t-max_400 | kodak_tmax_3200 | kodak_tmax_3200_+ | kodak_tmax_3200_++ | kodak_tmax_3200_- | \
1611# kodak_tmax_3200_alt | kodak_tri-x_400 | kodak_tri-x_400_+ | kodak_tri-x_400_++ | kodak_tri-x_400_- | \
1612# kodak_tri-x_400_alt | korben_214 | landscape_01 | landscape_02 | landscape_03 | landscape_04 | landscape_05 | \
1613# landscape_1 | landscape_10 | landscape_2 | landscape_3 | landscape_4 | landscape_5 | landscape_6 | landscape_7 | \
1614# landscape_8 | landscape_9 | lateafternoonwanderlust | latesunset | lc_1 | lc_10 | lc_2 | lc_3 | lc_4 | lc_5 | \
1615# lc_6 | lc_7 | lc_8 | lc_9 | lenox_340 | life_giving_tree | light_blown | lomo | lomography_redscale_100 | \
1616# lomography_x-pro_slide_200 | london_nights | louetta | low_contrast_blue | low_key_01 | lucky_64 | \
1617# lushgreensummer | magenta_day | magenta_day_01 | magenta_dream | magenta_yellow | magentacoffee | matrix | \
1618# mckinnon_75 | memories | metropolis | milo_5 | minimalistcaffeination | modern_film | modern_films_01 | \
1619# modern_films_02 | modern_films_03 | modern_films_04 | modern_films_05 | modern_films_06 | modern_films_07 | \
1620# mono_tinted | monochrome | monochrome_1 | monochrome_2 | moody_1 | moody_10 | moody_2 | moody_3 | moody_4 | \
1621# moody_5 | moody_6 | moody_7 | moody_8 | moody_9 | moonlight | moonlight_01 | moonrise | morning_6 | morroco_16 | \
1622# mostly_blue | moviz_1 | moviz_10 | moviz_11 | moviz_12 | moviz_13 | moviz_14 | moviz_15 | moviz_16 | moviz_17 | \
1623# moviz_18 | moviz_19 | moviz_2 | moviz_20 | moviz_21 | moviz_22 | moviz_23 | moviz_24 | moviz_25 | moviz_26 | \
1624# moviz_27 | moviz_28 | moviz_29 | moviz_3 | moviz_30 | moviz_31 | moviz_32 | moviz_33 | moviz_34 | moviz_35 | \
1625# moviz_36 | moviz_37 | moviz_38 | moviz_39 | moviz_4 | moviz_40 | moviz_41 | moviz_42 | moviz_43 | moviz_44 | \
1626# moviz_45 | moviz_46 | moviz_47 | moviz_48 | moviz_5 | moviz_6 | moviz_7 | moviz_8 | moviz_9 | mute_shift | \
1627# muted_01 | muted_fade | mysticpurplesunset | nah | natural_vivid | nemesis | neon_770 | neutral_pump | \
1628# neutral_teal_orange | neutral_warm_fade | newspaper | night_01 | night_blade_4 | night_king_141 | night_spy | \
1629# nightfromday | nightlife | nostalgiahoney | nostalgic | nw-1 | nw-10 | nw-2 | nw-3 | nw-4 | nw-5 | nw-6 | nw-7 | \
1630# nw-8 | nw-9 | old_west | once_upon_a_time | only_red | only_red_and_blue | operation_yellow | orange_dark_4 | \
1631# orange_dark_7 | orange_dark_look | orange_tone | orange_underexposed | oranges | paladin | paladin_1875 | \
1632# pasadena_21 | passing_by | pink_fade | pitaya_15 | pmcinematic_01 | pmcinematic_02 | pmcinematic_03 | \
1633# pmcinematic_04 | pmcinematic_05 | pmcinematic_06 | pmcinematic_07 | pmnight_01 | pmnight_02 | pmnight_03 | \
1634# pmnight_04 | pmnight_05 | polaroid_664 | polaroid_665 | polaroid_665_+ | polaroid_665_++ | polaroid_665_- | \
1635# polaroid_665_-- | polaroid_665_negative | polaroid_665_negative_+ | polaroid_665_negative_- | \
1636# polaroid_665_negative_hc | polaroid_667 | polaroid_669 | polaroid_669_+ | polaroid_669_++ | polaroid_669_+++ | \
1637# polaroid_669_- | polaroid_669_-- | polaroid_669_cold | polaroid_669_cold_+ | polaroid_669_cold_- | \
1638# polaroid_669_cold_-- | polaroid_672 | polaroid_690 | polaroid_690_+ | polaroid_690_++ | polaroid_690_- | \
1639# polaroid_690_-- | polaroid_690_cold | polaroid_690_cold_+ | polaroid_690_cold_++ | polaroid_690_cold_- | \
1640# polaroid_690_cold_-- | polaroid_690_warm | polaroid_690_warm_+ | polaroid_690_warm_++ | polaroid_690_warm_- | \
1641# polaroid_690_warm_-- | polaroid_polachrome | polaroid_px-100uv+_cold | polaroid_px-100uv+_cold_+ | \
1642# polaroid_px-100uv+_cold_++ | polaroid_px-100uv+_cold_+++ | polaroid_px-100uv+_cold_- | polaroid_px-100uv+_cold_-- | \
1643# polaroid_px-100uv+_warm | polaroid_px-100uv+_warm_+ | polaroid_px-100uv+_warm_++ | polaroid_px-100uv+_warm_+++ | \
1644# polaroid_px-100uv+_warm_- | polaroid_px-100uv+_warm_-- | polaroid_px-680 | polaroid_px-680_+ | polaroid_px-680_++ | \
1645# polaroid_px-680_- | polaroid_px-680_-- | polaroid_px-680_cold | polaroid_px-680_cold_+ | polaroid_px-680_cold_++ | \
1646# polaroid_px-680_cold_++_alt | polaroid_px-680_cold_- | polaroid_px-680_cold_-- | polaroid_px-680_warm | \
1647# polaroid_px-680_warm_+ | polaroid_px-680_warm_++ | polaroid_px-680_warm_- | polaroid_px-680_warm_-- | \
1648# polaroid_px-70 | polaroid_px-70_+ | polaroid_px-70_++ | polaroid_px-70_+++ | polaroid_px-70_- | polaroid_px-70_-- | \
1649# polaroid_px-70_cold | polaroid_px-70_cold_+ | polaroid_px-70_cold_++ | polaroid_px-70_cold_- | \
1650# polaroid_px-70_cold_-- | polaroid_px-70_warm | polaroid_px-70_warm_+ | polaroid_px-70_warm_++ | \
1651# polaroid_px-70_warm_- | polaroid_px-70_warm_-- | polaroid_time_zero_expired | polaroid_time_zero_expired_+ | \
1652# polaroid_time_zero_expired_++ | polaroid_time_zero_expired_- | polaroid_time_zero_expired_-- | \
1653# polaroid_time_zero_expired_--- | polaroid_time_zero_expired_cold | polaroid_time_zero_expired_cold_- | \
1654# polaroid_time_zero_expired_cold_-- | polaroid_time_zero_expired_cold_--- | portrait_1 | portrait_10 | portrait_2 | \
1655# portrait_3 | portrait_4 | portrait_5 | portrait_6 | portrait_7 | portrait_8 | portrait_9 | progressen | \
1656# protect_highlights_01 | prussian_blue | pseudogrey | purple | purple_2 | red_afternoon_01 | red_day_01 | \
1657# red_dream_01 | redblueyellow | reds | reds_oranges_yellows | reeve_38 | remy_24 | rest_33 | retro | \
1658# retro_brown_01 | retro_magenta_01 | retro_summer_3 | retro_yellow_01 | rollei_ir_400 | rollei_ortho_25 | \
1659# rollei_retro_100_tonal | rollei_retro_80s | rotate_muted | rotate_vibrant | rotated | rotated_crush | \
1660# saturated_blue | saving_private_damon | science_fiction | sea | serenity | seringe_4 | serpent | \
1661# seventies_magazine | sevsuz | shade_kings_ink | shadow_king_39 | shine | skin_tones | smart_contrast | smokey | \
1662# smooth_clear | smooth_cromeish | smooth_fade | smooth_green_orange | smooth_sailing | smooth_teal_orange | \
1663# soft_fade | softwarming | solarized_color | solarized_color_2 | springmorning | sprocket_231 | spy_29 | street | \
1664# studio_skin_tone_shaper | subtle_blue | subtle_green | subtle_yellow | summer | summer_alt | sunlightlove | sunny | \
1665# sunny_alt | sunny_rich | sunny_warm | sunset_aqua_orange | sunset_intense_violet_blue | sunset_violet_mood | \
1666# super_warm | super_warm_rich | sutro_fx | sweet_bubblegum | sweet_gelatto | taiga | tarraco | teal_fade | \
1667# teal_moonlight | tealmagentagold | tealorange | tealorange_1 | tealorange_2 | tealorange_3 | \
1668# technicalfx_backlight_filter | teigen_28 | tensiongreen_1 | tensiongreen_2 | tensiongreen_3 | tensiongreen_4 | \
1669# terra_4 | the_matrices | thriller_2 | toastedgarden | trent_18 | true_colors_8 | turkiest_42 | tweed_71 | \
1670# ultra_water | undeniable | undeniable_2 | unknown | urban_01 | urban_02 | urban_03 | urban_04 | urban_05 | \
1671# urban_cowboy | uzbek_bukhara | uzbek_marriage | uzbek_samarcande | velvetia | very_warm_greenish | vfb_21 | \
1672# vibrant | vibrant_alien | vibrant_contrast | vibrant_cromeish | victory | vintage | vintage_01 | vintage_02 | \
1673# vintage_03 | vintage_04 | vintage_05 | vintage_163 | vintage_alt | vintage_brighter | vintage_chrome | \
1674# vintage_mob | vintage_warmth_1 | violet_taste | vireo_37 | warm | warm_dark_contrasty | warm_fade | warm_fade_1 | \
1675# warm_highlight | warm_neutral | warm_sunset_red | warm_teal | warm_vintage | warm_yellow | well_see | western | \
1676# westernlut_2 | whiter_whites | winterlighthouse | wipe | wooden_gold_20 | yellow_55b | yellow_film_01 | \
1677# yellowstone | you_can_do_it | zed_32 | zeke_39 | zilverfx_bw_solarization | zilverfx_infrared | \
1678# zilverfx_vintage_bw }#@cli : Default values: 'resolution=33' and 'cut_and_round=1'.
1679#@cli : $ clut summer clut alien_green,17 clut orange_dark4,48
1680+clut : check "isnum(${2=33}) && $2>0 && isbool(${3=1})"
1681  to_clutname "$1" name=${} l[]
1682  e[^-1] "Input CLUT '"$name"' with resolution $2."
1683    path_clut=${-path_cache}
1684    if isfile(['{/${path_clut}clut_$name.cimgz}']) i ${path_clut}clut_$name.cimgz fi
1685    if $!"!=1 || w<$2 || h<$2 || d<$2" # Decompression needed
1686      rm input_cached gmic_cluts.gmz k[${"nmd 1,"$name}]
1687      if $!!=1
1688        rm i https://gmic.eu/gmic_cluts.gmz o ${path_clut}gmic_cluts.gmz # Try getting newest version of the CLUTs file
1689        repeat $! if ['{$>,n}']==['$name'] k[$>] break fi done
1690        if $!!=1
1691          error[0--5] "Command '$0': Unknown CLUT name '"$name"'."
1692        fi
1693      fi
1694      decompress_clut $2,$2,$2
1695      if $3 round c 0,255 to_rgb fi
1696      o. ${path_clut}clut_$name.cimgz
1697    elif "w>$2 || h>$2 || d>$2" r $2,$2,$2,3,2 # Downsize from higher resolution
1698    fi
1699    nm $name k.
1700  endl
1701
1702# [Internal] Use this command to 'clean' a .gmz file that represents CLUT keypoints.
1703# What it does is:
1704#
1705# - Standardize CLUT name.
1706# - Convert RGB CLUTs to Grayscale when possible.
1707# - Remove duplicates and sort by lexicographic order.
1708# - Display list of CLUTs to ease documentation update of command 'clut'.
1709#
1710clean_cluts :
1711  e[^-1] "Clean CLUT dataset.\n"
1712  round c 0,255
1713
1714  # Standardize names.
1715  repeat $! l[$>]
1716    nm={n}
1717
1718    # Standardize names for 'Moviz'.
1719    if "str = lowercase(['"$nm"*']);
1720        find(str,'tpf_-_cinematica_')==0" l[]
1721       if !narg($tpf) tpf=1 fi
1722       ('moviz_$tpf')
1723       tpf+=1
1724       nm={t} rm
1725    endl fi
1726    if "str = lowercase(['"$nm"*']);
1727        find(str,'_-_standard-vk')>=0" l[]
1728       if !narg($tpf) tpf=1 fi
1729       ('moviz_$tpf')
1730       tpf+=1
1731       nm={t} rm
1732    endl fi
1733
1734    # Standardize names for 'SmallHD MovieLook'.
1735    if "str = lowercase(['"$nm"*']);
1736        find(str,'_-_rec_709*')>=0" l[]
1737      ('$nm') z. 0,{w-11}
1738      nm={t} rm
1739    endl fi
1740
1741    # Standardize names for 'SmallHD MovieLook'.
1742    if "str = lowercase(['"$nm"']);
1743        find(str,'smallhd_movielook_')==0" l[]
1744      ('$nm') z. 18,100%
1745      replace_str "apocalypsethisverymoment","apocalypse_this_very_moment"
1746      replace_str "bobford","bob_ford"
1747      replace_str "lifegivingtree","life_giving_tree"
1748      replace_str "savingprivatedamon","saving_private_damon"
1749      replace_str "thematrices","the_matrices"
1750      nm={t} rm
1751    endl fi
1752
1753    # Standardize names for 'Fuji XTrans III'.
1754    if "str = lowercase(['"$nm"']);
1755        find(str,'fuji_xtrans_iii')==0" l[]
1756      ('$nm')
1757      replace_str "_-_","_" nm={t} rm
1758    endl fi
1759
1760    # Standardize names for 'RawTherapee'.
1761    if "str = lowercase(['"$nm"']);
1762        find(str,'kodak')==0 ||
1763        find(str,'polaroid')==0 ||
1764        find(str,'fuji')==0 ||
1765        find(str,'ilford')==0" l[]
1766      ({'$nm'},{'*'})
1767      replace_str " ","_"
1768      replace_str "xp_2","xp2"
1769      replace_str "hp_5","hp5"
1770      repeat 8 n={1+$>}
1771        replace_str "_"${n}"_+","_+"
1772        replace_str "_"${n}"_-","_-"
1773        replace_str "_"${n}"_alt","_alt"
1774        replace_str "_"${n}"_Alt","_alt"
1775        replace_str "_"${n}"*","*"
1776      done
1777      = 0,0,100% nm={t} rm
1778    endl fi
1779
1780    to_clutname $nm nm=${}
1781
1782    # Standardize names for 'PictureFX'.
1783    if "str = lowercase(['"$nm"']);
1784        find(str,'technicalfx')==0 ||
1785        find(str,'picturefx')==0 ||
1786        find(str,'analogfx')==0 ||
1787        find(str,'goldfx')==0 ||
1788        find(str,'zilverfx')==0" l[]
1789      ('$nm')
1790      replace_str "-","_" nm={t} rm
1791    endl fi
1792
1793    # Other name changes.
1794    l[]
1795      ({'$nm'},{'*'})
1796      replace_str "_v_2*","*"
1797      replace_str "_v_1*","*"
1798      replace_str "_*","*"
1799      replace_str "_b_w","_bw"
1800      replace_str "&",""
1801      replace_str "rec_709_-_","rec709_"
1802      replace_str "s-log","slog"
1803      replace_str "__","_"
1804      replace_str "action_-_","action_"
1805      replace_str "-version-",""
1806      replace_str "picturefx_",""
1807      = 0,0,100%  nm={t} rm
1808    endl
1809    nm $nm
1810  endl done
1811
1812  # Convert RGB CLUTs to Grayscale when possible.
1813  repeat $! l[$>]
1814    if "ref(crop(#0,0,0,0,3,1,h,1,1),R);
1815        ref(crop(#0,0,0,0,4,1,h,1,1),G);
1816        ref(crop(#0,0,0,0,5,1,h,1,1),B);
1817        R==G && G==B?1:0"
1818      channels 0,3
1819    fi
1820  endl done
1821
1822  # Search for duplicates and sort.
1823  p=0 for $p<$!
1824    nm0={$p,n}
1825    e[] "\r- Search duplicates for ["$p"] = '"$nm0"'                        "
1826    q={$p+1} for $q<$!
1827      nm={$q,n}
1828      if ['$nm0']==['$nm']
1829        e[] "   > Found duplicate ["$q"] -> Original 1x"{$p,h}", new 1x"{$q,h}"\n"
1830        rv[$p,$q] rm[$q]
1831      else q+=1
1832      fi
1833    done
1834    p+=1
1835  done
1836  sort_list +,n
1837
1838  # Display all clut names.
1839  doc="#@cli clut : \"clut_name\",_resolution>0,_cut_and_round={ 0=no | 1=yes }\n"\
1840      "#@cli : Insert one of the "$!" pre-defined CLUTs at the end of the image list.\\n\n"\
1841      "#@cli : 'clut_name' can be {" sep="|"
1842  nbc=28
1843  repeat $! l[$>]
1844    if !$< sep="}" fi
1845    str=" "{n}" "$sep
1846    s_str={size(['$str'])}
1847    nbc+=$s_str
1848    if $nbc<118
1849      doc=${doc}${str}
1850    else
1851      doc=${doc}" \\\n#"${str}
1852      nbc={1+$s_str}
1853    fi
1854  endl done
1855
1856  doc=${doc}"\n"\
1857      "#@cli : Default values: 'resolution=33' and 'cut_and_round=1'.\n"\
1858      "#@cli : $ clut summer clut alien_green,17 clut orange_dark4,48\n"
1859  e[] "\n"$doc
1860
1861#@cli m : eq. to 'command'. : (+)
1862
1863#@cli command : _add_debug_info={ 0 | 1 },{ filename | http[s]://URL | "string" } : (+)
1864#@cli : Import G'MIC custom commands from specified file, URL or string.
1865#@cli : (eq. to 'm').\n
1866#@cli : Imported commands are available directly after the 'command' invocation.
1867#@cli : Default value: 'add_debug_info=1'.
1868#@cli : $ image.jpg command "foo : mirror y deform $""1" +foo[0] 5 +foo[0] 15
1869
1870# compress_gmic
1871# Compress .gmic custom command files for compressing update files a little bit,
1872# by removing empty lines, and useless comments.
1873compress_gmic :
1874  merge_multiline_comments
1875  merge_multiline
1876  eval " # Remove useless comments
1877    p = 0;
1878    while (p<h,
1879      q = find(#-1,_'\n',p)%h;
1880      p==q?++p:(
1881        i[p]==_'#'?( # Line starts with a '#'
1882          ref(crop(0,p,1,5),cr);
1883          cr!='#@gui' && cr!='#@cli' && cr!='#@web'?copy(i[p],-1,q-p+1,1,0); # Remove comment line
1884        ):( # Line doesn't start with a '#' -> look for comment at line end
1885          l = find(#-1,_'#',p);
1886          l>=0 && l<q && i[l-1]<=_' '?(
1887            while (i[--l]<=_' ',0);
1888            copy(i[l+1],-1,q-l-1,1,0);
1889          );
1890          0;
1891        );
1892        p = q + 1;
1893      );
1894    )"
1895  discard -1
1896  autocrop. {_'\n'}
1897  eval. "* # Merge consecutive line feeds
1898         i==_'\n' && j[1]==_'\n'?(i()=-1)"
1899  discard. -1
1900  eval. "* # Remove leading spaces on each line
1901         i<=_' ' && i!=_'\n' && j[-1]==_'\n'?(
1902          for (p = 1, j[p] && j[p]<=_' ' && j[p]!=_'\n', ++p); copy(i(),-1,p,1,0))"
1903  discard. -1
1904
1905#@cli cursor : _mode = { 0=hide | 1=show } : (+)
1906#@cli : Show or hide mouse cursor for selected instant display windows.
1907#@cli : Command selection (if any) stands for instant display window indices instead of image indices.
1908#@cli : Default value: 'mode=1'.
1909
1910# csv2ts : _output_locale
1911# Convert selected buffers from .csv format to .ts format (translation files for G'MIC-Qt).
1912# Default value: 'output_locale=fr'.
1913csv2ts : skip "${1=fr}"
1914  e[^-1] "Convert buffer$? from .csv format to .ts format (in '$1' locale)."
1915  repeat $! l[$>] if w>1
1916    ('"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\n\n\
1917      "<!DOCTYPE TS>"\n\
1918      "<TS version=\"2.1\" language=\"$1\">"\n\
1919      "<context>"\n\
1920      "  <name>FilterTextTranslator</name>"\n\n':y)
1921    repeat h#0
1922      str={0,`I(0,$>)`}
1923      tr={0,`I(1,$>)`}
1924      if s=['$tr'];s!=0" && norm(s-=_' ')"
1925        ({'$str'}) autocrop. {'" "'} _gmd_ascii2html. str={t} rm.
1926        ({'$tr'}) autocrop. {'" "'} _gmd_ascii2html. tr={t} rm.
1927
1928        if {0,!i(2,$>)} # Global translation
1929          ('"    <message>"\n\
1930            "      <source>"$str"</source>"\n\
1931            "      <translation>"$tr"</translation>"\n\
1932            "    </message>"\n\n':y)
1933        else # Filter specialization
1934          i=2
1935          for {0,i($i,$>)}
1936            path={0,`I($i,$>)`} {('$path')} autocrop. {'" "'} _gmd_ascii2html. path={t} rm.
1937            ('"    <message>"\n\
1938              "      <source>"$str"</source>"\n\
1939              "      <comment>"{0,`I($i,$>)`}"</comment>"\n\
1940              "      <translation>"$tr"</translation>"\n\
1941              "    </message>"\n':y)
1942            i+=1
1943          done
1944          r. 1,{h+1},1,1,0,1 # Last item has two endl
1945        fi
1946      fi
1947    done
1948    ('"</context>\n</TS>"\n':y)
1949    a[^0] y k.
1950  fi endl done
1951
1952#@cli delete : filename1[,filename2,...] : (+)
1953#@cli : Delete specified filenames on disk. Multiple filenames must be separated by commas.
1954
1955#@cli d : eq. to 'display'. : (+)
1956
1957#@cli display : _X[%]>=0,_Y[%]>=0,_Z[%]>=0,_exit_on_anykey={ 0 | 1 } : (+)
1958#@cli : Display selected images in an interactive viewer (use the instant display window [0] if opened).
1959#@cli : (eq. to 'd').\n
1960#@cli : Arguments 'X','Y','Z' determine the initial selection view, for 3D volumetric images.
1961#@cli : Default value: 'X=Y=Z=0' and 'exit_on_anykey=0'.
1962#@cli : $$ https://gmic.eu/oldtutorial/_display
1963
1964#@cli d0 : eq. to 'display0'.
1965d0 :
1966  _gmic_s="$?" v + _display2d 0,0
1967
1968#@cli display0
1969#@cli : Display selected images without value normalization.
1970#@cli : (eq. to 'd0').
1971display0 :
1972  _gmic_s="$?" v + _display2d 0,0
1973
1974#@cli d2d : eq. to 'display2d'.
1975d2d :
1976  _gmic_s="$?" v + _display2d 0,1
1977
1978_d2d_core :
1979  _gmic_s="$?" _d2d_core=1 v + _display2d $1,1
1980
1981#@cli display2d
1982#@cli : Display selected 2d images in an interactive window.
1983#@cli : (eq. to 'd2d').
1984#@cli : This command is used by default by command 'display' when displaying 2d images.
1985#@cli : If selected image is a volumetric image, each slice is displayed on a separate display
1986#@cli : window (up to 10 images can be displayed simultaneously this way), with synchronized moves.
1987#@cli : When interactive window is opened, the following actions are possible:
1988#@cli : * Left mouse button: Create an image selection and zoom into it.
1989#@cli : * Middle mouse button, or CTRL+left mouse button: Move image.
1990#@cli : * Mouse wheel or PADD+/-: Zoom in/out.
1991#@cli : * Arrow keys: Move image left/right/up/down.
1992#@cli : * `CTRL + A`: Enable/disable transparency (show/hide alpha channel).
1993#@cli : * `CTRL + C`: Decrease window size.
1994#@cli : * `CTRL + D`: Increase window size.
1995#@cli : * `CTRL + F`: Toggle fullscreen mode.
1996#@cli : * `CTRL + N`: Change normalization mode (can be { none | normal | channel-by-channel }).
1997#@cli : * `CTRL + O`: Save a copy of the input image, as a numbered file 'gmic_xxxxxx.gmz'.
1998#@cli : * `CTRL + R`: Reset both window size and view.
1999#@cli : * `CTRL + S`: Save a screenshot of the current view, as a numbered file 'gmic_xxxxxx.png'.
2000#@cli : * `CTRL + SPACE`: Reset view.
2001#@cli : * `CTRL + X`: Show/hide axes.
2002#@cli : * `CTRL + Z`: Hold/release aspect ratio.
2003display2d :
2004  _gmic_s="$?" v + _$0 0,1
2005
2006# $1 = exit_on_single_click?, can be { 0=no | 1=yes }.
2007# $2 = default window normalization for display window #0.
2008_display2d : check "isbool(${1=0}) && isint(${2=1}) && $2>=0"
2009  e[0--3] "Start interactive display of 2d image"$_gmic_s"."
2010  m "_d2d_format :
2011       if $""#>=8 u {_([$""*])[0,4]:\\ }\\ ...\\ {_([$""*])[$""#-4,4]:\\ }
2012       else ('\"$""*\"') replace. {','},32 u {t} rm.
2013       fi"
2014  p
2015  repeat $! l[$>]
2016    ('{n}') if s=crop();find(s,_'.',size(s)-1,-1)>0 nm={-2,b}.{-2,x} else nm={-2,b} fi rm.
2017    nm img
2018
2019    if !w v - d v + break fi
2020    if d>10
2021      error[0--6] "Command 'display2d': Input image has "{d}" slices, cannot manage more than 10 simultaneous views."
2022    fi
2023    is_multiview={d>1}
2024    is_moderate_ratio={0,D=[w,h];max(D)/min(D)<6}
2025    may_have_alpha={s==2||s>=4}
2026    alpha_mode,axes_mode,fullscreen_mode,ratio_mode={s==1||s==3},1,0,$is_moderate_ratio
2027    normalization_mode={{*}?!!{*,n}:$2}
2028    posx,posy,sizx,sizy={0,[0,0,w,h]}
2029    is_bottom_text,wait_event,mx,my,omb=0,1,-1,-1,0
2030    xsel0,ysel0,xsel1,ysel1,notification=
2031    wnormalization0={*,n}
2032    fontsize,fontsize_notif=
2033
2034    if $is_multiview" && "!${-is_macos}
2035      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
2036      elif d<=4 wsiz0=${fitscreen\ .,,45%}
2037      else wsiz0=${fitscreen\ .,,30%}
2038      fi
2039      wsiz0=${fitscreen\ $wsiz0,1,,90%}
2040    else wsiz0=${fitscreen\ .}
2041    fi
2042
2043    if $is_multiview
2044      if narg($_d2d_names) l[] $_d2d_names repeat $! wname$>={$>,t} done rm endl fi
2045      com_iskey,com_flushkey,com_isvisible,com_isresized,com_idisp,sep=
2046      repeat {img,d}
2047        if narg($_d2d_names) w$>[] {{*$>,w}?[-1,-1]:[$wsiz0]},0,${wname$>}" ("{w}x{h}x{d}x{s}")"
2048        else w$>[] {{*$>,w}?[-1,-1]:[$wsiz0]},0,$nm" ("{w}x{h}x{d}x{s}")-#"$>
2049        fi
2050        com_iskey.=$sep"{*"$>",$""1}"
2051        com_flushkey.=$sep"{*"$>",-$""1}"
2052        com_isvisible.=$sep"{*"$>"}"
2053        com_isresized.=$sep"{*"$>",r}"
2054        com_idisp.=$sep"{*"$>",x}"
2055        sep=,
2056      done
2057      m "iskey : u {max("$com_iskey")}"\n\
2058        "flushkey : skip "$com_flushkey\n\
2059        "isvisible : u {min("$com_isvisible")}"\n\
2060        "isresized : u {max("$com_isresized")}"\n\
2061        "idisp : u {argmax("$com_idisp")}"
2062
2063      # Auto-arrange display window layout in multi-view mode.
2064      if !${-is_macos}
2065        ww,wh={[$wsiz0]+[8,40]}
2066        if d==2
2067          if {*,u}/$ww>{*,v}/$wh # Horizontal alignment
2068            ox,oy={round([max(0,{*,u}-2*$ww),max(0,{*,v}-$wh)]/2)}
2069            w0[] -1,-1,-1,-1,$ox,$oy
2070            w1[] -1,-1,-1,-1,{$ox+$ww},$oy
2071          else # Vertical alignment
2072            ox,oy={round([{*,u}-$ww,{*,v}-2*$wh]/2)}
2073            w0[] -1,-1,-1,-1,$ox,$oy
2074            w1[] -1,-1,-1,-1,$ox,{$oy+$wh}
2075          fi
2076        elif d<=4
2077          ox,oy={round([max(0,{*,u}-2*$ww),max(0,{*,v}-2*$wh)]/2)}
2078          ww,wh+=$ox,$oy
2079          w0[] -1,-1,-1,-1,$ox,$oy
2080          w1[] -1,-1,-1,-1,$ww,$oy
2081          w2[] -1,-1,-1,-1,$ox,$wh
2082          if d==4 w3[] -1,-1,-1,-1,$ww,$wh fi
2083        else
2084          ox,oy={round([max(0,{*,u}-3*$ww),max(0,{*,v}-(d>6?3:2)*$wh)]/2)}
2085          ww2,wh2={2*[$ww,$wh]+[$ox,$oy]}
2086          ww,wh+=$ox,$oy
2087          w0[] -1,-1,-1,-1,$ox,$oy
2088          w1[] -1,-1,-1,-1,$ww,$oy
2089          w2[] -1,-1,-1,-1,$ww2,$oy
2090          w3[] -1,-1,-1,-1,$ox,$wh
2091          w4[] -1,-1,-1,-1,$ww,$wh
2092          if d>5 w5[] -1,-1,-1,-1,$ww2,$wh fi
2093          if d>6 w6[] -1,-1,-1,-1,$ox,$wh2 fi
2094          if d>7 w7[] -1,-1,-1,-1,$ww,$wh2 fi
2095          if d>8 w8[] -1,-1,-1,-1,$ww2,$wh2 fi
2096        fi
2097      fi
2098
2099    else
2100      w[] {{*,w}?[-1,-1]:[$wsiz0]},0,$nm" ("{w}x{h}x{d}x{s}")"
2101      m "iskey : u {*,$""1}"\n\
2102        "flushkey : skip {*,-$""1}"\n\
2103        "isvisible : u {*}"\n\
2104        "isresized : u {*,r}"\n\
2105        "idisp : u 0"
2106    fi
2107
2108    repeat {img,d} cursor[$>] 0 done
2109
2110    # Start event loop.
2111    for ${-isvisible}" && "!${"iskey ESC"}" && "!((${"iskey CTRLLEFT"}" || "${"iskey CTRLRIGHT"})" && "${"iskey W"})
2112
2113      # Correct aspect ratio while centering image.
2114      if $ratio_mode" && "$sizx>16" && "$sizy>16
2115        nposx,nposy,nsizx,nsizy=$posx,$posy,$sizx,$sizy
2116        repeat 2
2117          if {*,w}/$sizx<{*,h}/$sizy
2118            nposy,nsizy={nsizy=$nsizx*{*,h}/{*,w};round([$nposy-(nsizy-$nsizy)/2,nsizy])}
2119          else
2120            nposx,nsizx={nsizx=$nsizy*{*,w}/{*,h};round([$nposx-(nsizx-$nsizx)/2,round(nsizx)])}
2121          fi
2122          if $<" && "$nsizx>w#0" && "$nsizy>h#0 nposx,nposy,nsizx,nsizy={[0,0,w#0,h#0]} fi
2123        done
2124        if [$nposx,$nposy,$nsizx,$nsizy]!=[$posx,$posy,$sizx,$sizy]
2125          posx,posy,sizx,sizy=$nposx,$nposy,$nsizx,$nsizy
2126          rmn baseview
2127        fi
2128      fi
2129
2130      # Generate baseview.
2131      if !narg($baseview)
2132
2133        # Get view corresponding to position and zoom factor.
2134        ($posx,{$posx+$sizx}) ($posy;{$posy+$sizy}) r[-2,-1] 2,2 a[-2,-1] c
2135        r. {[{*,w,h}]+1},1,2,3 z. 0,0,{[w,h]-2} round. 1,-1 ind_warp={$!-1}
2136        +channels[0] {0,[0,min(3,s-1)]} warp. ..,0,0,1
2137        if $is_multiview repeat {img,d-1} +slices[img] {1+$>} warp. [$ind_warp],0,0,1 done a[-{img,d}--1] z fi
2138        rm..
2139
2140        if s>($alpha_mode?4:3) channels. 0,{$alpha_mode?3:2} fi # Discard useless channels
2141
2142        # Check if image potentially contain inf or nan values.
2143        +r[0] 1,1,1,1,2 is_infnan={isnan(i)||isinf(i)} rm.
2144
2145        if $normalization_mode
2146          if $is_infnan
2147            # Find min and max values that are not nan of inf.
2148            eval[0] "*begin(m = inf; M = -inf);
2149              !isinf(i) && !isnan(i)?(m = min(i,m); M = max(i,M));
2150              end(
2151                merge(m,min);
2152                merge(M,max);
2153                run('im,iM=',m,',',M)
2154              )"
2155            vim,viM={[$im-abs($im),$iM+abs($iM)]}
2156            f. "isnan(i)?"$vim":isinf(i)?(i<0?"$vim":"$viM"):i"
2157          else
2158            im,iM={0,[im,iM]}
2159          fi
2160
2161        else
2162          im,iM=0,255
2163          if $is_infnan f. "isnan(i)?"$im":isinf(i)?(i<0?"$im":"$iM"):i" fi
2164        fi
2165
2166        # Normalize view.
2167        if $normalization_mode==1
2168          sh. 0,{s-($alpha_mode" && "$may_have_alpha?2:1)}
2169          if $is_multiview repeat {img,d} +slices. $> n. 0,255 j.. .,0,0,$> rm. done else n. 0,255 fi
2170          rm.
2171        elif $normalization_mode==2
2172          sh. 0,{s-($alpha_mode" && "$may_have_alpha?2:1)}
2173          if $is_multiview repeat d*s z,c={[$>%d,int($>/d)]} sh. $z,$z,$c n. 0,255 rm. done
2174          else repeat s sh. $> n. 0,255 rm. done
2175          fi
2176        fi
2177        if $posx<0" || "$posy<0" || "$posx+$sizx>=w#0" || "$posy+$sizy>=h#0
2178          100%,100%
2179          rectangle. {A=-[$posx,$posy]*[w,h]/[$sizx,$sizy];\
2180                      B=A+[w#0,h#0]*[w,h]/[$sizx,$sizy]-1;\
2181                      [ceil(A),floor(B)]},1,1
2182          *[-2,-1]
2183        fi
2184
2185        # Add alpha channel if necessary.
2186        if $alpha_mode
2187          coords={A=-[$posx,$posy]*[w,h]/[$sizx,$sizy];\
2188                  B=A+[w#0,h#0]*[w,h]/[$sizx,$sizy]-1;\
2189                  [ceil(A),floor(B)]}
2190          if !$may_have_alpha # Alpha mode without alpha channel -> Add alpha channel
2191            100%,100%,1,1,64 rectangle. $coords,1,255 r. 100%,100%,.. a[-2,-1] c
2192          else # Alpha mode with alpha channel
2193            sh. 100% 100%,100%,1,1,64 rectangle. $coords,1,0 r. 100%,100%,.. +[-2,-1] rm.
2194          fi
2195        fi
2196
2197        # Render image with transparency pattern.
2198        if $alpha_mode
2199          (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]
2200        elif s==1 r. 100%,100%,100%,3
2201        elif s==2 r. 100%,100%,100%,3,0
2202        fi
2203        nm. baseview
2204        rmn view
2205      fi
2206
2207      # Manage notifications.
2208      if narg($notification)
2209        wait_event=0
2210        if !isnum($notification) # Create notification gfx
2211          rmn notification_gfx
2212          ofs,fs={narg($fontsize_notif)?0$fontsize_notif:32}
2213          do
2214            0 t. {``$notification},0,0,$fs,1,255
2215            if narg($fontsize_notif) break
2216            elif {baseview,"(w#-1>0.7*w || h#-1>0.25*h) && "$fs>13" && "$ofs>=$fs}
2217              ofs,fs=$fs,{max(13,round($fs/1.25))} rm.
2218            elif {baseview,"w#-1<0.3*w && h#-1<0.25*h && "$fs<64" && "$ofs<=$fs}
2219              ofs,fs=$fs,{min(64,round($fs*1.25))} rm.
2220            else
2221              fontsize_notif=$fs break
2222            fi
2223          while 1
2224
2225          r. {[w+12,h+8]},1,1,0,0,0.5,0.5 rectangle. 0,0,100%,100%,1,0xFFFFFFFF,255 to_rgb.
2226          nm. notification_gfx
2227          notification=$|
2228        else
2229          if $|>$notification+1 rm[notification_gfx] wait_event=1 notification= fi
2230          rmn view
2231        fi
2232      fi
2233
2234      # Generate view.
2235      if !narg($view)
2236        [baseview]
2237        if $mx>=0
2238          posmx,posmy={floor([$posx,$posy]+[$mx,$my]*[$sizx,$sizy]/[{*,w,h}])}
2239          is_selection_a_point={[0$xsel0,0$ysel0]==[0$xsel1,0$ysel1]}
2240
2241          if narg($xsel0)" && "!$is_selection_a_point
2242            dselx,dsely={[$xsel1-$xsel0,$ysel1-$ysel0]}
2243            ofs,fs={narg($fontsize)?0$fontsize:32}
2244            do
2245              0 t. " Box ( "{``{[min($xsel0,$xsel1),min($ysel0,$ysel1)]}}" ) - "\
2246                   "( "{``{[max($xsel0,$xsel1),max($ysel0,$ysel1)]}}" ) \n"\
2247                   " Size = ( "{``{abs([$dselx,$dsely]+1)}}" ), "\
2248                   "Length = "{_norm($dselx,$dsely)}" \n"\
2249                   " Angle = "{_atan2($dsely,$dselx)*180/pi}"\260 ",1,0,$fs,1,1
2250              if narg($fontsize) break
2251              elif {baseview,"(w#-1>0.7*w || h#-1>0.45*h) && "$fs>13" && "$ofs>=$fs}
2252                ofs,fs=$fs,{max(13,round($fs/1.25))} rm.
2253              elif {baseview,"w#-1<0.3*w && h#-1<0.45*h && "$fs<64" && "$ofs<=$fs}
2254                ofs,fs=$fs,{min(64,round($fs*1.25))} rm.
2255              else
2256                fontsize=$fs break
2257              fi
2258            while 1
2259            n. 0,255 +dilate. 3 *. -1 n. 0,80 +[-2,-1] r. 100%,100%,..,..
2260            j.. .,0,$is_bottom_text~,0,0,0.85 rm.
2261          fi
2262
2263          if $mx>=0
2264            if $posmx>=0" && "$posmx<w#0" && "$posmy>=0" && "$posmy<h#0
2265              if !narg($xsel0)" || "$is_selection_a_point
2266                repeat {img,d}
2267                  if {img,P=I($posmx,$posmy,$>);(s>=1" || "s<=4)" && "min(isint(P))" && "min(P)>=0" && "max(P)<=255}
2268                    hexstr="= \#"\
2269                          {img,`"digit(x) = (x<10?_'0' + x:_'A' + x - 10);\
2270                                 P = I("$posmx,$posmy,$>");\
2271                                 [ digit(P[0]>>4),digit(P[0]&15),\
2272                                   s<2?0:digit(P[1]>>4),s<2?0:digit(P[1]&15),\
2273                                   s<3?0:digit(P[2]>>4),s<3?0:digit(P[2]&15),\
2274                                   s<4?0:digit(P[3]>>4),s<4?0:digit(P[3]&15) ]"`}" "
2275                  else hexstr= fi
2276                  ofs,fs={narg($fontsize)?0$fontsize:32}
2277                  do
2278                    0 t. " Point ( "$posmx","$posmy" ) = [ "${_d2d_format\ {img,_I($posmx,$posmy,$>)}}" ] "$hexstr,\
2279                         1,0,$fs,1,1
2280                    if narg($fontsize) break
2281                    elif {baseview,"(w#-1>0.7*w || h#-1>0.15*h) && "$fs>13" && "$ofs>=$fs}
2282                      _ofs,fs=$fs,{max(13,round($fs/1.25))} rm.
2283                    elif {baseview,"w#-1<0.3*w && h#-1<0.15*h && "$fs<64" && "$ofs<=$fs}
2284                      ofs,fs=$fs,{min(64,round($fs*1.25))} rm.
2285                    else
2286                      fontsize=$fs break
2287                    fi
2288                  while 1
2289
2290                  n. 0,255 +dilate. 3 *. -1 n. 0,80 +[-2,-1] r. 100%,100%,1,..
2291                  j.. .,0,$is_bottom_text~,$>,0,0.85 rm.
2292                done
2293              fi
2294              x0,y0,x1,y1={[round([$posmx-$posx,$posmy-$posy]*[{*,w,h}]/[$sizx,$sizy]),\
2295                            round([$posmx-$posx+1,$posmy-$posy+1]*[{*,w,h}]/[$sizx,$sizy])-1]}
2296              if $x1-$x0>=8" && "$y1-$y0>=8 # Draw pixel contour when zoomed-in
2297                repeat d==1?1:d*s if d==1 sh. else sh. {z=$>%d;[z,z,int($>/d)]} fi
2298                  rectangle. $x0,$y0,$x1,$y1,1,0x55555555,0
2299                  rectangle. $x0,$y0,$x1,$y1,1,0xAAAAAAAA,255
2300                  rm.
2301                done
2302              fi
2303            fi
2304            if $axes_mode # Draw horizontal/vertical axes
2305              repeat d==1?1:d*s if d==1 sh. else sh. {z=$>%d;[z,z,int($>/d)]} fi
2306                line. $mx,0,$mx,100%,0.5,0xFF00FF00,255
2307                line. $mx,0,$mx,100%,0.5,0x00FF00FF,0
2308                line. 0,$my,100%,$my,0.5,0xFF00FF00,255
2309                line. 0,$my,100%,$my,0.5,0x00FF00FF,0
2310                rm.
2311              done
2312            fi
2313          fi
2314
2315          if narg($xsel0) # Draw rectangular selection
2316            x0,y0,x1,y1={xm=min($xsel0,$xsel1);xM=max($xsel0,$xsel1);\
2317                         ym=min($ysel0,$ysel1);yM=max($ysel0,$ysel1);\
2318                         [[xm-$posx,ym-$posy]*[{*,w,h}]/[$sizx,$sizy],\
2319                          [xM+1-$posx,yM+1-$posy]*[{*,w,h}]/[$sizx,$sizy]-1]}
2320            repeat d==1?1:d*s if d==1 sh. else sh. {z=$>%d;[z,z,int($>/d)]} fi
2321              rectangle. $x0,$y0,$x1,$y1,0.2,0
2322              rectangle. $x0,$y0,$x1,$y1,0.9,0x55555555,0
2323              rectangle. $x0,$y0,$x1,$y1,0.9,0xAAAAAAAA,255
2324              rm.
2325            done
2326            if $xsel0>$xsel1 x0,x1=$x1,$x0 fi
2327            if $ysel0>$ysel1 y0,y1=$y1,$y0 fi
2328            if $xsel0!=$xsel1" && "$ysel0!=$ysel1
2329              x0,y0,x1,y1={[(0.5+[$xsel0-$posx,$ysel0-$posy])*[{*,w,h}]/[$sizx,$sizy],\
2330                            (0.5+[$xsel1-$posx,$ysel1-$posy])*[{*,w,h}]/[$sizx,$sizy]]}
2331              repeat d==1?1:d*s if d==1 sh. else sh. {z=$>%d;[z,z,int($>/d)]} fi
2332                line. $x0,$y0,$x1,$y1,0.9,0x33333333,0
2333                line. $x0,$y0,$x1,$y1,0.9,0xCCCCCCCC,255
2334                rm.
2335              done
2336            fi
2337          fi
2338        fi
2339
2340        if $notification_gfx
2341          +r[notification_gfx] 100%,100%,. j.. .,{[w#-2-w-4,4]},0,0,{sqrt(max(0,1-($|-$notification)))} rm.
2342        fi
2343
2344        nm. view
2345        if $is_multiview repeat d +slices[view] $> w$>. rm. done else w. fi
2346      fi
2347
2348      # Manage user events.
2349      if $wait_event wait else wait 40 fi
2350      idisp=${-idisp} # Index of 'active' display window
2351      wait_event=1
2352
2353      old_mx,old_my=$mx,$my
2354      nposx,nposy,nsizx,nsizy=$posx,$posy,$sizx,$sizy
2355      is_CTRL,mb,mx,my,mo={${"iskey CTRLLEFT"}" || "${"iskey CTRLRIGHT"}},{*$idisp,b,x,y,-o}
2356
2357      # Test end of pan shift.
2358      if !($mb&4)" && "!($is_CTRL" && "($mb&1)) pan_mx,pan_my,pan_posx,pan_posy= fi
2359
2360      # Test if text must be displayed at the bottom.
2361      if {view,$my<0" || "$my>=h-16} is_bottom_text=0
2362      elif $my<16 is_bottom_text=1
2363      fi
2364
2365      # Events related to window resizing.
2366      if ${-isresized} # One of the display windows has been resized
2367        repeat {img,d},w if {*$w,r}
2368          w$w[] {*$w,d,e}
2369          if $is_multiview repeat {img,d} w$>[] {*$w,w,h} done fi
2370          if $nsizx>w#0" && "$nsizy>h#0 nposx,nposy,nsizx,nsizy={[0,0,w#0,h#0]} fi
2371          break
2372        fi done
2373        fontsize,fontsize_notif=
2374        rmn baseview
2375      elif $is_CTRL" && "${"iskey D"}" && "{*$idisp,d}<{*$idisp,u}" && "{*$idisp,e}<{*$idisp,v} # Increase window size
2376        w$idisp[] {round([{*$idisp,w,h}]*1.25)}
2377        if $is_multiview repeat {img,d} w$>[] {*$idisp,w,h} done fi
2378        notification="Increase Window Size"
2379        fontsize,fontsize_notif=
2380        flushkey D rmn baseview
2381      elif $is_CTRL" && "${"iskey C"}" && "{*$idisp,d}>64" && "{*$idisp,e}>64 # Decrease window size
2382        w$idisp[] {round([{*$idisp,w,h}]/1.25)}
2383        if $is_multiview repeat {img,d} w$>[] {*$idisp,w,h} done fi
2384        notification="Decrease Window Size"
2385        fontsize,fontsize_notif=
2386        flushkey C rmn baseview
2387      elif $is_CTRL" && "${"iskey R"} # Reset window size (and view)
2388        nposx,nposy,nsizx,nsizy={0,[0,0,w,h]}
2389        repeat {img,d} w$>[] $wsiz0 done
2390        notification="Reset Window Size"
2391        fontsize,fontsize_notif=
2392        flushkey R rmn baseview
2393      fi
2394
2395      # Events related to image selection.
2396      if !$is_CTRL" && "$mb&1" && "$mx>0
2397        xsel,ysel={X=[$nposx,$nposy]+[$mx,$my]*[$nsizx,$nsizy]/[{*,w,h}];\
2398                   floor([max(0,min(X[0],w#0-1)),max(0,min(X[1],h#0-1))])}
2399        if !narg($xsel0)" && "!($omb&1) xsel0,ysel0,xsel1,ysel1=$xsel,$ysel,$xsel,$ysel
2400        elif narg($xsel0) xsel1,ysel1=$xsel,$ysel
2401        fi
2402        if $mx<=16 nposx-={$nsizx/64} wait_event=0 xzoom,yzoom=
2403        elif $mx>={*,w}-17 nposx+={$nsizx/64} wait_event=0 xzoom,yzoom=
2404        fi
2405        if $my<=16 nposy-={$nsizy/64} wait_event=0 xzoom,yzoom=
2406        elif $my>{*,h}-17 nposy+={$nsizx/64} wait_event=0 xzoom,yzoom=
2407        fi
2408        wait_event=0
2409        rmn view
2410      elif !($mb&1)
2411        if narg($xsel0)
2412
2413          if "p0 = ["$xsel0,$ysel0]"; p1 = ["$xsel1,$ysel1"];  # One px selection -> reset view or exit.
2414              siz = max("$sizx,$sizy");
2415              p0==p1 || (siz>128 && norm1(p1-p0)<=siz/100)"
2416            if $1" && "$nsizx>=w#0" && "$nsizy>=h#0 break fi
2417            nposx,nposy,nsizx,nsizy={0,[0,0,w,h]}
2418          else # Otherwise, zoom in
2419            nposx,nposy,nsizx,nsizy={[min($xsel0,$xsel1),min($ysel0,$ysel1),abs([$xsel1-$xsel0,$ysel1-$ysel0])+1]}
2420          fi
2421          xzoom,yzoom= mb={$mb&6}
2422          rmn view
2423        fi
2424        xsel0,ysel0,xsel1,ysel1=
2425      fi
2426
2427      # Events related to image displacement and mode changes.
2428      if ${"iskey ARROWLEFT"} nposx-={$nsizx/($is_CTRL?4:16)} xzoom,yzoom= # Go left
2429      elif ${"iskey ARROWRIGHT"} nposx+={$nsizx/($is_CTRL?4:16)} xzoom,yzoom= # Go right
2430      elif ${"iskey ARROWUP"} nposy-={$nsizy/($is_CTRL?4:16)} xzoom,yzoom= # Go up
2431      elif ${"iskey ARROWDOWN"} nposy+={$nsizy/($is_CTRL?4:16)} xzoom,yzoom= # Go down
2432      elif $is_CTRL" && "${"iskey O"} # Save copy
2433        n=0 do filename gmic.gmz,$n n+=1 while isfile(['{/${}}'])
2434        if $is_multiview +slices[img] $idisp o. ${} rm. else o[img] ${} fi
2435        notification="Save Copy:\n"${}
2436        flushkey O
2437      elif $is_CTRL" && "${"iskey S"} # Save screenshot
2438        n=0 do filename gmic.png,$n n+=1 while isfile(['{/${}}'])
2439        if $is_multiview +slices[baseview] $idisp o. ${} rm. else o[baseview] ${} fi
2440        notification="Save Screenshot:\n"${}
2441        flushkey S
2442      elif $is_CTRL" && "${"iskey SPACE"} # Center view
2443        nposx,nposy,nsizx,nsizy={0,[0,0,w,h]}
2444        notification="Center View"
2445        flushkey SPACE
2446      elif $is_CTRL" && "${"iskey N"} # Change normalization mode
2447        normalization_mode={($normalization_mode+1)%3}
2448        notification=${"s0=Disable s1=Enable s2=\"Enable C.by.C\" u ${s"$normalization_mode"}"}" Normalization"
2449        flushkey N rmn baseview
2450      elif $is_CTRL" && "${"iskey A"} # Toggle alpha mode
2451        alpha_mode={!$alpha_mode}
2452        notification=${"s0=Disable s1=Enable u ${s"$alpha_mode"}"}" Alpha"
2453        flushkey A rmn baseview
2454      elif $is_CTRL" && "${"iskey F"} # Toggle fullscreen mode
2455        fullscreen_mode={!$fullscreen_mode}
2456        if $fullscreen_mode
2457          fullscreen_wsize={*,w,h} fullscreen_params=$nposx,$nposy,$nsizx,$nsizy
2458          w[] {*,u,v},0,1
2459        else
2460          nposx,nposy,nsizx,nsizy=$fullscreen_params
2461          w[] $fullscreen_wsize,0,0
2462        fi
2463        repeat {img,d} cursor[$>] {!$axes_mode} done
2464        notification=${"s0=Disable s1=Enable u ${s"$fullscreen_mode"}"}" Fullscreen"
2465        flushkey F rmn baseview
2466      elif $is_CTRL" && "${"iskey X"} # Toggle axes mode
2467        repeat {img,d} cursor[$>] $axes_mode done axes_mode={!$axes_mode}
2468        notification=${"s0=Hide s1=Show u ${s"$axes_mode"}"}" Axes"
2469        flushkey X rmn view
2470      elif $is_CTRL" && "${"iskey Z"}" && "$is_moderate_ratio # Toggle aspect-ratio mode
2471        ratio_mode={!$ratio_mode}
2472        notification=${"s0=Release s1=Hold u ${s"$ratio_mode"}"}" Aspect Ratio"
2473        if !$ratio_mode nposx,nposy,nsizx,nsizy={0,[0,0,w,h]} fi
2474        flushkey Z rmn baseview
2475      elif $mx>=0" && "($mb&4" || "($is_CTRL" && "$mb&1)) # Pan (middle mouse button)
2476        if !narg($pan_mx) pan_mx,pan_my,pan_posx,pan_posy=$mx,$my,$posx,$posy fi
2477        nposx,nposy={shiftx=($mx-$pan_mx)*$nsizx/{*,w};\
2478                     shifty=($my-$pan_my)*$nsizy/{*,h};\
2479                     [$pan_posx-shiftx,$pan_posy-shifty]}
2480        xzoom,yzoom=
2481      elif ${"iskey PADSUB"}" || "($mx>=0" && "$mo<0) # Zoom out
2482        if $nsizx>=w#0" && "$nsizy>=h#0
2483          nposx,nposy,nsizx,nsizy={[$nposx/2,$nposy/2,w#0,h#0]}
2484        else
2485          if !narg($xzoom)
2486            xzoom,yzoom={X=$mx<0?[$nposx,$nposy]+[$nsizx,$nsizy]/2:\
2487                           [$nposx,$nposy]+[$mx,$my]*[$nsizx,$nsizy]/[{*,w,h}];\
2488                   [max(0,min(X[0],w#0-1)),max(0,min(X[1],h#0-1))]}
2489          fi
2490          nposx,nposy,nsizx,nsizy={[[$xzoom,$yzoom]+[$nposx-$xzoom,$nposy-$yzoom]/0.75,\
2491                                   min(w#0,round($nsizx/0.75)),\
2492                                   min(h#0,round($nsizy/0.75))]}
2493          if $nsizx>w#0" && "$nsizy>h#0 nsizx,nsizy={[w#0,h#0]} fi
2494        fi
2495      elif ($nsizx>2" || "$nsizy>2)" && "(${"iskey PADADD"}" || "($mx>=0" && "$mo>0)) # Zoom in
2496        xzoom,yzoom={X=$mx<0?[$nposx,$nposy]+[$nsizx,$nsizy]/2:\
2497                             [$nposx,$nposy]+[$mx,$my]*[$nsizx,$nsizy]/[{*,w,h}];\
2498               [max(0,min(X[0],w#0-1)),max(0,min(X[1],h#0-1))]}
2499        nposx,nposy,nsizx,nsizy={[[$xzoom,$yzoom]+0.75*[$nposx-$xzoom,$nposy-$yzoom],round(0.75*[$nsizx,$nsizy])]}
2500      fi
2501
2502      # Constrain image displacement.
2503      if $nposx>=w#0-0.5*$nsizx nposx={w#0-1-0.5*$nsizx}
2504      elif $nposx<=-0.5*$nsizx nposx={1-0.5*$nsizx}
2505      fi
2506      if $nposy>=h#0-0.5*$nsizy nposy={h#0-1-0.5*$nsizy}
2507      elif $nposy<=-0.5*$nsizy nposy={1-0.5*$nsizy}
2508      fi
2509
2510      if [$nposx,$nposy,$nsizx,$nsizy]!=[$posx,$posy,$sizx,$sizy]
2511        posx,posy,sizx,sizy=$nposx,$nposy,$nsizx,$nsizy
2512        rmn baseview
2513      fi
2514      if [$mx,$my]!=[$old_mx,$old_my] rmn view fi
2515      omb=$mb
2516
2517    done
2518    k[0]
2519    if narg($_d2d_core) w[] -1,-1,$wnormalization0
2520    else
2521      if $is_multiview repeat d w$>[] 0 done else w[] 0 fi
2522    fi
2523  nm $nm um iskey,flushkey,isvisible,isresized,idisp endl done
2524  um _d2d_format v -1 d[]
2525
2526#@cli d3d : eq. to 'display3d'.
2527d3d : skip "${1=},${2=0}"
2528  l[] is_image_arg=${"is_image_arg $1"} is_arg={$is_image_arg" || isnum($1)"} onfail is_arg=0 endl
2529  if $is_arg arg=$is_image_arg,$2 if $is_image_arg pass$1 1 _d3d_wh={[w,h]} store. _d3d_background fi
2530  else arg=0,0 noarg fi
2531  v + _display3d $arg
2532
2533#@cli display3d : _[background_image],_exit_on_anykey={ 0 | 1 } : _exit_on_anykey={ 0 | 1 }
2534#@cli : Display selected 3D objects in an interactive viewer (use the instant display window [0] if opened).
2535#@cli : (eq. to 'd3d').
2536#@cli : Default values: '[background_image]=(default)' and 'exit_on_anykey=0'.
2537display3d : skip "${1=},${2=0}"
2538  l[] is_image_arg=${"is_image_arg $1"} is_arg={$is_image_arg" || isnum($1)"} onfail is_arg=0 endl
2539  if $is_arg arg=$is_image_arg,$2 if $is_image_arg pass$1 1 _d3d_wh={[w,h]} store. _d3d_background fi
2540  else arg=0,0 noarg fi
2541  v + _$0 $arg
2542
2543# $1 = is_user_background?
2544# $2 = exit_on_any_key?
2545_display3d :
2546  is_user_background,exit_on_anykey=$1,$2
2547  if !$! e[0--3] "Display 3D object []." return fi
2548
2549  repeat $! l[$>]
2550    l. check3d 0
2551    onfail l[] ({'${}'}) s +,{'"'check3d': "'} k. msg={t} rm endl error[] "Command 'display3d': "$msg
2552    endl
2553    nm={n} nbv,nbp={f2ui([i[6],i[7]])}
2554    e[0--5] "Display 3D object ["{arg(1+$>,$[])}"] = '"$nm"' ("$nbv" vertices, "$nbp" primitives)."
2555
2556    # Init display window and variables.
2557    disp_title=$nm" ("$nbv" vertices, "$nbp" primitives)"
2558    if !{*}
2559      if narg($_d3d_wh) w[] ${fitscreen\ $_d3d_wh,1},0,$disp_title
2560      else w[] {0.7*[{*,u,v}]},0,$disp_title
2561      fi
2562    else disp_normalization={*,n} w[] -1,-1,0,$disp_title fi
2563    disp_size0={*,w,h}
2564
2565    (1,0,0,0;0,1,0,0;0,0,1,0) store. pose3d
2566    posx,posy,zoomfactor=50,50,1
2567    is_fullscreen,is_zbuffer,is_axes3d,is_outline,is_boundingbox,is_animate,is_outvideo={*,f},1,1,0,0,0,0
2568    mode_render=4
2569    mode_drender={$nbp<2048?$mode_render:-1}
2570    mode_background={$is_user_background?7:3}
2571    mode_orientation=2
2572    mode_animate=1
2573    speed_animate=1
2574    focale=800
2575    mx0,my0,mx1,my1=
2576    notification= fontsize_notif=
2577    wait_event=1
2578
2579    # Create 3D axes.
2580    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
2581
2582    # Start interactive loop.
2583    do
2584      is_motion={narg($mx0)}
2585      if $is_animate" || "$is_outvideo rmn render fi
2586
2587      # Init background image.
2588      if !narg($background)
2589        if $mode_background<3 {*,w,h},1,3,{arg(1+$mode_background,0,255,128)}
2590        elif $mode_background==3 3,2,1,1,"32,32,64,64,116,96" permute. cyzx r. {*,w,h},1,3,3 round.
2591        elif $mode_background==4 3,2,1,1,"0,0,0,0,64,96" permute. cyzx r. {*,w,h},1,3,3 round.
2592        elif $mode_background==5 3,2,1,1,"8,0,0,160,90,0" permute. cyzx r. {*,w,h},1,3,3 round.
2593        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
2594        else $_d3d_background r. {*,w,h},1,3,1
2595        fi
2596        w. nm. background
2597        rmn object3d
2598      fi
2599
2600      # Init normalized 3D object.
2601      if !narg($object3d)
2602        +c3d[0] n3d. *3d. {background,$zoomfactor*0.65*min(w,h)}
2603        if $mode_orientation==1 rv3d. fi
2604        rmn boundingbox3d
2605        if $is_boundingbox +boundingbox3d. o3d. 0.35 nm. boundingbox3d +3d.. . rv[-2,-1] fi
2606        nm. object3d
2607        rmn render
2608      fi
2609
2610      # Render 3D object.
2611      if !narg($render)
2612        $pose3d
2613        if $is_animate" || "$is_outvideo l.
2614          da={$speed_animate*($is_animate?20*($|-$time_animate):1)}
2615          if $mode_animate==0 rotation3d 1,0,0,$da rv
2616          elif $mode_animate==1 rotation3d 0,1,0,$da rv
2617          elif $mode_animate==2 rotation3d 0,0,1,$da rv
2618          else
2619            dax,day,daz={[0.75,0.82,0.97]*$da}
2620            rotation3d 0,1,0,$dax rotation3d 1,0,0,$day rotation3d 0,0,1,$daz rv
2621          fi
2622          m* 1,3,1,1 j.. .,3 rm. +store. pose3d
2623          time_animate=$|
2624        endl fi
2625        p={^} rm.
2626        m={$is_motion?$mode_drender:$mode_render}
2627        [background] nm. render
2628        if $m<0
2629          if !narg($boundingbox3d) +boundingbox3d[object3d] o3d. 0.35 nm. boundingbox3d fi
2630          +pose3d[boundingbox3d] $p
2631          j3d[render] .,$posx%,$posy%,0,1,1,0,0,$focale rm.
2632        else
2633          +pose3d[object3d] $p
2634          if $is_outline
2635            {render,[w,h]},1,3,-1
2636            j3d. ..,$posx%,$posy%,0,1,$m,{$mode_orientation==2},$is_zbuffer,$focale
2637            +channels. 0 !=. -1
2638            +dilate. 5 r. 100%,100%,1,3 j[render] .,0,0,0,0,0.8,. rm.
2639            j[render] ..,0,0,0,0,1,. rm[-2,-1]
2640          else j3d[render] .,$posx%,$posy%,0,1,$m,{$mode_orientation==2},$is_zbuffer,$focale
2641          fi
2642          rm.
2643        fi
2644        if $is_axes3d
2645          +pose3d[axes3d] $p
2646          eval " # Colorize axes depending on their Z-sign
2647            const off = "$off_axes";
2648            ref([ 255,0,0 ],col);
2649            i[13]>0?copy(i[off],col,3);
2650            i[19]>0?copy(i[off + 3],col,3);
2651            i[25]>0?copy(i[off + 6],col,3);
2652          "
2653          j3d[render] .,50,{-2,h-50},0,0.75,1,0,0,$focale rm.
2654        fi
2655        rmn view
2656
2657        if $is_outvideo
2658          o[render] $filename_outvideo,20,0,1
2659          nb_frames={int(360/$speed_animate)}
2660          if $is_outvideo>=$nb_frames
2661            o[] $filename_outvideo,20,0,0 # Close video stream
2662            is_outvideo=0
2663            notification="Output Video:\nDone!"
2664          else
2665            is_outvideo+=1
2666            notification="Output Video:\nFrame "{$is_outvideo+1}/$nb_frames
2667          fi
2668        fi
2669      fi
2670
2671      # Manage notifications.
2672      if narg($notification)
2673        wait_event=0
2674        if !isnum($notification) # Create notification gfx
2675          rmn notification_gfx
2676          ofs,fs={narg($fontsize_notif)?0$fontsize_notif:32}
2677          do
2678            0 t. {``$notification},0,0,$fs,1,255
2679            if narg($fontsize_notif) break
2680            elif {background,"(w#-1>0.7*w || h#-1>0.25*h) && "$fs>13" && "$ofs>=$fs}
2681              ofs,fs=$fs,{max(13,round($fs/1.25))} rm.
2682            elif {background,"w#-1<0.3*w && h#-1<0.25*h && "$fs<64" && "$ofs<=$fs}
2683              ofs,fs=$fs,{min(64,round($fs*1.25))} rm.
2684            else
2685              fontsize_notif=$fs break
2686            fi
2687          while 1
2688          r. {[w+12,h+8]},1,1,0,0,0.5,0.5 rectangle. 0,0,100%,100%,1,0xFFFFFFFF,255 to_rgb.
2689          nm. notification_gfx
2690          notification=$|
2691        else
2692          if $|>$notification+1 rm[notification_gfx] wait_event=1 notification= fi
2693          rmn view
2694        fi
2695      fi
2696
2697      # Refresh window view.
2698      if !narg($view)
2699        if $notification_gfx
2700          +j[render] [notification_gfx],0.99~,5,0,0,{sqrt(max(0,1-($|-$notification)))}
2701        else [render]
2702        fi
2703        nm. view w. -1,-1,0,$is_fullscreen,$disp_title
2704      fi
2705
2706      if $is_motion" || "$is_animate wait_event=0 fi
2707      if $wait_event wait elif !$is_outvideo wait 20 fi
2708
2709      # Manage user-events
2710      if $exit_on_anykey" && "{*,k} break fi
2711      if $is_outvideo continue fi # Skip user event management
2712      mx,my,mb={*,x,y,b}
2713      is_CTRL={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
2714      if {*,-F1} # Render: Dots
2715        mode_render,mode_drender={M=0;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2716        notification="Render: Dots" if $mode_drender<0 notification.=" + Box" fi
2717        rmn render
2718      elif {*,-F2} # Render: Wireframe
2719        mode_render,mode_drender={M=1;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2720        notification="Render: Wireframe" if $mode_drender<0 notification.=" + Box" fi
2721        rmn render
2722      elif {*,-F3} # Render: Flat
2723        mode_render,mode_drender={M=2;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2724        notification="Render: Flat" if $mode_drender<0 notification.=" + Box" fi
2725        rmn render
2726      elif {*,-F4} # Render: Flat-shaded
2727        mode_render,mode_drender={M=3;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2728        notification="Render: Flat-Shaded" if $mode_drender<0 notification.=" + Box" fi
2729        rmn render
2730      elif {*,-F5} # Render: Gouraud-shaded
2731        mode_render,mode_drender={M=4;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2732        notification="Render: Gouraud-Shaded" if $mode_drender<0 notification.=" + Box" fi
2733        rmn render
2734      elif {*,-F6} # Render: Phong-shaded
2735        mode_render,mode_drender={M=5;m=$mode_render;wasbbox=$mode_drender<0;[M,m!=M?(wasbbox?-1:M):(wasbbox?M:-1)]}
2736        notification="Render: Phong-Shaded" if $mode_drender<0 notification.=" + Box" fi
2737        rmn render
2738      elif {*,-F7}" && "($focale>100" || "!$focale) # Decrease focale
2739        if !$focale focale=2000 else focale-=100 fi
2740        notification="Focale: "$focale
2741        rmn render
2742      elif {*,-F8}" && "$focale # Increase focale
2743        if $focale>=2000 focale=0 notification="Focale: Inf" else focale+=100 notification="Focale: "$focale fi
2744        rmn render
2745      elif {*,-F9} # Choose animation mode
2746        mode_animate={($mode_animate+1)%4}
2747        n0,n1,n2,n3="X-Axis","Y-Axis","Z-Axis","XYZ-Axes" notification="Animation Mode: "${n$mode_animate}
2748        rmn render
2749      elif {*,-F10} # Choose animation speed
2750        speed_animate={max(1,($speed_animate+1)%9)}
2751        notification="Animation Speed: X"$speed_animate
2752      elif {*,-SPACE} # Start/stop animation
2753        is_animate,time_animate={!$is_animate},$|
2754        n0,n1="Off","On" notification="Animation: "${n$is_animate}
2755        rmn render
2756      fi
2757      if $is_CTRL
2758        if {*,-A} # Show/hide 3D axes
2759          is_axes3d={!$is_axes3d}
2760          n0,n1="Off","On" notification="3D Axes: "${n$is_axes3d}
2761          rmn render
2762        elif {*,-B} # Change background
2763          mode_background={($mode_background+1)%($is_user_background?8:7)}
2764          n0,n1,n2,n3,n4,n5,n6,n7=\
2765            "Black","White","Gray","Gradient \#1","Gradient \#2","Gradient \#3","Checkerboard","User-Defined"
2766          notification="Background: "${n$mode_background}
2767          rmn background
2768        elif {*,-C}" && "{*,w}>128" && "{*,h}>128 w[] {0.8*[{*,w,h}]} # Decrease window size
2769          notification="Decrease Window Size" fontsize_notif=
2770          rmn background
2771        elif {*,-D}" && "{*,w}<0.8*{*,u}" && "{*,h}<0.8*{*,v} w[] {1.25*[{*,w,h}]} # Increase window size
2772          notification="Increase Window Size" fontsize_notif=
2773          rmn background
2774        elif {*,-F} # Toggle fullscreen
2775          is_fullscreen={!$is_fullscreen}
2776          if $is_fullscreen w[] {*,u,v} else w[] {0.75*[{*,u,v}]} fi
2777          n0,n1="Off","On" notification="Fullscreen: "${n$is_fullscreen} fontsize_notif=
2778          rmn background
2779        elif {*,-G} # Save object as a .obj file
2780          n=0 do filename gmic.obj,$n n+=1 while isfile(['{/${}}'])
2781          notification="Save Copy:\n"${}
2782          o[0] ${}
2783        elif {*,-L} # Show/hide outline
2784          is_outline={!$is_outline}
2785          n0,n1="Off","On" notification="Outline: "${n$is_outline}
2786          rmn render
2787        elif {*,-O} # Save object as a .gmz file
2788          n=0 do filename gmic.gmz,$n n+=1 while isfile(['{/${}}'])
2789          o[0] ${}
2790          notification="Save Copy:\n"${}
2791        elif {*,-P} # Print 3D pose on console
2792          $pose3d v 0 e[] "  > 3D Pose = [ "{^}" ]." rm.
2793        elif {*,-R} # Reset window
2794          w[] $disp_size0
2795          notification="Reset Window Size" fontsize_notif=
2796          rmn background
2797        elif {*,-S} # Save screenshot
2798          n=0 do filename gmic.png,$n n+=1 while isfile(['{/${}}'])
2799          o[render] ${}
2800          notification="Save Screenshot:\n"${}
2801        elif {*,-T} # Change orientation mode
2802          mode_orientation={($mode_orientation+1)%3}
2803          n0,n1,n2="Forward","Backward","Double-Sided" notification="Orientation: "${n$mode_orientation}
2804          if $mode_orientation rv3d[object3d] fi
2805          rmn render
2806        elif {*,-V} # Start/stop output video
2807          is_outvideo={!$is_outvideo}
2808          is_animate=0
2809          n=0 do filename gmic.mp4,$n n+=1 while isfile(['{/${}}'])
2810          filename_outvideo=${}
2811        elif {*,-X} # Show/hide bounding box
2812          is_boundingbox={!$is_boundingbox}
2813          n0,n1="Off","On" notification="Bounding Box: "${n$is_boundingbox}
2814          rmn object3d
2815        elif {*,-Z} # Enable/disable Z-buffer
2816          is_zbuffer={!$is_zbuffer}
2817          n0,n1="Off","On" notification="Z-Buffer: "${n$is_zbuffer}
2818          rmn render
2819        fi
2820      fi
2821      if {*,-r} rmn background fontsize_notif= fi
2822
2823      if $mx>=0 # Manage mouse-drag
2824        if $mb
2825          if !narg($mx0) mx0,my0,mx1,my1=$mx,$my,$mx,$my else mx1,my1=$mx,$my fi
2826        else
2827          if narg($mx0) rmn render fi
2828          mx0,my0,mx1,my1=
2829        fi
2830      fi
2831
2832      # Estimate new 3D pose from motion.
2833      if narg($mx1)" && "($mx0!=$mx1" || "$my0!=$my1)
2834        rmn render
2835        if $mb&1" && "!$is_CTRL # Rotation
2836          rotation3d[] {"
2837            const w2 = "{*,w}"/2;
2838            const h2 = "{*,h}"/2;
2839            const R = 0.375*min("{*,w,h}");
2840            const u0 = "$mx0" - w2;
2841            const v0 = "$my0" - h2;
2842            const u1 = "$mx1" - w2;
2843            const v1 = "$my1" - h2;
2844            n0 = norm(u0,v0);
2845            nu0 = n0>R?u0*R/n0:u0;
2846            nv0 = n0>R?v0*R/n0:v0;
2847            nw0 = sqrt(max(0,R^2 - nu0^2 - nv0^2));
2848            n1 = norm(u1,v1);
2849            nu1 = n1>R?u1*R/n1:u1;
2850            nv1 = n1>R?v1*R/n1:v1;
2851            nw1 = sqrt(max(0,R^2 - nu1^2 - nv1^2));
2852            u = nv0*nw1 - nw0*nv1;
2853            v = nw0*nu1 - nu0*nw1;
2854            w = nv0*nu1 - nu0*nv1;
2855            n = norm(u,v,w);
2856            [ u,v,w,-asin(n/R^2)*180/pi ]"}
2857          $pose3d m*[-2,-1] store. pose3d
2858          mx0,my0=$mx1,$my1
2859        elif $mb&4" || "($mb&1" && "$is_CTRL) # Pan
2860          posx,posy={"
2861            const px = "$mx1-$mx0+$posx*{*,w}%";
2862            const py = "$my1-$my0+$posy*{*,h}%";
2863            cut([ px*100/"{*,w}", py*100/"{*,h}" ],0,100)"}
2864          mx0,my0=$mx1,$my1
2865        elif $mb&2 # Zoom with mouse button
2866          fact={1+($my0-$my1)/100}
2867          zoomfactor*=$fact
2868          *3d[object3d] $fact if narg($boundingbox3d) *3d[boundingbox3d] $fact fi
2869          mx0,my0=$mx1,$my1
2870        fi
2871      fi
2872      if {*,o} # Zoom with mousewheel
2873        fact={1+{*,-o}/10}
2874        zoomfactor*=$fact
2875        *3d[object3d] $fact if narg($boundingbox3d) *3d[boundingbox3d] $fact fi
2876        rmn render
2877      fi
2878
2879    while {*}" && "!{*,ESC}" && "!($is_CTRL" && "{*,W})
2880    k[0]
2881  endl done
2882  if narg($disp_normalization) w[] -1,-1,$disp_normalization else w[] 0 fi
2883  v -1 d[]
2884
2885#@cli da : eq. to 'display_array'.
2886da :
2887  _gmic_s="$?" v + _display_array $*
2888
2889#@cli display_array : _width>0,_height>0
2890#@cli : Display images in interactive windows where pixel neighborhoods can be explored.
2891#@cli : Default values: 'width=13' and 'height=width'.
2892display_array :
2893  _gmic_s="$?" v + _$0 $*
2894
2895_display_array : check ${1=13}>0" && "${2=$1}>0
2896  e[0--3] "Display $1x$2 array of pixel values for image"$_gmic_s"."
2897
2898  dxb={round($1/2,1,1)} dxf={$1-1-$dxb}
2899  dyb={round($2/2,1,1)} dyf={$2-1-$dyb}
2900
2901  repeat $! l[$>]
2902    if w<128" && "h<128 r 128,128,100%,100%,0,0,0.5,0.5 fi # Manage cases of small and large images.
2903    x0=0 y0=0 w={w} h={h}
2904    wmax={0.9*{*,u}} hmax={0.9*{*,v}}
2905    do
2906      if w>=$wmax" || "h>=$hmax
2907        n={n} nm. "Image "'{b}.{x}'" is too large, please select a sub-image."
2908        +select. 2 x0={i[0]} y0={i[1]} w={1+i[3]-i[0]} h={1+i[4]-i[1]}
2909        rm. nm. $n
2910      fi
2911      +z. $x0,$y0,0,{$x0+$w-1},{$y0+$h-1},0 round. 1 n. 0,255
2912    while w>=$wmax" || "h>=$hmax
2913
2914    x1=-1 y1=-1 c1=0 ox1=-1 oy1=-1 oc1=-1
2915    x2=-1 y2=-1 c2=0 ox2=-1 oy2=-1 oc2=-1
2916    x3=-1 y3=-1 c3=0 ox3=-1 oy3=-1 oc3=-1
2917    c0=0 oxm=-1 oym=-1
2918    w. -1,-1,0,0,{-2,b}.{-2,x}
2919    do  # Enter event loop.
2920
2921      # Manage user interactions.
2922      wait[0-3]
2923      oc0=$c0
2924      repeat 4
2925        if $>" && "!{*$>}" && "${x$>}>=0 w$> 0 x$>=-1 y$>=-1 c$>=0 fi
2926        if {*$>,o} c$>={(${c$>}+sign({*$>,o}))%s} wait[$>] -1 fi
2927        if {*$>,SPACE}" || "{*$>,ENTER}" || "{*$>,ARROWRIGHT}" || "{*$>,ARROWDOWN} c$>={(${c$>}+1)%s} wait[$>] -1 fi
2928        if {*$>,BACKSPACE}" || "{*$>,ARROWLEFT}" || "{*$>,ARROWUP} c$>={(${c$>}-1)%s} wait[$>] -1 fi
2929      done
2930      if $oc0!=$c0 c1=$c0 c2=$c0 c3=$c0 fi
2931      xm={*,x} ym={*,y}
2932      if $xm>=0" && "{*,b}&1 x1=$xm y1=$ym fi
2933      if $xm>=0" && "{*,b}&2 x2=$xm y2=$ym fi
2934      if $xm>=0" && "{*,b}&4 x3=$xm y3=$ym fi
2935
2936      # Generate main image view.
2937      if $xm>=0" && "($oxm!=$xm" || "$oym!=$ym) w[] -1,-1,{-2,b}.{-2,x}" - ("$xm,$ym")" fi
2938      if $x1!=$ox1" || "$y1!=$oy1" || "$x2!=$ox2" || "$y2!=$oy2" || "$x3!=$ox3" || "$y3!=$oy3
2939        .
2940        if $x1>=0
2941          xb={$x1-$dxb} yb={$y1-$dyb} xe={$x1+$dxf} ye={$y1+$dyf}
2942          rectangle. $xb,$yb,$xe,$ye,0.2,0,255,255
2943          rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,0,255,255
2944        fi
2945        if $x2>=0
2946          xb={$x2-$dxb} yb={$y2-$dyb} xe={$x2+$dxf} ye={$y2+$dyf}
2947          rectangle. $xb,$yb,$xe,$ye,0.2,255,32,255
2948          rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,255,32,255
2949        fi
2950        if $x3>=0
2951          xb={$x3-$dxb} yb={$y3-$dyb} xe={$x3+$dxf} ye={$y3+$dyf}
2952          rectangle. $xb,$yb,$xe,$ye,0.2,255,255,0
2953          rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,255,255,0
2954        fi
2955        w. {-2,w},{-2,h} rm. oxm=$xm oym=$ym
2956      fi
2957
2958      # Generate zoomed views.
2959      if $x1>=0" && "($ox1!=$x1" || "$oy1!=$y1" || "$oc1!=$c1)
2960        +z.. {$x1-$dxb},{$y1-$dyb},0,$c1,{$x1+$dxf},{$y1+$dyf},0,$c1
2961        +z.. {$x1-$dxb},{$y1-$dyb},0,{$x1+$dxf},{$y1+$dyf},0
2962        __display_array[-2,-1] $1,$2,0,255,255
2963        w1. {w},{h},0,0,{-3,b}" - ("$x1,$y1,c=$c1")"
2964        rm. ox1=$x1 oy1=$y1 oc1=$c1
2965      fi
2966      if $x2>=0" && "($ox2!=$x2" || "$oy2!=$y2" || "$oc2!=$c2)
2967        +z.. {$x2-$dxb},{$y2-$dyb},0,$c2,{$x2+$dxf},{$y2+$dyf},0,$c2
2968        +z.. {$x2-$dxb},{$y2-$dyb},0,{$x2+$dxf},{$y2+$dyf},0
2969        __display_array[-2,-1] $1,$2,255,32,255
2970        w2. {w},{h},0,0,{-3,b}" - ("$x2,$y2,c=$c2")"
2971        rm. ox2=$x2 oy2=$y2 oc2=$c2
2972      fi
2973      if $x3>=0" && "($ox3!=$x3" || "$oy3!=$y3" || "$oc3!=$c3)
2974        +z.. {$x3-$dxb},{$y3-$dyb},0,$c3,{$x3+$dxf},{$y3+$dyf},0,$c3
2975        +z.. {$x3-$dxb},{$y3-$dyb},0,{$x3+$dxf},{$y3+$dyf},0
2976        __display_array[-2,-1] $1,$2,255,255,0
2977        w3. {w},{h},0,0,{-3,b}" - ("$x3,$y3,c=$c3")"
2978        rm. ox3=$x3 oy3=$y3 oc3=$c3
2979      fi
2980    while {*}" && "\
2981          !{*,ESC}" && "!{*,Q}" && "\
2982          !{*1,ESC}" && "!{*1,Q}" && "\
2983          !{*2,ESC}" && "!{*2,Q}" && "\
2984          !{*3,ESC}" && "!{*3,Q}
2985    k[0] w 0 w1 0 w2 0 w3 0
2986  endl done
2987
2988__display_array :
2989  round.. 1 c.. 0,999 r. 100%,100%,1,3,{s==1}
2990  +luminance. r.. {$1*24},{$2*24} grid.. {100/$1}%,{100/$2}%,0,0,1,0
2991  xb={24*int($1/2)} yb={24*int($2/2)} xe={$xb+24} ye={$yb+24}
2992  rectangle.. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,$3,$4,$5
2993  repeat $2,yg
2994    repeat $1,xg
2995      t.. {-3,i($xg,$yg)},{5+$xg*24},{5+$yg*24},13,0.8,{i($xg,$yg)>128?0:255}
2996    done
2997  done
2998  rm[-3,-1]
2999
3000#@cli dc : eq. to 'display_camera'.
3001dc : check_opencv $0
3002  v + _display_camera
3003
3004#@cli display_camera
3005#@cli : Open camera viewer.
3006#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
3007display_camera : check_opencv $0
3008  v + _$0
3009
3010_display_camera :
3011  e[0--3] "Open camera stream viewer."
3012
3013  # Initialize camera and get resolution.
3014  l[] camera
3015  onfail
3016    use_vt100
3017    e[0--4] ${_vt100_r}${_vt100_b}"Command 'display_camera': Unable to read camera stream. Exiting."$_vt100_n
3018    return
3019  endl
3020
3021  wc,hc={[w,h]}
3022
3023  # Open interactive window.
3024  w ${"fitscreen "$wc,$hc},0,"G'MIC Camera Stream Viewer"
3025  wnfs,hnfs={*,w,h}
3026  angle,fullscreen,brightness,contrast=0
3027  do
3028
3029    # Display frame from camera.
3030    ww,wh={*,w,h}
3031    camera
3032    if $angle rotate. {90*$angle} fi
3033    if $brightness +. {10*$brightness} c. 0,255 fi
3034    if $contrast /. 255 *. {1.2^$contrast} *. 255 c. 0,255 fi
3035    rr2d. $ww,$wh,2,1 w.
3036    rm
3037    wait 30
3038
3039    # Manage user events.
3040    if {*,r} w[] {*,d,e}
3041    elif {*,-R}" || "{*,-SPACE} angle={($angle+1)%4}
3042    elif {*,-ARROWUP} brightness={min(5,$brightness+1)}
3043    elif {*,-ARROWDOWN} brightness={max(-5,$brightness-1)}
3044    elif {*,-ARROWRIGHT} contrast={min(5,$contrast+1)}
3045    elif {*,-ARROWLEFT} contrast={max(-5,$contrast-1)}
3046    elif {*,-F}" || "{*,-ENTER}" || "{*,-F5}
3047       fullscreen={1-$fullscreen}
3048       if $fullscreen
3049         wwnfs,whnfs={*,w,h}
3050         w[] {*,u,v},0,1
3051       else
3052         w[] $wwnfs,$whnfs,0,0
3053       fi
3054    fi
3055
3056  while {*}" && "!{*,ESC}
3057  camera 0,0 w[] 0
3058
3059#@cli dfft : eq. to 'display_fft'.
3060dfft :
3061  v + _display_fft
3062
3063#@cli display_fft
3064#@cli : Display fourier transform of selected images, with centered log-module and argument.
3065#@cli : (eq. to 'dfft').
3066#@cli : $ image.jpg +display_fft
3067display_fft :
3068  v + _$0
3069
3070_display_fft :
3071  e[0--3] "Render fourier transform of image$? with centered log-module and argument."
3072  repeat $! l[$>] fftpolar +.. 1 log.. n 0,255 a x endl done s x,2
3073
3074#@cli dg : eq. to 'display_graph'.
3075dg : 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}"
3076  _display_graph ${1-8},"$9","$10"
3077
3078#@cli display_graph : _width>=0,_height>=0,_plot_type,_vertex_type,_xmin,_xmax,_ymin,_ymax,_xlabel,_ylabel
3079#@cli : Render graph plot from selected image data.
3080#@cli : 'plot_type' can be { 0=none | 1=lines | 2=splines | 3=bar }.
3081#@cli : 'vertex_type' can be { 0=none | 1=points | 2,3=crosses | 4,5=circles | 6,7=squares }.
3082#@cli : 'xmin','xmax','ymin','ymax' set the coordinates of the displayed xy-axes.
3083#@cli : if specified 'width' or 'height' is '0', then image size is set to half the screen size.
3084#@cli : Default values: 'width=0', 'height=0', 'plot_type=1', 'vertex_type=1', 'xmin=xmax=ymin=ymax=0 (auto)', \
3085# 'xlabel="x-axis"' and 'ylabel="y-axis"'.
3086#@cli : $ 128,1,1,1,'cos(x/10+u)' +display_graph 400,300,3
3087display_graph : check "${1=0}>=0 && ${2=0}>=0"
3088  skip ${3=1},${4=0},${5=0},${6=0},${7=0},${8=0},"${9=x-axis}","${10=y-axis}"
3089  _display_graph ${1-8},"$9","$10"
3090
3091_display_graph : check "${1=0}>=0 && ${2=0}>=0"
3092  skip ${3=1},${4=0},${5=0},${6=0},${7=0},${8=0},"${9=x-axis}","${10=y-axis}"
3093  e[0--3] "Render $1x$2 graph plot from data of image$?."
3094
3095  repeat $! l[$>] nm={0,n}
3096
3097    # Determine output size.
3098    if $1>0" && "$2>0 w,h=$1,$2 else w,h={{*,u}/2},{{*,v}/2} fi
3099    w,h={[max($w,33),max($h,33)]}
3100
3101    # Determine xmin,xmax/ymin,ymax.
3102    one={$3!=3} siz={w*h*d}
3103    if $5==$6 xmin=0 xmax={$siz-$one} else xmin={min($5,$6)} xmax={max($5,$6)} fi
3104    if $7==$8 ymin={im-(iM-im)/20} ymax={iM+(iM-im)/20} else ymin={min($7,$8)} ymax={max($7,$8)} fi
3105    dx={$xmax-$xmin} dy={$ymax-$ymin}
3106
3107    # Determine number of axes tick marks.
3108    u=${"_axes[] "$xmin,$xmax",{0.3*"$w"/14}"} offx={arg(1,$u)} deltax={arg(2,$u)}
3109    u=${"_axes[] "$ymin,$ymax",{0.3*"$h"/14}"} offy={arg(1,$u)} deltay={arg(2,$u)}
3110
3111    # Create plot canvas.
3112    gw={$w-32} gh={$h-32} gg={($gw-$one)/($siz-$one)}
3113    $gw,$gh,1,3,255
3114
3115    grid. {$deltax*$gw/$dx},{$deltay*$gh/$dy},{($offx-$xmin)*$gw/$dx},{$gh-($offy-$ymin)*$gh/$dy},0.25,0xCCCCCCCC,0
3116
3117    # Define color palette for curves.
3118    if s#-2==1 (120,120,200)
3119    elif s#-2<=3 (220,10,10;10,220,10;10,10,220)
3120    else
3121      (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
3122      sh. 0,2,0,0 f. 255,0,0,0,255,0,0,0,255 rm.
3123    fi
3124
3125    # Draw plot for each channel.
3126    repeat s#-3 sh... $> graph... .,$3,$4,$ymax,$ymin,1,{-2,@0-2} rm. shift. 0,-1 done
3127    rm[-3,-1]
3128    line. 0,0,100%,0,1,110 line. 100%,0,100%,100%,1,110
3129    line. 100%,100%,0,100%,1,255 line. 0,100%,0,0,1,255
3130
3131    100%,100%,1,1,255
3132    axes. $xmin,$xmax,$ymax,$ymin,14,1,0
3133    if $xmin>0 axes. 0,0,$ymax,$ymin,14,1,160 fi
3134    if $xmax<0 axes. {w-1},{w-1},$ymax,$ymin,14,1,160 fi
3135    if $ymin>0 axes. $xmin,$xmax,{h-1},{h-1},14,1,160 fi
3136    if $ymax<0 axes. $xmin,$xmax,0,0,14,1,160 fi
3137    +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1]
3138    frame. 16,16,220
3139    0 t. "$9",0,0,14,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm.
3140    0 t. "$10",0,0,14,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm.
3141
3142  nm $nm endl done c 0,255
3143
3144#@cli dh : eq. to 'display_histogram'.
3145dh :
3146  _gmic_s="$?" v + _display_histogram $"*"
3147
3148#@cli display_histogram : _width>=0,_height>=0,_clusters>0,_min_value[%],_max_value[%],_show_axes={ 0 | 1 },_expression.
3149#@cli : Render a channel-by-channel histogram.
3150#@cli : If selected images have several slices, the rendering is performed for all input slices.
3151#@cli : 'expression' is a mathematical expression used to transform the histogram data for visualization purpose.
3152#@cli : (eq. to 'dh').
3153#@cli : if specified 'width' or 'height' is '0', then image size is set to half the screen size.
3154#@cli : Default values: 'width=0', 'height=0', 'clusters=256', 'min_value=0%', 'max_value=100%', 'show_axes=1' \
3155# and 'expression=i'.
3156#@cli : $ image.jpg +display_histogram 512,300
3157display_histogram :
3158  _gmic_s="$?" v + _$0 $"*"
3159
3160_display_histogram : check "${1=0}>=0 && ${2=0}>=0 && ${3=256}>0" skip ${4=0%},${5=100%},${6=1},"${7=i}"
3161  e[0--3] "Render $1x$2 channel-by-channel histogram of image"$_gmic_s", with $3 clusters, minimum value $4
3162 and maximum value $5."
3163  repeat $! l[$>] nm={0,n}
3164    if ${is_percent\ $4} m={im+(iM-im)*$4} else m=$4 fi
3165    if ${is_percent\ $5} M={im+(iM-im)*$5} else M=$5 fi
3166    s={s} s c
3167    repeat $s l[{-1-$>}] s z histogram $3,$m,$M a z endl done
3168    a c f '"${7--1}"' vM={iM} s z
3169    repeat $! l[$>]
3170      if $1>0" && "$2>0 wh=$1,$2 else wh={{*,u}/2},{{*,v}/2} fi
3171      $wh,1,{s},-255
3172      repeat s sh[-2,-1] $> graph. ..,3,0,$vM,0,1,0 rm[-2,-1] done
3173      rm.. + 255
3174      if $6
3175        100%,100%
3176        axes. $m,$M,$vM,0,14,1,255
3177        if $m>0 axes. 0,0,$vM,0,14,1,200 fi
3178        if $M<0 axes. {w-1},{w-1},$vM,0,14,1,200 fi
3179        +dilate. 3 ri.. ... j... ..,0,0,0,0,1,.,255 rm[-2,-1]
3180      fi
3181    endl done
3182    a z nm $nm
3183  endl done
3184
3185#@cli display_parametric : _width>0,_height>0,_outline_opacity,_vertex_radius>=0,_is_antialiased={ 0 | 1 },\
3186# _is_decorated={ 0 | 1 },_xlabel,_ylabel
3187#@cli : Render 2D or 3D parametric curve or point clouds from selected image data.
3188#@cli : Curve points are defined as pixels of a 2 or 3-channel image.
3189#@cli : If the point image contains more than 3 channels, additional channels define the (R,G,B) color for each vertex.
3190#@cli : If 'outline_opacity>1', the outline is colored according to the specified vertex colors and
3191#@cli : 'outline_opacity-1' is used as the actual drawing opacity.
3192#@cli : Default values: 'width=512', 'height=width', 'outline_opacity=3', 'vertex_radius=0', 'is_antialiased=1',\
3193# 'is_decorated=1', 'xlabel="x-axis"' and 'ylabel="y-axis"'.
3194#@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
3195#@cli : $ 1000,1,1,2,u(-100,100) quantize 4,1 noise 12 channels 0,2 +normalize 0,255 append c \
3196# display_parametric 512,512,0.1,8
3197display_parametric : check "${1=512}>0 && ${2=$1}>0 && ${4=0}>=0" skip ${3=3},${5=1},${6=1},"${7=x-axis}","${8=y-axis}"
3198  s0="no " s1="" o0="" o1="colored "
3199  e[^-1] "Render $1x$2 parametric graph plot from data of image$?, with "${o{$3>1}}"outline opacity "\
3200      {$3>1?$3-1:$3}", vertex radius $4, "${s{$5!=0}}"antialiasing and "${s{$6!=0}}"decoration."
3201  repeat $! l[$>]
3202    nm={0,n} N={w*h*d}
3203    i[0] ('CImg3d') +[0] 0.5 i[1] ($N;$N)  # Header + nb of vertices/primitives.
3204
3205    # Calibrate colors of vertices.
3206    if s==4 +channels. 3,3 r. 100%,100%,1,2 a[-2,-1] c is_grayscale=1
3207    else is_grayscale={s<4} channels. 0,5
3208    fi
3209
3210    # Manage coordinates of vertices.
3211    sh. 0 xm={im} xM={iM} rm.
3212    sh. 1 ym={im} yM={iM} rm.
3213    sh. 2 zm={im} zM={iM} rm.
3214    permute. cxyz s. x,2
3215    i.. (1,0;1,{$N-1}) r.. 2,$N,1,1,3 round.. 1,$N,1,1,1 # Primitives, colors and opacities.
3216    y a y c3d n3d *3d 1,-1,1
3217
3218    {if($6,max(1,$1-32),$1)},{if($6,max(1,$2-32),$2)},1,{if($is_grayscale,1,3)},255
3219    *3d[0] {0.96*min(w,h)}
3220    if $6 L={0.1*max($1,$2)} grid[1] $L,$L,0,0,0.25,0xCCCCCCCC,0 fi
3221
3222    if $5 # Anti-aliased.
3223      r[1] 200%,200%,1,100%,1 *3d[0] 2
3224      if $4 +circles3d[0] {2*$4} j3d[1] [2],50%,50%,0,1,3,0,0 rm[2] fi
3225    elif $4 # Aliased.
3226      +circles3d[0] $4 j3d[1] [2],50%,50%,0,1,3,0,0 rm[2]
3227    fi
3228
3229    # Convert point cloud to connected segments.
3230    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]
3231      r[5] 1,{h-1},1,1,0
3232      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
3233      y a y endl j3d[1] [0],50%,50%,0,{if($3>1,$3-1,$3)},2,0,0
3234    fi
3235
3236    rm[0]
3237    if $5 r. 50%,50%,1,100%,2 fi
3238
3239    if $6 # Add decoration.
3240      xc={0.5*($xm+$xM)} yc={0.5*($ym+$yM)} dx={0.5*($xM-$xm)/0.96} dy={0.5*($yM-$ym)/0.96}
3241      xm={$xc-$dx} xM={$xc+$dx} ym={$yc-$dy} yM={$yc+$dy}
3242      100%,100%,1,1,255 axes. $xm,$xM,$yM,$ym,14,1,0
3243      if $xm>0 axes. 0,0,$yM,$ym,14,1,160 fi
3244      if $xM<0 axes. {w-1},{w-1},$yM,$ym,14,1,160 fi
3245      if $ym>0 axes. $xm,$xM,{h-1},{h-1},14,1,160 fi
3246      if $yM<0 axes. $xm,$xM,0,0,14,1,160 fi
3247      +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1]
3248      frame 1,1,128 frame 15,15,220
3249      0 t. "$7",0,0,14,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm.
3250      0 t. "$8",0,0,14,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm.
3251    fi
3252    nm. $nm
3253  endl done
3254
3255#@cli dp : eq. to 'display_parallel'.
3256dp :
3257  _gmic_s="$?" v + _display_parallel 1
3258
3259#@cli display_parallel
3260#@cli : Display each selected image in a separate interactive display window.
3261#@cli : (eq. to 'dp').
3262display_parallel :
3263  _gmic_s="$?" v + _$0 1
3264
3265# $1 = Normalization used for display window.
3266_display_parallel : check ${1=0}>=0
3267  e[0--3] "Display image$? in parallel."
3268  print
3269  is_d2d_compatible={"res = l<=10; repeat (l,k, res&=(w#k==w && h#k==h && d#k==1 && s#k==s))"}
3270  if $is_d2d_compatible
3271    N=$!
3272    repeat $!
3273      if s=['{$>,n}'];find(s,_'.',size(s)-1,-1)>0 nm={$>,b}.{$>,x} else nm={$>,b} fi
3274      ('$nm':;)
3275    done
3276    store[-$N--1] _d2d_names
3277    a z _display2d 0,$1 s z
3278    _d2d_names=
3279  else
3280    if $!<=1 v - d v + return fi
3281    14,$! eval. "!x?copy(i(),[[',d['],vtos(y,10,10),_']'])" =. 0 discard. 0 str={t} rm.
3282    m "__dp : parallel "$str __dp um __dp
3283  fi
3284  v -1 d[]
3285
3286#@cli dp0 : eq. to 'display_parallel0'.
3287dp0 :
3288  _gmic_s="$?" v + _display_parallel 0
3289
3290#@cli display_parallel0
3291#@cli : Display each selected image in a separate interactive display window, without value normalization.
3292#@cli : (eq. to 'dp0').
3293display_parallel0 :
3294  _gmic_s="$?" v + _display_parallel 0
3295
3296#@cli display_polar : _width>32,_height>32,_outline_type,_fill_R,_fill_G,_fill_B,_theta_start,_theta_end,_xlabel,_ylabel
3297#@cli : Render polar curve from selected image data.
3298#@cli : 'outline_type' can be { r<0=dots with radius -r | 0=no outline | r>0=lines+dots with radius r }.
3299#@cli : 'fill_color' can be { -1=no fill | R,G,B=fill with specified color }.
3300#@cli : Default values: 'width=500', 'height=width', 'outline_type=1', 'fill_R=fill_G=fill_B=200', 'theta_start=0', \
3301# 'theta_end=360', 'xlabel="x-axis"' and 'ylabel="y-axis"'.
3302#@cli : $ 300,1,1,1,'0.3+abs(cos(10*pi*x/w))+u(0.4)' display_polar 512,512,4,200,255,200
3303#@cli : $ 3000,1,1,1,'x^3/1e10' display_polar 400,400,1,-1,,,0,{15*360}
3304display_polar : check "${1=500}>32 && ${2=$1}>32"
3305  skip ${3=1},${4=200},${5=$4},${6=$5},${7=0},${8=360},"${9=x-axis}","${10=y-axis}"
3306  e[^-1] "Render $1x$2 polar graph plot from data of image"$_gmic_s", with outline $4 and fill color ($4,$5,$6)."
3307  repeat $! l[$>] nm={0,n}
3308
3309    # Compute (x,y) coordinates of the polar curve points.
3310    M={max(abs(iM),abs(im))}
3311    * {0.48*min($1,$2)/$M}
3312    y ({$7*pi/180};{-$8*pi/180}) r. 1,..,1,1,3
3313    +sin. cos.. *. ... *[-3,-2]
3314    a[-2,-1] x N={h}
3315    nm. coords
3316
3317    # Generate 3D object for curve outline.
3318    if $3
3319      ('CImg3d') +. 0.5 ($N,$N)
3320      +z[coords] 0,2
3321      1,$N,1,1,2 1,$N,1,1,'y' ++. 1 a[-3--1] x =. 0,2,100%
3322      3,$N,1,1,0 1,$N,1,1,1 y[-6--1] a[-6--1] y
3323      nm. _plot_polar_outline
3324    fi
3325
3326    # Generate 3D object for filling.
3327    if "$4>=0 && $5>=0 && $6>=0"
3328      ('CImg3d') +. 0.5 ({$N+1},$N)
3329      +z[coords] 0,-1,2,100% z. 0,2
3330      1,$N,1,1,3 1,$N 1,$N,1,1,'1+y' ++. 1 a[-4--1] x =. 1,3,100%
3331      3,$N,1,1,$4,$5,$6 1,$N,1,1,1
3332      y[-6--1] a[-6--1] y
3333      nm. _plot_polar_fill
3334    fi
3335    rm[coords]  # Remove original curve coordinates.
3336
3337    # Render graph image.
3338    {$1-32},{$2-32},1,3,255
3339    L={0.1*max($1,$2)} grid. $L,$L,0,0,0.25,0xCCCCCCCC,0  # Draw background grid
3340    if "$4>=0 && $5>=0 && $6>=0"                          # Draw curve filling
3341      j3d. [_plot_polar_fill],50%,50%,0,1,2,1,0
3342      rm[_plot_polar_fill]
3343    fi
3344    if $3
3345      if $3>=0                                            # Draw curve outline
3346        j3d. [_plot_polar_outline],50%,50%,0,1,1,0,0
3347      fi
3348      if $3!=0                                            # Draw curve vertices
3349        if abs($3)>1 circles3d[_plot_polar_outline] {abs($3)} fi
3350        j3d. [_plot_polar_outline],50%,50%,0,0.2,2,0,0
3351      fi
3352      rm[_plot_polar_outline]
3353    fi
3354
3355    # Draw axes and frame.
3356    nM={$M/0.96}
3357    100%,100%,1,1,255 axes. {-$nM},$nM,$nM,{-$nM},14,1,0
3358    +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1]
3359    frame. 1,1,128 frame. 15,15,220
3360    0 t. "$9",0,0,13,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm.
3361    0 t. "$10",0,0,13,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm.
3362
3363  nm $nm endl done
3364
3365#@cli dq : eq. to 'display_quiver'.
3366dq :
3367  _gmic_s="$?" v + _display_quiver $*
3368
3369#@cli display_quiver : _size_factor>0,_arrow_size>=0,_color_mode={ 0=monochrome | 1=grayscale | 2=color }
3370#@cli : Render selected images of 2D vectors as a field of 2D arrows.
3371#@cli : (eq. to 'dq').
3372#@cli : Default values: 'size_factor=16', 'arrow_size=1.5' and 'color_mode=1'.
3373#@cli : $ image.jpg +luminance gradient[-1] xy rv[-2,-1] *[-2] -1 a[-2,-1] c crop 60,10,90,30 +display_quiver[1] ,
3374display_quiver :
3375  _gmic_s="$?" v + _$0 $*
3376
3377_display_quiver : check "${1=16}>0 && ${2=1.5}>=0 && isint(${3=2}) && $3>=0 && $3<=2"
3378  e[0--3] "Render field of 2D arrows from image"$_gmic_s", with size factor $1, arrow size $2 in "\
3379          ${arg\ 1+$3,monochrome,grayscale,color}" mode."
3380  repeat $! l[$>]
3381    +norm. /.. {max(1e-6,iM)} rm. # Normalize vector values.
3382    {$1*w},{$1*h},1,{"1<<cut($3,0,2)"}
3383    eval.. "
3384      begin(ref(resize([255],s#1,1),C));
3385      "${-math_lib}"
3386      len = norm2(I(x,y));
3387      ang = atan2(i(x,y,0,1),i(x,y,0,0))*180/pi;
3388      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 ];
3389      P*=$1*$2*len;
3390      X = resize([(x+0.5)*w#1/w,(y+0.5)*h#1/h],size(P),0,2);
3391      X+=mul(P,rot(-ang°),2);
3392      if ($3,
3393        v = min(1,max(0.5,3*len));
3394        if ($3==1,
3395          C = [ 255*v,255 ],
3396          C = [ hsv2rgb([ ang,1,v ]), 255 ];
3397        );
3398      );
3399      repeat (3,k,
3400        i0 = arg(k + 1,0,2,4);
3401        i1 = arg(k + 1,2,10,6);
3402        i2 = arg(k + 1,12,12,8);
3403        polygon(#1,3,X[i0,2],X[i1,2],X[i2,2],1,C);
3404      ); I"
3405    rm..
3406  endl done
3407
3408#@cli drgba : eq. to 'display_rgba'.
3409drgba : skip "${1=none},${2=$1},${3=$1}"
3410  _gmic_s="$?" v + _display_rgba ${^0} v -
3411  if !${} noarg fi
3412
3413#@cli display_rgba : _background_RGB_color
3414#@cli : Render selected RGBA images over a checkerboard or colored background.
3415#@cli : (eq. to 'drgba').
3416#@cli : Default values: 'background_RGB_color=undefined' (checkerboard).
3417#@cli : $ image.jpg +norm threshold[-1] 40% blur[-1] 3 normalize[-1] 0,255 append c display_rgba
3418display_rgba : skip "${1=none},${2=$1},${3=$1}"
3419  _gmic_s="$?" v + _$0 ${^0} v -
3420  if !${} noarg fi
3421
3422_display_rgba : skip "${1=},${2=$1},${3=$1}"
3423  l[] is_rgb={"isnum($1)"} onfail is_rgb=0 endl
3424  if $is_rgb e[0--4] "Render RGBA image"$_gmic_s" over RGB background ($*)."
3425  else e[0--4] "Render RGBA image"$_gmic_s" over a checkerboard background."
3426  fi
3427  repeat $! l[$>] if s==2" || "s==4
3428    if $is_rgb i[0] 100%,100%,1,3 fc[0] {[$*]}
3429    else i[0] (160,128;128,160) r[0] 16,16 r[0] [1],[1],1,{s-1},0,2
3430    fi
3431    nm[0] {1,n}
3432    sh. {s-1} j[0] [1],0,0,0,0,1,[2],255 k[0]
3433  fi endl done to_rgb u $is_rgb
3434
3435#@cli dt : eq. to 'display_tensors'.
3436dt :
3437  _gmic_s="$?" v + _display_tensors $*
3438
3439#@cli display_tensors : _size_factor>0,_ellipse_size>=0,_color_mode={ 0=monochrome | 1=grayscale | 2=color },_outline>=0
3440#@cli : Render selected images of tensors as a field of 2D ellipses.
3441#@cli : (eq. to 'dt').
3442#@cli : Default values: 'size_factor=16', 'ellipse_size=1.5', 'color_mode=2' and 'outline=2'.
3443#@cli : $ image.jpg +diffusiontensors 0.1,0.9 resize2dx. 32 +display_tensors. 64,2
3444#@cli : $$ https://gmic.eu/oldtutorial/_display_tensors
3445display_tensors :
3446  _gmic_s="$?" v + _$0 $*
3447
3448_display_tensors : check "${1=16}>0 && ${2=1.5}>=0 && isint(${3=2}) && $3>=0 && $3<=2 && ${4=2}>=0"
3449  e[0--3] "Render field of 2x2 tensors from image"$_gmic_s", with size factor $1, ellipse size $2 in "\
3450          ${arg\ 1+$3,monochrome,grayscale,color}" mode and outline $4."
3451  repeat $! l[$>]
3452    * {($2*$1/2)/max(abs(im),abs(iM))} # Normalize tensor values.
3453    {$1*w},{$1*h},1,{"1<<cut($3,0,2)"}
3454    f.. "
3455      begin(C = resize([255],s#1,1); Co = resize([0],s#1,1); Co[s#1 - 1] = $3?255:0);
3456      "${-math_lib}"
3457      X = ([ x,y ]+=0.5)*$1;
3458      T = [ R, G, G, B ];
3459      E = eig(T);
3460      r1 = E[0];
3461      r2 = E[1];
3462      ang = atan2(E[3],E[2])*180/pi;
3463      if ($3,
3464        v = min(1,max(0.5,3*r1/($1*$2)));
3465        if ($3==1,
3466          C = [ 255*v,255 ],
3467          C = [ hsv2rgb([ 2*ang,1-r2/r1,v ]), 255 ];
3468        );
3469      );
3470      for (k = 1, k>=0, --k, ellipse(#1,X,r1 + k*$4,r2 + k*$4,ang°,1,arg(k + 1,C,Co)));
3471      I"
3472    rm..
3473  endl done
3474
3475#@cli dw : eq. to 'display_warp'.
3476dw :
3477  _gmic_s="$?" v + _display_warp $*
3478
3479#@cli display_warp : _cell_size>0
3480#@cli : Render selected 2D warping fields.
3481#@cli : (eq. to 'dw').
3482#@cli : Default value: 'cell_size=15'.
3483#@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
3484display_warp :
3485  _gmic_s="$?" v + _$0 $*
3486
3487_display_warp : check "${1=15}>0"
3488  e[0--3] "Render 2D warping field"$_gmic_s", with cell size $1."
3489  repeat $! l[$>]
3490    if d!=1" || "s!=2
3491      error[0--3] "Command 'display_warp': Invalid image ["{$!-$>-1}"]: Dimensions "{w}","{h}","{d}","{s}"
3492       does not represent a 2D field of 2D vectors."
3493    fi
3494    i[0] 100%,100%,1,1,1 grid[0] $1,$1 nm[0] {1,n}
3495    warp[0] [1],1,1,0 rm[1]
3496  endl done * 255
3497
3498#@cli e : eq. to 'echo'. : (+)
3499
3500#@cli echo : message : (+)
3501#@cli : Output specified message on the error output.
3502#@cli : (eq. to 'e').\n
3503#@cli : Command selection (if any) stands for displayed call stack subset instead of image indices.
3504#@cli : When invoked with a '+' prefix (i.e. '+echo'), the command output its message on stdout rather than stderr.
3505
3506#@cli echo_file : filename,message
3507#@cli : Output specified message, appending it to specified output file.
3508#@cli : (similar to 'echo' for specified output file stream).
3509echo_file : skip "${2='\n'}"
3510  ('"${2--1}\n"') ot. $1 rm.
3511
3512#@cli function1d : 0<=smoothness<=1,x0>=0,y0,x1>=0,y1,...,xn>=0,yn
3513#@cli : Insert continuous 1D function from specified list of keypoints (xk,yk)
3514#@cli : in range [0,max(xk)] (xk are positive integers).
3515#@cli : $ function1d 1,0,0,10,30,40,20,70,30,80,0 +display_graph 400,300
3516+function1d :
3517  e[^-1] "Input continuous 1D function, with smoothness $1 and keypoints (${2--1})."
3518  l[]
3519
3520  # Sort and normalize input keypoints.
3521  smoothness={max(0,min(1,$1))}
3522  (${2--1}) r 2,{int(w/2)},1,1,-1
3523  sort +,y s x size={0,iM>=0?1+int(iM):0}
3524  if !$size rm 0 break fi
3525  a x
3526
3527  # Compute slopes for splines.
3528  +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
3529
3530  # Determine spline coefficients for each part of the curve.
3531  $size,1,1,1,-1
3532  repeat h#0-1
3533    x0={0,i(0,$>)} y0={0,i(1,$>)} x1={0,i(0,$>+1)} y1={0,i(1,$>+1)}
3534    slope={($y1-$y0)/max(0.01,$x1-$x0)}
3535    yp0={0,i(2,$>)*$smoothness+(1-$smoothness)*$slope}
3536    yp1={0,i(2,$>+1)*$smoothness+(1-$smoothness)*$slope}
3537    i={round($x0,1,1)} j={round($x1,1,0)}
3538    line[1] $i,0,$j,0,1,$>
3539    if $j-$i<=1 # Linear interpolation for very close points.
3540      ({$y0-$x0*$slope}^{$slope}^0^0)
3541    else # Cubic interpolation otherwise.
3542      (1,$x0,{($x0)^2},{($x0)^3};\
3543       1,$x1,{($x1)^2},{($x1)^3};\
3544       0,1,{2*$x0},{3*($x0)^2};\
3545       0,1,{2*$x1},{3*($x1)^2})
3546      ($y0;$y1;$yp0;$yp1)
3547      invert.. mmul[-2,-1] y. c
3548    fi
3549  done
3550  a[2--1] x map.. . rm.
3551
3552  # Render final curve.
3553  100%,1,1,1,1
3554  (0,{w-1}) r. {-2,w},1,1,1,3 round.
3555  +sqr. +*[-2,-1] a[-4--1] c *[-2,-1] s. c +[-4--1] rm..
3556  endl
3557
3558#@cli i : eq. to 'input'. : (+)
3559
3560#@cli input : \
3561# [type:]filename : \
3562# [type:]http://URL : \
3563# [selection]x_nb_copies>0 : \
3564# { width>0[%] | [image_w] },{ _height>0[%] | [image_h] },{ _depth>0[%] | [image_d] },{ _spectrum>0[%] \
3565# | [image_s] },_{ value1,_value2,... | 'formula' } : \
3566# (value1{,|;|/|^}value2{,|;|/|^}...[:{x|y|z|c|,|;|/|^}]) : \
3567# 0 : (+)
3568#@cli : Insert a new image taken from a filename or from a copy of an existing image [index],
3569#@cli : or insert new image with specified dimensions and values. Single quotes may be omitted in
3570#@cli : 'formula'. Specifying argument '0' inserts an 'empty' image.
3571#@cli : (eq. to 'i' | (no arg)).
3572#@cli : Default values: 'nb_copies=1', 'height=depth=spectrum=1' and 'value1=0'.
3573#@cli : $ input image.jpg
3574#@cli : $ input (1,2,3;4,5,6;7,8,9^9,8,7;6,5,4;3,2,1)
3575#@cli : $ image.jpg (1,2,3;4,5,6;7,8,9) (255^128^64) 400,400,1,3,'(x>w/2?x:y)*c'
3576#@cli : $$
3577
3578#@cli input_565 : filename,width>0,height>0,reverse_endianness={ 0 | 1 }
3579#@cli : Insert image data from a raw RGB-565 file, at the end of the list.
3580#@cli : Default value: 'reverse_endianness=0'.
3581+input_565 : check "isint($2) && $2>0 && isint($3) && $3>0 && isbool(${4=0})"
3582  e[^-1] "Input raw RGB-565 file '$1', with size $2x$3."
3583  l[] raw:"$1",ushort if $4 endian ushort fi
3584  r $2,$3,1,1,-1 +>> 5 &. 63 +&.. 31 >>... 11 *[-3,-1] 8 *.. 4 a c endl
3585
3586#@cli input_csv : "filename",_read_data_as={ 0=numbers | 1=strings | _variable_name }
3587#@cli : Insert number of string array from specified .csv file.
3588#@cli : If 'variable_name' is provided, the string of each cell is stored in a numbered variable '_variable_name_x_y', \
3589# where 'x' and 'y' are the indices of the cell column and row respectively (starting from '0').
3590#@cli : Otherwise, a 'WxH' image is inserted at the end of the list, with each vector-valued pixel 'I(x,y)' encoding \
3591# the number or the string of each cell.
3592#@cli : This command returns the 'W,H' dimension of the read array, as the status.
3593#@cli : Default value: 'read_data_as=1'.
3594+input_csv : check "isvarname('${2=1}') || isin($2,0,1,2)"
3595  l[]
3596    is_var={"s=['$2'];s!='0'&&s!='1'"}
3597    if $is_var # Read data as new variables
3598      e[0--4] "Input string array from file '$1', in variable '$2'."
3599      it "$1" replace {'" "'},255 s -,{'\n'}
3600      W,H=0,$!
3601      repeat $! _input_csv_var[] $2,$>,${u\ {$>,t}} W={max($W,${})} done
3602      rm u $W,$H
3603    else # Read data as a new image
3604      s0,s1="number","string"
3605      e[0--4] "Input "${s$2}" array from file '$1'."
3606      it "$1" s -,{'\n'} replace {'" "'},255
3607      if $2 repeat $! _input_csv_str[] ${u\ {$>,t}} rv[$>,-1] rm. done a y replace 255,{'" "'} # Read as strings
3608      else repeat $! _input_csv_val[] ${u\ {$>,t}} rv[$>,-1] rm. done a y # Read as values
3609      fi
3610      nm "$1" u {[w,h]}
3611    fi
3612  endl
3613
3614_input_csv_var :
3615  $=arg
3616  W={$#-2}
3617  repeat $W
3618   arg=${arg{3+$>}}
3619   if ['$arg']!=0 ({'${arg{3+$>}}'}) replace. 255,{'" "'} $1_$>_$2={t} rm. else $1_$>_$2= fi
3620  done
3621  u $W
3622
3623_input_csv_str :
3624  $=arg
3625  repeat $#
3626    arg=${arg{1+$>}}
3627    if ['$arg']!=0 ({'$arg'}:c) else (0) fi
3628  done
3629  a x
3630
3631_input_csv_val :
3632  $=arg
3633  repeat $#
3634    arg=${arg{1+$>}}
3635    if ['$arg']!=0 ({stov(['$arg'])}) else (nan) fi
3636  done
3637  a x
3638
3639#@cli input_cube : "filename",_convert_1d_cluts_to_3d={ 0 | 1 }.
3640#@cli : Insert CLUT data from a .cube filename (Adobe CLUT file format).
3641#@cli : Default value: 'convert_1d_cluts_to_3d=1'.
3642+input_cube : skip ${2=1}
3643  e[^-1] "Input CLUT from file '$1'"
3644  l[]
3645    it[] "$1" f "i<_' ' && i!=10?_' ':i" s -,10
3646    i[0] 0
3647    range={"
3648      ref(vector128(),line);
3649      dmin = [ 0,0,0 ];
3650      dmax = [ 1,1,1 ];
3651      dim = size = 0;
3652      target = 0;
3653      for (k = 1, k<l, ++k,
3654        linesiz = min(size(line)-1,h#k);
3655        copy(line[0],i[#k,0],linesiz);
3656        copy(line[linesiz],0,size(line) - linesiz,1,0);
3657        same(line,'LUT_1D_SIZE ',12)?(
3658          copy(line[0],line[12],size(line) - 12);
3659          size = stov(line);
3660          dim = 1;
3661          resize(#0,3,size,1,1,0);
3662        ):same(line,'LUT_3D_SIZE ',12)?(
3663          copy(line[0],line[12],size(line) - 12);
3664          size = stov(line);
3665          dim = 3;
3666          resize(#0,3,size,size,size,0);
3667        ):same(line,'DOMAIN_MIN ',11)?(
3668          copy(line[0],line[11],size(line) - 11);
3669          dmin[0] = stov(line);
3670          ind = find(line,_' ');
3671          copy(line[0],line[ind + 1],size(line) - ind);
3672          dmin[1] = stov(line);
3673          ind = find(line,_' ');
3674          copy(line[0],line[ind + 1],size(line) - ind);
3675          dmin[2] = stov(line);
3676        ):same(line,'DOMAIN_MAX ',11)?(
3677          copy(line[0],line[11],size(line) - 11);
3678          dmax[0] = stov(line);
3679          ind = find(line,_' ');
3680          copy(line[0],line[ind + 1],size(line) - ind);
3681          dmax[1] = stov(line);
3682          ind = find(line,_' ');
3683          copy(line[0],line[ind + 1],size(line) - ind);
3684          dmax[2] = stov(line);
3685        ):(
3686          val = stov(line);
3687          !isnan(val)?do (
3688            i[#0,target++] = val;
3689            ind = find(line,_' ');
3690            ind<0?break();
3691            copy(line[0],line[ind + 1],size(line) - ind);
3692            val = stov(line);
3693          ,_(while); !isnan(val));
3694        );
3695      );
3696      [dmin,dmax];"}
3697
3698    k[0]
3699    permute yzcx
3700    if [$range]!=[0,0,0,1,1,1]
3701      f "begin(
3702           range = ["$range"];
3703           dmin = range[0,3];
3704           dmax = range[3,3];
3705           delta = dmax - dmin;
3706         );
3707         (I - dmin)*255/delta"
3708    else * 255
3709    fi
3710    if "w>1 && h==1 && d==1 && $2" size={w} s c y.. y. z r $size,$size,$size a c fi
3711    nm "$1"
3712  endl
3713
3714#@cli input_flo : "filename"
3715#@cli : Insert optical flow data from a .flo filename (vision.middlebury.edu file format).
3716+input_flo :
3717  e[^-1] "Input optical flow from file '$1'."
3718  l[]
3719    i raw:"$1",float
3720    if i!=202021.25 endian. fi
3721    if i!=202021.25 error[0--3] "Command 'input_flo': Filename '$1' is not a valid .flo file." return fi
3722    +rows 1,2 cast. float,uint w,h={^} rm.
3723    rows 3,100% r 2,$w,$h,1,-1 permute yzcx
3724  endl
3725
3726#@cli ig : eq. to 'input_glob'.
3727+ig :
3728  v + _input_glob "$*"
3729
3730#@cli input_glob : pattern
3731#@cli : Insert new images from several filenames that match the specified glob pattern.
3732#@cli : (eq. to 'ig').
3733+input_glob :
3734  _input_glob "$*"
3735
3736_input_glob :
3737  e[0--3] "Input all files that match glob pattern '$*'."
3738  files 3,"$*"
3739  N=$!
3740  m "_ig : $""=arg repeat $""# i ${arg{1+$>}} done"
3741  _ig ${} um _ig
3742  if $N==$! error[0--3] "Command 'input_glob': No matching filenames for pattern '$*'." fi
3743
3744#@cli input_gpl : filename
3745#@cli : Input specified filename as a .gpl palette data file.
3746+input_gpl :
3747  e[^-1] "Input .gpl palette file '$*'."
3748  l[]
3749    it[] "$*" discard 13 replace 9,32 s -,10
3750    colors=0
3751    repeat $! l[$>]
3752      s -,32
3753      if $!>=3" && "isint({0,t})" && "isint({1,t})" && "isint({2,t}) colors=$colors;{0,t},{1,t},{2,t} fi
3754      rm 0
3755    onfail rm 0 endl done
3756    rm ($colors) rows 1,100% nm "$1" permute yzcx
3757  endl
3758
3759#@cli input_cached : "basename.ext",_try_downloading_from_gmic_server={ 0 | 1 }
3760#@cli : Input specified filename, assumed to be stored in one of the G'MIC resource folder.
3761#@cli : If file not found and 'try_downloading=1', file is downloaded from the G'MIC server and stored
3762#@cli : in the '${-path_cache}' folder.
3763#@cli : Default value: 'try_downloading_from_gmic_server=1'.
3764+input_cached : check "isbool(${2=1})"
3765  basename "$1" basename=${}
3766  e[^-1] "Input cached file '"$basename"'."
3767  if ['$GMIC_SYSTEM_PATH']!=0 g_path_unix=$GMIC_SYSTEM_PATH/ else g_path_unix=/usr/lib/gimp/2.0/plug-ins/ fi
3768  path_test0=${-path_cache}
3769  path_test1=$_path_rc
3770  path_test2=${-path_gimp}plug-ins/
3771  path_test3=${-path_gimp}plug-ins/gmic_gimp_qt/
3772  path_test4=$g_path_unix
3773  file_found=0
3774  repeat 5
3775    file=${path_test$>}$basename
3776    l[] i $file file_found=1 onfail endl
3777    if $file_found break fi
3778  done
3779  if !$file_found" && $2" # Try downloading from the G'MIC server
3780    url=https://gmic.eu/"$1"
3781    l[] i $url o ${-path_cache}$basename file_found=1 is_change 1
3782    onfail
3783    endl
3784  fi
3785  if !$file_found
3786    error[0--3] "Command 'input_cached': Unknown filename '$1'."
3787  fi
3788
3789#@cli it : eq. to 'input_text'.
3790+it :
3791  v + _input_text "$*"
3792
3793#@cli input_text : filename
3794#@cli : Input specified text-data filename as a new image.
3795#@cli : (eq. to 'it').
3796+input_text :
3797  v + _$0 "$*"
3798
3799_input_text :
3800  e[0--3] "Input text-data file '$*'."
3801  i raw:"$*",uchar
3802  if i[0]==239" && "i[1]==187" && "i[2]==191 rows. 3,100% fi # Remove BOM
3803  discard. {'\r'} # Remove CR
3804
3805#@cli lorem : _width>0,_height>0
3806#@cli : Input random image of specified size, retrieved from Internet.
3807#@cli : Default values: 'width=height=800'.
3808+lorem : check "isint(${1=800}) && $1>0 && isint(${2=$1}) && $2>0"
3809  e[^-1] "Input random image of size $1x$2."
3810  i "https://picsum.photos/$1/$2"
3811
3812# Merge multi-lines (lines continued by backslash before NL) in a string image.
3813merge_multiline :
3814  eval[^] "*i==_'\\' && j[-1]!=_'\\' && j[+1]==_'\n'?(
3815            for (p = 2, j[p] && j[p]<=_' ' && j[p]!=_'\n', ++p); copy(i(),-1,p,1,0))"
3816  discard -1
3817
3818# Merge multi-line comments in a string image.
3819merge_multiline_comments :
3820  eval[^] "*i==_'\\' && j[-1]!=_'\\' && j[+1]==_'\n' && j[+2]==_'#'?(
3821            for (p = 3, j[p] && j[p]<=_' ' && j[p]!=_'\n', ++p); copy(i(),-1,p,1,0))"
3822  discard -1
3823
3824#@cli network : mode={ -1=disabled | 0=enabled w/o timeout | >0=enabled w/ specified timeout in seconds } : (+)
3825#@cli : Enable/disable load-from-network and set corresponding timeout.
3826#@cli : (Default mode is 'enabled w/o timeout').
3827
3828#@cli o : eq. to 'output'. : (+)
3829
3830#@cli output : [type:]filename,_format_options : (+)
3831#@cli : Output selected images as one or several numbered file(s).
3832#@cli : (eq. to 'o').
3833#@cli : Default value: 'format_options'=(undefined).
3834
3835#@cli output_565 : "filename",reverse_endianness={ 0=false | 1=true }
3836#@cli : Output selected images as raw RGB-565 files.
3837#@cli : Default value: 'reverse_endianness=0'.
3838output_565 : check "isbool(${2=0})"
3839  e[^-1] "Output image$? as raw RGB-565 file '$1'."
3840  N=$! repeat $N +l[$>]
3841    s c c 0,255 /[-3,-1] 8 /.. 4 round
3842    bsl... 11 bsl.. 5 +
3843    if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi
3844    if $2 endian ushort fi
3845    o raw:$fn,ushort rm
3846  endl done is_change 0
3847
3848#@cli output_cube : "filename"
3849#@cli : Output selected CLUTs as a .cube file (Adobe CLUT format).
3850output_cube :
3851 e[^-1] "Output CLUT$? as file '$1'."
3852 N=$! repeat $N +l[$>] to_rgb
3853   l={round((w*h*d)^(1/3))}
3854   if w*h*d!=$l^3 error "Command '$0': CLUT '"{n}"' has invalid dimensions "({w},{h},{d},{s}). fi
3855   r $l,$l,$l,3,-1 permute cxyz / 255
3856   if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi
3857   o dlm:$fn rm it[] $fn replace {','},32
3858   0 nm. $fn basename={b} rm.
3859   header="\# Created by: G'MIC (https://gmic.eu)\n"\
3860          "TITLE \""$basename"\"\n\n"\
3861          "# LUT size\n"\
3862          "LUT_3D_SIZE "$l"\n\n"\
3863          "# Data domain\n"\
3864          "DOMAIN_MIN 0.0 0.0 0.0\n"\
3865          "DOMAIN_MAX 1.0 1.0 1.0\n\n"\
3866          "# LUT data points\n"
3867   i[0] ('$header')
3868   y a y ot $fn rm
3869 endl done is_change 0
3870
3871#@cli output_flo : "filename"
3872#@cli : Output selected optical flow as a .flo file (vision.middlebury.edu file format).
3873output_flo :
3874  e[^-1] "Output optical flow$? as file '$1'."
3875  N=$! repeat $N +l[$>]
3876   w,h={[w,h]}
3877   channels 0,1 permute cxyz i[0] (202021.25) i[1] ($w,$h) cast[1] uint,float y a y
3878   if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi
3879   o raw:$fn,float rm
3880 endl done is_change 0
3881
3882#@cli output_ggr : filename,_gradient_name
3883#@cli : Output selected images as .ggr gradient files (GIMP).
3884#@cli : If no gradient name is specified, it is deduced from the filename.
3885output_ggr : skip "${2=}"
3886 e[^-1] "Output image$? as .ggr gradient file '$1'."
3887 N=$!
3888  repeat $N +l[$>] r 1,{w*h*d},1,100%,-1 to_rgba / 255
3889    if narg("$2") name="$2"
3890    else l[] 1 nm. "$1" ('{b}') f "if(x==0 && i>=_'a' && i<=_'z',i-_'a'+_'A',i)" name={t} rm endl
3891    fi
3892    ('"GIMP Gradient\nName: "$name\n{0,h}\n')
3893    repeat h#0
3894      start={_$>/{0,h}}
3895      end={_($>+1)/{0,h}}
3896      mid={_0.5*($start+$end)}
3897      rgba={0,I(0,$>)}
3898      r={arg(1,$rgba)} g={arg(2,$rgba)} b={arg(3,$rgba)} a={arg(4,$rgba)}
3899      ('$start" "$mid" "$end" "$r" "$g" "$b" "$a" "$r" "$g" "$b" "$a" 0 0\n"')
3900    done
3901    rm[0] a x
3902    if $N>1 ot ${"filename \"$1\",$>"} else ot "$1" fi
3903    rm
3904  endl done is_change 0
3905
3906#@cli output_gmz : filename,_datatype
3907#@cli : Output selected images as .gmz files (G'MIC native file format).
3908#@cli : 'datatype' can be { bool | uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }.
3909output_gmz : skip ${2=auto}
3910  e[^-1] "Output image$? as gmz file '$1', with pixel type '$2'."
3911  1,64
3912  eval "
3913    draw([_'G',_'M',_'Z'],0,0,0,0,1,3);
3914    pos = 4;
3915    repeat (l - 1,k,
3916      ref(name(#k,1026),nam);
3917      len = find(nam,0);
3918      len>=0?(
3919        pos + len>=h?resize(#-1,1,2*h + len,1,1,0);
3920        len>0?draw(nam,0,pos,0,0,1,len);
3921        pos+=1 + len;
3922      );
3923    );
3924    resize(#-1,1,pos,1,1,0)"
3925  o cimgz:$1,$2
3926  rm.
3927
3928#@cli output_obj : filename,_save_materials={ 0=no | 1=yes }
3929#@cli : Output selected 3D objects as Wavefront 3D object files.
3930#@cli : Set 'save_materials' to '1' to produce a corresponding material file (`.mtl`) and eventually texture files.
3931#@cli : Beware, the export to `.obj` files may be quite slow for large 3D objects.
3932#@cli : Default value: 'save_materials=1'.
3933output_obj : check "isbool(${2=1})" check3d 0
3934  s0,s1=out,
3935  e[^-1] "Output 3D object$? as Wavefront 3D object file '$1' (with"${s$2}" materials)."
3936  N=$! repeat $N nm={$>,n} +l[$>]
3937    nb_materials,nb_primitives,nb_textures,nb_tcoords=0
3938
3939    # Generate basename, file header and eval macros.
3940    if $N>1 filename=${"filename \"$1\",$>"} else filename="$1" fi
3941    0 nm. $filename basename={b} folder={f} ext={x} if narg($ext) ext=.$ext fi rm.
3942    fnobj="begin(out = 0);
3943           tos(x) = vtos(x,6);
3944           write(ind,s) = (
3945             l_s = find(s,0);
3946             l_s<=0?(l_s = size(s));
3947             out + l_s>h(#ind)?resize(#ind,1,2*h(#ind) + l_s,1,1,0);
3948             copy(i[#ind,out],s,l_s);
3949             out+=l_s)"
3950
3951    # Decompose into vertices, primitives and colors.
3952    s3d 0 nb_vertices,nbp={1,f2ui([i[0],i[1]])} rm[0,1,5]
3953    e[] "  > File '"$folder$basename$ext"' ("$nb_vertices" vertices, "$nbp" primitives)."
3954
3955    # Generate vertex output.
3956    l[0]
3957      e[] "    - Export vertices."
3958      r 3,{h/3},1,1,-1 permute zycx
3959      0 nm. out_obj
3960      eval.. ">"$fnobj";
3961        !(y%10000)?run('e[] \"\r    - Export vertices: ',round(100*y/h),' %  \"');
3962        ref(string('v ',tos(i0),' ',tos(i1),' ',tos(i2),'\n'),str);
3963         write(#"$out_obj",str); I;
3964        end(resize(#"$out_obj",1,out,1,1,0))"
3965      rm[0] i[0] ('"\n# Vertices.\n"':y) a y nm out_obj
3966      e[] "\r    - Export vertices: Done."
3967    endl
3968
3969    # Generate material output.
3970    if $2 l.
3971      e[] "    - Export materials."
3972      256,256,256 nm. materials_rgb
3973      1,$nbp,1,3 nm. materials
3974      0 nm. out_mtl
3975      mv[0] $!
3976      eval $fnobj";
3977        const nbp = "$nbp";
3978        off = nb_materials = nb_textures = 0;
3979        repeat (nbp,p,
3980          !(p%10000)?run('e[] \"\r    - Export materials: ',round(100*p/nbp),' %  \"');
3981
3982          i[off]!=-128?( # RGB color
3983            ref(cut(round(crop(0,off,1,3)),0,255),rgb);
3984            m = i(#"$materials_rgb",rgb);
3985            !m?(
3986              ++nb_materials;
3987              ref(string('newmtl m',nb_materials,'\n',
3988                         'Kd ',tos(rgb[0]/255),' ',tos(rgb[1]/255),' ',tos(rgb[2]/255),'\n'),str);
3989              write(#"$out_mtl",str);
3990              m = i(#"$materials_rgb",rgb) = nb_materials;
3991            );
3992            i[#"$materials",p] = m;
3993            off+=3;
3994
3995          ):i[off + 2]?( # Non-shared texture
3996            ++nb_materials; ++nb_textures;
3997            wt = i[off + 1]; ht = i[off + 2]; st = i[off + 3];
3998            run('+rows. ',off + 4,',',off + 3 + wt*ht*st,' r. ',wt,',',ht,',1,',st,',-1 tfn=$folder${basename}_t',
3999                nb_textures,'.png o. $tfn rm.');
4000            tfile = get('tfn',1024,1);
4001            ref(string('newmtl m',nb_materials,'\n',
4002                       'map_Kd ',get('tfn',1024,1),'\n'),str);
4003            write(#"$out_mtl",str);
4004            I[#"$materials",p] = [ nb_materials,wt,ht ];
4005            off+=4 + wt*ht*st;
4006
4007          ):( # Shared texture
4008            tind = i[off + 1];
4009            I[#"$materials",p] = I[#"$materials",tind];
4010            off+=4;
4011
4012          );
4013        );
4014        resize(#"$out_mtl",1,out,1,1,0);
4015        run('nb_materials,nb_textures=',nb_materials,',',nb_textures)"
4016      e[] "\r    - Export materials: Done."
4017      k[materials,out_mtl]
4018    endl fi
4019
4020    # Generate primitive output.
4021    e[] "    - Export primitives."
4022    0 nm. out_prim
4023    mv[1] $!
4024    eval $fnobj";
4025      const materials = 0"$materials";
4026      const nbp = "$nbp";
4027      off = p_material = material = nb_primitives = nb_tcoords = 0;
4028      repeat (nbp,p,
4029        !(p%10000)?run('e[] \"\r    - Export primitives: ',round(100*p/nbp),' %  \"');
4030
4031        type = i[off];
4032        $2?material = i[#materials,p];
4033        material!=p_material?ref(string('usemtl m',material,'\n'),str_mtl);
4034
4035        type==1?( # Colored point
4036          ref(string('p ',f2ui(i[off + 1] + 1),'\n'),str);
4037          write(#"$out_prim",str);
4038          ++nb_primitives;
4039
4040        ):type==2?( # Colored or textured segment
4041          ref(string('l ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,'\n'),str);
4042          write(#"$out_prim",str);
4043          ++nb_primitives;
4044
4045        ):type==3 || (type==9 && !$2)?( # Colored triangle
4046          $2 && material!=p_material?write(#"$out_prim",str_mtl);
4047          ref(string('f ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,' ',f2ui(i[off + 3]) + 1,'\n'),str);
4048          write(#"$out_prim",str);
4049          ++nb_primitives;
4050
4051        ):type==4 || (type==12 && !$2)?( # Colored quadrangle
4052          $2 && material!=p_material?write(#"$out_prim",str_mtl);
4053          ref(string('f ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,' ',
4054                     f2ui(i[off + 3]) + 1,' ',f2ui(i[off + 4]) + 1,'\n'),str);
4055          write(#"$out_prim",str);
4056          ++nb_primitives;
4057
4058        ):type==9?( # Textured triangle
4059          material!=p_material?write(#"$out_prim",str_mtl);
4060          wt = i(#materials,0,p,0,1); ht = i(#materials,0,p,0,2);
4061          ref(string('vt ',tos(i[off + 4]/wt),' ',tos(1 - i[off + 5]/ht),'\n',
4062                     'vt ',tos(i[off + 6]/wt),' ',tos(1 - i[off + 7]/ht),'\n',
4063                     'vt ',tos(i[off + 8]/wt),' ',tos(1 - i[off + 9]/ht),'\n',
4064                     'f ',f2ui(i[off + 1]) + 1,'/',nb_tcoords + 1,' ',f2ui(i[off + 2]) + 1,'/',nb_tcoords + 2,
4065                     ' ',f2ui(i[off + 3]) + 1,'/',nb_tcoords + 3,'\n'),str);
4066          write(#"$out_prim",str);
4067          nb_tcoords+=3;
4068          ++nb_primitives;
4069
4070        ):type==12?( # Textured quadrangle
4071          material!=p_material?write(#"$out_prim",str_mtl);
4072          wt = i(#materials,0,p,0,1); ht = i(#materials,0,p,0,2);
4073          ref(string('vt ',tos(i[off + 5]/wt),' ',tos(1 - i[off + 6]/ht),'\n',
4074                     'vt ',tos(i[off + 7]/wt),' ',tos(1 - i[off + 8]/ht),'\n',
4075                     'vt ',tos(i[off + 9]/wt),' ',tos(1 - i[off + 10]/ht),'\n',
4076                     'vt ',tos(i[off + 11]/wt),' ',tos(1 - i[off + 12]/ht),'\n',
4077                     'f ',f2ui(i[off + 1]) + 1,'/',nb_tcoords + 1,' ',f2ui(i[off + 2]) + 1,'/',nb_tcoords + 2,
4078                     ' ',f2ui(i[off + 3]) + 1,'/',nb_tcoords + 3,' ',f2ui(i[off + 4]) + 1,'/',nb_tcoords + 4,'\n'),str);
4079          write(#"$out_prim",str);
4080          nb_tcoords+=4;
4081          ++nb_primitives;
4082
4083        ):( # Unsupported primitive
4084          run(['warn[] \"Command 'output_obj': Cannot convert primitive \#'],p,'/',nbp - 1,
4085               ' (size ',type,'). Ignoring it.\"');
4086        );
4087
4088        off+=type + 1;
4089        p_material = material;
4090      );
4091      resize(#"$out_prim",1,out,1,1,0);
4092      run('nb_primitives=',nb_primitives)"
4093    e[] "\r    - Export primitives: Done."
4094    l[out_obj,out_prim] i[1] ('"\n# Primitives.\n"':y) a y endl
4095    if $2 k[out_mtl,out_obj] else k[out_obj] fi
4096
4097    # Generate file header.
4098    header="# Object name: "$nm\n\
4099           "# Vertices: "$nb_vertices\n\
4100           "# Primitives: "$nb_primitives\n\
4101           "# Materials: "$nb_materials\n\
4102           "# Textures: "$nb_textures\n\
4103           {`"d2(x) = (s = date(x); s>9?vtos(s,-1,2):[0,s]+_'0');
4104              string('# Generated on ',d2(0),'/',d2(1),'/',d2(2),' at ',d2(4),':',d2(5),':',d2(6),
4105                     ' by G\47MIC (https://gmic.eu).\n')"`}
4106
4107    # Save object geometry (.obj file).
4108    l[out_obj]
4109      i[0] ('"# File: "$folder$basename$ext\n$header':y)
4110      if $2 i[1] ('"\n# Materials.\n\
4111                      mtllib "$folder$basename$ext".mtl\n"':y) fi
4112      a y
4113      o raw:$folder$basename$ext,uchar
4114    endl
4115
4116    # Save object materials (.mtl file).
4117    if $2 l[out_mtl]
4118      i[0] ('"# File: "$folder$basename$ext.mtl\n$header\n':y)
4119      a y
4120      o raw:$folder$basename$ext.mtl,uchar
4121    endl fi
4122
4123    rm
4124  endl done is_change 0
4125
4126#@cli ot : eq. to 'output_text'.
4127ot :
4128  _gmic_s="$?" v + _output_text "$*"
4129
4130#@cli output_text : filename
4131#@cli : Output selected images as text-data filenames.
4132#@cli : (eq. to 'ot').
4133output_text :
4134  _gmic_s="$?" v + _$0 "$1"
4135
4136_output_text :
4137  e[0--3] "Output image"$_gmic_s" as text-data file '$1'."
4138  o raw:"$1",uchar
4139
4140#@cli on : eq. to 'outputn'.
4141on :
4142  _gmic_s="$?" v + _outputn $*
4143
4144#@cli outputn : filename,_index
4145#@cli : Output selected images as automatically numbered filenames in repeat...done loops.
4146#@cli : (eq. to 'on').
4147outputn :
4148  _gmic_s="$?" v + _$0 $*
4149
4150_outputn : skip "${2=}"
4151  if $#==1 filename=${filename\ "$1",$>}
4152  else filename=${filename\ "$1",$2}
4153  fi
4154  e[0--3] "Output image"$_gmic_s" as file '"$filename"'."
4155  o $filename
4156
4157#@cli op : eq. to 'outputp'.
4158op :
4159  _gmic_s="$?" v + _outputp $*
4160
4161#@cli outputp : prefix
4162#@cli : Output selected images as prefixed versions of their original filenames.
4163#@cli : (eq. to 'op').
4164#@cli : Default value: 'prefix=_'.
4165outputp :
4166  _gmic_s="$?" v + _$0 $*
4167
4168_outputp : skip ${1="_"}
4169  if $!>1 e[0--4] "Output image"$_gmic_s" as their initial locations, prefixed by '$1'."
4170  else e[0--4] "Output image"$_gmic_s" as its initial location, prefixed by '$1'."
4171  fi
4172  repeat $! o[$>] {$>,f}$1{$>,b}.{$>,x} done
4173
4174#@cli ow : eq. to 'outputw'.
4175ow :
4176  _gmic_s="$?" v + _outputw
4177
4178#@cli outputw
4179#@cli : Output selected images by overwriting their original location.
4180#@cli : (eq. to 'ow').
4181outputw :
4182  _gmic_s="$?" v + _$0 $*
4183
4184_outputw :
4185  if $!>1 e[0--4] "Output image"$_gmic_s" as their initial location."
4186  else e[0--4] "Output image"$_gmic_s" as its initial location."
4187  fi
4188  repeat $! o[$>] {$>,n} done
4189
4190#@cli ox : eq. to 'outputx'.
4191ox :
4192  _gmic_s="$?" v + _outputx $*
4193
4194#@cli outputx : extension1,_extension2,_...,_extensionN,_output_at_same_location={ 0 | 1 }
4195#@cli : Output selected images with same base filenames but for N different extensions.
4196#@cli : (eq. to 'ox').
4197#@cli : Default value: 'output_at_same_location=0'.
4198outputx :
4199  _gmic_s="$?" v + _$0 $*
4200
4201_outputx :
4202  $=arg
4203  is_last_arg=0 is_same_location=0
4204  if isnum($-1) is_last_arg={isint($-1)" && "$-1>=0" && "$-1<=1} is_same_location=$-1 fi
4205  N={$#-$is_last_arg} s0= s1=s
4206  if !$N e[0--3] "Output image"$_gmic_s" at same location, with same base filename but extension ''
4207                      (skipped, no extension provided)." return
4208  fi
4209  if $is_same_location
4210    if $is_last_arg
4211      e[0--4] "Output image"$_gmic_s" at same location, with same base filename but extension"${s{$N>1}}"' ${^-1}'."
4212    else
4213      e[0--4] "Output image"$_gmic_s" at same location, with same base filename but extension"${s{$N>1}}" '$*'."
4214    fi
4215     repeat $! l[$>]
4216      repeat $N ext=${arg{1+$>}} if ext=lowercase(['$ext']);ext=='jpg'||ext=='jpeg' ext.=,85 fi o {0,f}{0,b}.$ext done
4217    endl done
4218  else
4219    if $is_last_arg e[0--4] "Output image"$_gmic_s" with same base filename but extension"${s{$N>1}}"' ${^-1}'."
4220    else e[0--4] "Output image"$_gmic_s" with same base filename but extension"${s{$N>1}}" '$*'."
4221    fi
4222    repeat $! l[$>]
4223      repeat $N ext=${arg{1+$>}} if ext=lowercase(['$ext']);ext=='jpg'||ext=='jpeg' ext.=,85 fi o {0,b}.$ext done
4224    endl done
4225  fi
4226
4227#@cli parse_cli : _output_mode,_{ * | command_name }
4228#@cli : Parse definition of '#@cli'-documented commands and output info about them in specified output mode.
4229#@cli : 'output_mode' can be { ascii | bashcompletion | html | images | print }.
4230#@cli : Default values: 'output_mode=print' and 'command_name=*'.
4231parse_cli : skip "${1=print},${2=*}"
4232  e[^-1] "Parse '#@cli' command(s) '$2' and output in '$1' mode."
4233
4234  # Check that specified output mode is actually implemented.
4235  l[] ({'$$parse_cli_$1'}) rm
4236  onfail error[0--2] "Command 'parse_cli': Invalid output mode '$1'."
4237  endl
4238
4239  if !$! l[] it ${_path_rc}update$_version.gmic onfail endl fi
4240  if !$! return fi
4241  i[0] ('\n') y a y
4242  merge_multiline_comments
4243
4244  # Split commands/section into different blocs.
4245  eval "
4246    for (p = 0, p<h,
4247
4248      # Find next occurrence of line starting with '#@cli'.
4249      q = find(#0,'\n#@cli',p);
4250      q<0?(copy(i[p],0,h - p,1,0); break());
4251      for (r = q + 6, r<h && (c=i[r])<=_' ' && c!=_'\n', ++r);
4252      r>=h || i[r]=='\n'?(copy(i[p],0,r - p,1,0); p = r; continue());
4253
4254      (i[r]==_':' && i[r + 1]==_':')?( # Category
4255        r = find(#0,_'\n',r)%h;
4256
4257      ):i[r]!=_':'?do( # Command
4258        r = find(#0,_'\n',r)%h;
4259        crop(0,r,1,6)=='\n#@cli'?(
4260          for (s = r + 6, s<h && (c=i[s])<=_' ' && c!='_\n', ++s);
4261          s<h && i[s]==':' && i[s + 1]!=':'?(r = s):break();
4262        ):break(),1
4263
4264      ):( # Unexpected '#@cli' line -> Display warning
4265        r = find(#0,_'\n',r)%h;
4266        ref(vector1024(),line);
4267        copy(line,i[q + 1],min(size(line),r - q - 1));
4268        run('warn[0--3] \"Unexpected line: \'',line,'\'.\"');
4269        copy(i[p],0,r - p,1,0); p = r; continue()
4270      );
4271      copy(i[p],0,q - p + 1,1,0); # Set values to discard
4272      p = r)"
4273  s -,0
4274
4275  # Keep only command blocs and name them.
4276  if $!
4277    $!,1,1,1,">
4278      begin(
4279        ref(vector1024(),command);
4280        ref(vector1024(),category);
4281      );
4282      for (p = 5, p<h#x && (c=i[#x,p])<=_' ' && c!=_'\n', ++p);
4283      p>=h#x || i[#x,p]==_'\n'?(run('nm[',x,'] __to_discard__'); break());
4284
4285      i[#x,p]==_':' && i[#x,p + 1]==_':'?( # Category
4286        for (p+=2, i[#x,p]<=_' ', ++p);
4287        for (q = h#x - 1, i[#x,q]<=_' ', --q);
4288        copy(category,i[#x,p],l = min(q - p + 1,size(category) - 1));
4289        category[l] = 0;
4290        run('nm[',x,'] __to_discard__');
4291      ):( # Command
4292        for (q = p, q<h#x && (c=i[#x,q])!=_':' && c>_' ', ++q);
4293        copy(command,i[#x,p],l = min(q - p,size(command) - 1));
4294        command[l] = 0;
4295        run('nm[',x,'] \"',string(command,'@',category),'\"');
4296      )"
4297    rm. rmn __to_discard__
4298    l parse_cli_trigger_$1 $2 onfail endl
4299  fi
4300  parse_cli_$1
4301
4302# Return 1 if last of the selected bloc describes a shortcut command, 0 otherwise.
4303parse_cli_is_eqto :
4304  nbl={"n = 1; for (p = 0, (q = find(#-1,_'\n',p))>=0, ++n, p = ++q); n"}
4305  u {$nbl"==1 && find(#-1,'eq. to \'')>=0"}
4306
4307#
4308# Implements 'bashcompletion' mode for command 'parse_cli'.
4309#
4310parse_cli_bashcompletion :
4311  v 0 use_vt100
4312  sort_list +,n
4313  +e[] "#"\n\
4314"#  Bash completion rules for 'gmic'."\n\
4315"#"\n\
4316"# This file has been generated automatically."\n\
4317"# Do not edit!"\n\
4318"#"\n\
4319"# This file should be copied/renamed in '/usr/share/bash-completion/completions/gmic'."\n\
4320"#"\n\n\
4321"_gmic()"\n\
4322"{"\n\
4323"    local cur prev opts coms"\n\
4324"    if type -t _init_completion >/dev/null; then"\n\
4325"        _init_completion -n = || return"\n\
4326"    else"\n\
4327"        COMPREPLY=()"\n\
4328"        cur=\"${COMP_WORDS[COMP_CWORD]}\""\n\
4329"        prev=\"${COMP_WORDS[COMP_CWORD-1]}\""\n\
4330"    fi"
4331  coms="    coms=\"" c=
4332
4333  # Extract list of available commands.
4334  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4335    ('{n}') l. s -,{'@'} name={0,t} rm endl nm $name
4336    coms.=$c$name c=" "
4337  endl done
4338  +e[] $coms"\""\n\
4339"    opts=$(echo \"$coms\" | sed \"s: \\([^ ]\\+\\): \\1 -\\1 \\+\\1:g\")"\n
4340
4341  # Duplicate list of arguments for command shortcuts.
4342  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi
4343    name={$>,n}
4344    if ${parse_cli_is_eqto[$>]}
4345      +l[$>] s -,{'"eq. to "'} k. s -,39 eqto={0,t} rm endl
4346      l[$>] pass[$eqto] 0 k. nm $name
4347      onfail
4348        warn[] "Warning: Ignoring shortcut '"$_vt100_c$name$_vt100_m$_vt100_b"', "\
4349               "links to unknown command '"$_vt100_c$eqto$_vt100_m$_vt100_b"'."
4350        nm __to_discard__
4351      endl
4352    fi
4353  done
4354  rmn __to_discard__
4355
4356  # Extract list of arguments.
4357  +e[] "    case \"${prev}\" in"
4358  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4359    name={n}
4360    if n=['$name'];"n[0]!=_'_' && find(n,'input') && find(n,'output') && "\
4361                   "n!='i' && n!='o' && n!='m' && n!='it' && n!='ot'"
4362      if s=['$name'];s=='help'||s=='h'
4363        +e[] "        \""$name"\" | \"-"$name"\" | \"+"$name"\")"
4364        +e[] "            COMPREPLY=( $(compgen -W \"$coms\" -- \"$cur\") ); return 0;;"
4365      else
4366        s -,{'\n'} k[0] discard {'#@cli'} max {'" "'} autocrop {'" "'}
4367        s -,{':'} autocrop {'" "'} rm[0] replace {'" "'},{'_'}
4368        n_args=0 args,c= repeat $! if ['{/{$>,t}}']!='(+)' args.=$c{$>,t} c=" " n_args+=1 fi done
4369        if $n_args==1 args="> "$args fi
4370        if ['$args']!=0
4371          +e[] "        \""$name"\" | \"-"$name"\" | \"+"$name"\")"
4372          +e[] "            COMPREPLY=( $(compgen -W \""{/{/$args}}"\") ); return 0;;"
4373        fi
4374      fi
4375      rm 0
4376    fi
4377  endl done
4378
4379  +e[] "    esac"\n\n\
4380"    COMPREPLY=( $(compgen -W \"$opts\" -- \"$cur\") )"\n\
4381"    if type -t _filedir >/dev/null; then"\n\
4382"        _filedir"\n\
4383"    else"\n\
4384"        comptopt -o filenames 2>/dev/null"\n\
4385"        COMPREPLY=( $(compgen -f -- ${cur}) )"\n\
4386"    fi"\n\
4387"}"\n\
4388"complete -F _gmic -o filenames gmic"
4389  rm
4390
4391parse_cli_trigger_bashcompletion :
4392  parse_cli_trigger_print $*
4393
4394#
4395# Implements 'ascii' mode for command 'parse_cli'.
4396#
4397parse_cli_ascii :
4398  use_vt100
4399  if !narg($_shell_cols) _shell_cols={${-shell_cols}-5} fi
4400  category= n_category=0
4401  if !narg($_section) _section=1 fi
4402
4403  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4404
4405    # Detect and display new category.
4406    if !0$_no_categories
4407      ('{n}') l. s -,{'@'} if $!>1 cat={t} fi rm endl
4408      if ['$cat']!=['$category']
4409        n_category+=1
4410        category=$cat
4411        ('$_section.$n_category." "') ('$category') +f.. {'" "'} +f.. {'-'} a[-4,-3] x a[-2,-1] x
4412        +e[] \n"  "$_vt100_r$_vt100_b{-2,t}$_vt100_n
4413        +e[] "  "$_vt100_r{t}$_vt100_n
4414        rm[-2,-1]
4415      fi
4416    fi
4417
4418    # Parse command declaration.
4419    s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'}
4420    +l[0]
4421      s -,{'": "'} autocrop {'" "'}
4422      name={0,t} rm[0]
4423      is_builtin=0
4424      if $!
4425        is_builtin={"find(#-1,'(+)')>=0"}
4426        if $is_builtin rm. fi
4427      fi
4428      +e[] "\n  "$_vt100_m$_vt100_b$name${"s1,s0=\" (+)\", u $s"$is_builtin}:$_vt100_n
4429      repeat $! l[$>]
4430        if ${parse_cli_is_eqto.}
4431          s -,39 eqto={1,t} k[0]
4432          +e[] "      "${_vt100_i}"Shortcut for command '"$_vt100_m$_vt100_b$eqto$_vt100_n"'."
4433        else
4434          eqto=
4435          l. _gmd2ascii_cut $_shell_cols,8 if $!>1 i[1--2] ('"\\\\\n       "':y) a y fi endl
4436          str="      "{t}${"if "$<" u \" |\" else u \"\" fi"}
4437          +e[] ${_vt100_c}$str$_vt100_n
4438        fi
4439      endl done
4440      rm
4441    endl
4442    rm[0]
4443
4444    # Parse command description.
4445    n_example=0 nl="\n"
4446    repeat $! if {$>,i==_':'} l[$>]
4447      if "h==1 || (h==2 && i[-1,2]==_' ')" rm ('":  "':y) fi # Empty description lines
4448      rows {1+(i[1]==_'" "')},100%
4449      if "find(#0,'(eq. to ')>=0" # Found 'Eq. to' description.
4450        +l s -,{'\47'} shortcut={1,t} rm onfail shortcut="(unknown)" rm endl
4451        +e[] $nl"    ("${_vt100_i}"equivalent to shortcut command '"$_vt100_m$_vt100_b$shortcut$_vt100_n"')." nl="\n"
4452      elif "find(#0,'Default value:')>=0 || find(#0,'Default values:')>=0" # Found 'Default value(s):' description
4453        if !0$_no_default_values
4454          s +,{':'}
4455          if $!>2" && "h#1==1" && "i("#1")==_':'
4456            i[0] ('$_vt100_b':y) i[2] ('$_vt100_n':y)
4457          fi
4458          a y gmd2ascii $_shell_cols,5
4459          if $!>1 i[1--2] ('"\n      "':y) a y fi autocrop {'\n'}
4460          +e[] "\n    "{/{t}} nl=
4461        fi
4462      elif i==_'$'" && "i[1]!=_'$' # Found example description
4463        if !0$_no_examples
4464          rows 1,100% autocrop {'" "'}
4465          _gmd2ascii_cut {$_shell_cols-8},7
4466          if $!>1 i[1--2] ('"\\\\\n      "':y) a y fi autocrop {'\n'}
4467          example={/{t}}
4468          n_example+=1
4469          if $n_example==1 example_str=${_vt100_b}"Example:"$_vt100_n"\n      "[#$n_example] nl="\n"
4470          else example_str="  "[#$n_example]
4471          fi
4472          +e[] $nl"    "$example_str" "$_vt100_c$example$_vt100_n nl=
4473        fi
4474      elif i==_'$'" && "i[1]==_'$' # Found link to tutorial page
4475        if !0$_no_tutorial_link
4476          if h==2
4477            url=https://gmic.eu/tutorial/$name
4478          else
4479            rows 2,100 autocrop {'" "'} url={t}
4480          fi
4481          +e[] "\n    "${_vt100_b}"Tutorial: "$_vt100_n$_vt100_u$url$_vt100_n nl=
4482        fi
4483      else # Other kind of description line
4484        parse_cli_text_ascii.
4485        if w +e[] $nl{/{t}} nl= fi
4486      fi
4487
4488    endl fi done
4489    rm 0
4490  endl done rm u $eqto
4491
4492parse_cli_text_ascii :
4493  if !narg($_shell_cols) _shell_cols={${-shell_cols}-5} fi
4494  replace_str "\\n","\n" gmd2ascii $_shell_cols,0
4495
4496  # Ensure output text contains no more than two consecutive newlines.
4497  # Also add a 4-chars left margin on each line.
4498  if w
4499    s +,{'\n'}
4500    if {i==_'\n'} rm. fi # Remove last newline.
4501    eval "repeat (l,p,
4502            i(#p)==_'\n'?(
4503              h(#p)>2?resize(#p,1,1,1,1,0)
4504            ):(
4505              resize(#p,1,h#p + 4,1,1,0,0,0,1);
4506              copy(i[#p,0],_' ',4,1,0))
4507            )"
4508    a y
4509  fi
4510
4511parse_cli_trigger_ascii :
4512  if "['$1']!='*'"
4513    1,$!,1,1,"
4514      begin(ref([lowercase(['$1']),_'@'],str));
4515      ref(lowercase(name(#y)),nm);
4516      !find(nm,str)?y:-1"
4517    discard. -1 if h k[{i}] else rm fi
4518  fi
4519
4520#
4521# Implements 'html' mode for command 'parse_cli'.
4522#
4523parse_cli_html :
4524  v 0 use_vt100 e[] ""
4525
4526  # Sort by categories.
4527#  repeat $! ('{$>,n}') l. s +,{'@'} rv a y nm={t} endl rm. nm[$>] $nm done # Rename as 'category@name'
4528#  sort_list +,n # Sort by categories
4529#  repeat $! ('{$>,n}') l. s +,{'@'} rv a y nm={t} endl rm. nm[$>] $nm done # Rename as 'name@category'
4530
4531  # Generate html page 'List of Commands'.
4532  html="<!DOCTYPE html>"\n\
4533"<html lang=\"en\">"\n\
4534"  <head>"\n\
4535"    <meta charset=\"utf-8\">"\n\
4536"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
4537"    <link rel=\"stylesheet\" href=\"../highslide/highslide.css\"/>"\n\
4538"    <link rel=\"stylesheet\" href=\"https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css\">"\n\n\
4539"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
4540"- Reference Documentation</title>"\n\
4541"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
4542"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
4543"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
4544"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
4545"  </head>"\n\n\
4546"  <body>"\n\
4547"    <div id=\"include_header\"></div>"\n\n\
4548"    <div class=\"section_title\"><a href=\"index.html\"><p>Reference</p></a></div><div class=\"section_content\">"\n\n\
4549"    <a name=\"top\"></a>"\n\n\
4550"<!-- begin_content -->"\n\n\
4551"<!-- list_of_categories -->"\n
4552
4553  list_categories,c=
4554  category,p_category= n,is_tr,is_table,row=0
4555  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4556    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4557    if ['$category']!=['$p_category']
4558      strvar $category category_id=${}
4559      if $is_tr
4560        if $n%5" && "$row>1 html.="        <td colspan=\""{5-$n%5}"\"></td>"\n fi
4561        html.="      </tr>"\n is_tr=0
4562      fi
4563      if $is_table html.="    </table><br/>"\n is_table=0 fi
4564      html.="\n    <h1 class=\"ref_h1\"><a name=\""$category_id"\"></a>"$category":</h1>"\n\
4565            "    <table class=\"ref_table_category\">"\n
4566
4567      ('$category') replace_str. ",","&#44;" lcategory={t} rm.
4568      list_categories.=$c$lcategory c=,
4569      is_table=1
4570      n,row=0
4571    fi
4572    if !${parse_cli_is_eqto.}
4573      is_builtin={"find(#-1,'(+)')>=0"}
4574      if !($n%5)
4575        if $is_tr html.="      </tr>"\n fi
4576        html.="      <tr>"\n
4577        is_tr=1 row+=1
4578      fi
4579      if ['$name']=='index' url_name=_index.html else url_name=$name.html fi
4580      if $is_builtin
4581        html.="        <td><a href=\""$url_name"#top\"><span class=\"gmd_monospace\" >"$name"</span></a></td>"\n
4582      else
4583        html.="        <td><a href=\""$url_name"#top\">"$name"</a></td>"\n
4584      fi
4585      n+=1
4586    fi
4587    p_category=$category
4588  endl done
4589  if $is_tr
4590    if $n%5" && "$row>1 html.="        <td colspan=\""{5-$n%5}"\"></td>"\n fi
4591    html.="      </tr>"\n
4592  fi
4593  if $is_table html.="    </table>"\n fi
4594
4595  # Add 'Shortcuts' category.
4596  html.="\n    <h1 class=\"ref_h1\"><a name=\"shortcuts\"></a>Command Shortcuts:</h1>"\n\
4597        "    <table class=\"ref_table_shortcuts\">"\n\
4598        "      <tr style=\"background-color: \#d8dcea;\"><td><b>Shortcut name</b></td><td>"\
4599        "<b>Equivalent command name</b></td></tr>"\n
4600
4601  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4602    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4603    if ${parse_cli_is_eqto.}
4604      is_builtin={"find(#-1,'(+)')>=0"}
4605      +l s -,{'"eq. to \47"'} k. s -,{'\47'} k[0] autocrop {'" "'} eqto={t} rm endl
4606      ('$name') replace_str. ">","&gt;" replace_str. "<","&lt;" html_name={t} rm.
4607      html.="      <tr><td><a href=\""$eqto.html"#top\"><b>"$html_name"</b></a></td>"
4608      if $is_builtin
4609        html.="<td><a href=\""$eqto.html"#top\"><span class=\"gmd_monospace\">"$eqto"</span></a></td>"
4610      else
4611        html.="<td><a href=\""$eqto.html"#top\">"$eqto"</a></td>"
4612      fi
4613      html.="</tr>"\n
4614      n+=1
4615    fi
4616  endl done
4617  if $is_table html.="    </table>"\n fi
4618
4619  html.=\n\
4620"<!-- end_content -->"\n\n\
4621"<!-- ref_navigation_bottom -->"\n\
4622"    </div><div class=\"section_end\"></div>"\n\
4623"    <div id=\"include_footer\"></div>"\n\
4624"  </body>"
4625  ({'$html'}:y)
4626
4627  # Insert TOC for categories.
4628  toc_html="    <h1 class=\"ref_h1\">Categories:</h1><ul>"\n
4629  repeat narg({/$list_categories})
4630    arg 1+$>,{/$list_categories} category=${}
4631    ('$category') replace_str. "&#44;","," category={t} rm.
4632    strvar $category category_id=${}
4633    toc_html.="      <li><a href=\"#"$category_id"\">"$category"</a></li>"\n
4634  done
4635  toc_html.="      <li><a href=\"#shortcuts\">Command Shortcuts</a></li>"\n
4636  toc_html.="    </ul>"
4637
4638  replace_str. "<!-- list_of_categories -->",$toc_html
4639  ot. list_of_commands.html
4640  if !isfile('index.html') x "ln -fs list_of_commands.html index.html" fi
4641  rm.
4642
4643  # Retrieve full list of commands and discard those starting with '_'.
4644  repeat $! if s=['{$<,n}'];s[0]==_'_' rm[$<] else l[$<]
4645    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4646    is_eqto=${parse_cli_is_eqto.}
4647    if !$is_eqto _is_$name=1 else rm fi
4648  endl fi done
4649
4650  # Generate html page for each command.
4651  repeat $! l[$>]
4652    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4653    e[] "\r  > "$_vt100_c[#{1+$>}]$_vt100_n" "{`copy(vector48(_'" "'),['$category" / "$name'])`}
4654
4655    # Find previous and next commands.
4656    if !$> previous= else pass[{$>-1}] 1 ('{n}') discard. {'_c1'} previous={t} rm[-2,-1] fi
4657    if !$< next= else pass[{$>+1}] 1 ('{n}') l. s -,{'@'} next={0,t} rm endl rm. fi
4658    if ['$name']=='index' url_name=_index.html else url_name=$name.html fi
4659    if ['$previous']=='index' url_previous=_index.html else url_previous=$previous.html fi
4660    if ['$next']=='index' url_next=_index.html else url_next=$next.html fi
4661
4662    strvar[] $category category_id=${}
4663
4664    html="<!DOCTYPE html>"\n\
4665"<html lang=\"en\">"\n\
4666"  <head>"\n\
4667"    <meta charset=\"utf-8\">"\n\
4668"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
4669"    <link rel=\"stylesheet\" href=\"../highslide/highslide.css\"/>"\n\
4670"    <link rel=\"stylesheet\" href=\"https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css\">"\n\n\
4671"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
4672"- Reference Documentation - "$name"</title>"\n\
4673"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
4674"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
4675"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
4676"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
4677"    <script src=\"../highslide/highslide-full.js\"></script>"\n\
4678"    <script>"\n\
4679"      hs.graphicsDir = '../highslide/graphics/';"\n\
4680"      hs.wrapperClassName = 'wide-border';"\n\
4681"      hs.showCredits = 'false';"\n\
4682"    </script>"\n\
4683"  </head>"\n\n\
4684"  <body>"\n\
4685"    <div id=\"include_header\"></div>"\n\n\
4686"    <div class=\"section_title\"><a href=\"index.html\"><p>Reference</p></a></div>"\
4687"<div class=\"section_content\" style=\"padding-top: 0;\">"\n\n\
4688"      <a name=\"top\"></a>"\n\n\
4689"<!-- begin_content -->"\n\n
4690
4691    if !0$_pdf_output
4692      html.="      <table class=\"ref_navigation_top\"><tr><td>"\
4693"<a href=\"index.html\">Table of Contents</a>&nbsp;&nbsp;&#9656;&nbsp;&nbsp;"\
4694"<a href=\"list_of_commands.html#top\">List of Commands</a>&nbsp;&nbsp;&#9656;&nbsp;&nbsp;"\
4695"<a href=\"list_of_commands.html#"$category_id"\">"$category"</a>&nbsp;&nbsp;&#9656;&nbsp;&nbsp;"\
4696"<a href=\""$url_name"#top\"><samp>"$name"</samp></a></td>"
4697      if narg($previous)" || "narg($next)
4698        html.="<td>"
4699        if narg($previous) html.="<a href=\""$url_previous"#top\">&#9664;&nbsp;&nbsp;<samp>"$previous"</samp></a>" fi
4700        if narg($previous)" && "narg($next) html.="&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;" fi
4701        if narg($next) html.="<a href=\""$url_next"#top\"><samp>"$next"</samp>&nbsp;&nbsp;&#9654;</a>" fi
4702        html.="</td>"\n
4703      fi
4704      html.="      </tr></table>\n"
4705
4706    else
4707      html.="<img style=\"margin-top: 2em; width: 100%;\" src=\"reference_pdf.png\"/>"\n
4708    fi
4709
4710    # Parse command declaration.
4711    s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'}
4712    +l[0]
4713      s -,{'": "'} autocrop {'" "'} rm[0]
4714      is_builtin=0
4715      if $!" && find(#-1,'(+)')>=0" is_builtin=1 rm. fi
4716      if $is_builtin
4717        html.="      <table class=\"ref_h1_builtin\"><tr><td><h1 class=\"ref_h1\" style=\"margin-bottom: 0;\">"\
4718              $name"</h1></td><td><span class=\"ref_builtin_command\">Built-in command</span></td></tr></table>"\n
4719      else
4720        html.="      <h1 class=\"ref_h1\">"$name"</h1>"\n
4721      fi
4722      if $!
4723        html.="\n      <h2>Arguments:</h2>\n      <ul>"\n
4724        if $!>1 or="&nbsp;&nbsp;&nbsp; or" else or= fi
4725        repeat $! l[$>]
4726          if !$< or= fi
4727          html.="        <li><span class=\"gmd_monospace\">"{t}"</span>"$or"</li>"\n
4728        endl done
4729        html.="      </ul>"\n
4730      else
4731        html.="\n      <h3>No arguments</h2>"\n
4732      fi
4733      rm
4734    endl
4735    rm[0]
4736
4737    # Parse command description.
4738    html.="\n      <h2>Description:</h2>"\n
4739    line=0
4740    n_example=0
4741    nb_examples=0
4742    is_tutorial_tag=0
4743    tutorial_html=
4744
4745    repeat $! if {$>,i==_':'} l[$>]
4746      if "h==1 || (h==2 && i[-1,2]==_' ')" rm ('":  "':y) fi # Empty description lines
4747      rows {1+(i[1]==_'" "')},100%
4748      if i==_'$'" && "i[1]!=_'$' nb_examples+=1 fi
4749    endl fi done
4750
4751    repeat $! l[$>]
4752      if "find(#0,'(eq. to ')>=0"
4753
4754        # Found 'Eq. to' description.
4755        +l s -,{'\47'}
4756          replace_str[1] "&","&amp;"
4757          replace_str[1] "<","&lt;"
4758          replace_str[1] ">","&gt;"
4759          shortcut={1,t}
4760          rm
4761        onfail
4762          shortcut="(unknown)"
4763          rm
4764        endl
4765        html.="      <p>(<em>equivalent to shortcut command</em> <span class=\"gmd_monospace\">"$shortcut\
4766              "</span>).</p>"\n
4767
4768      elif "find(#0,'See also:')>=0"
4769
4770        # Found 'See also:' description.
4771        s +,{':'} autocrop {'" "'}
4772        if $!>2" && "h#1==1" && "i("#1")==_':'
4773          html.="\n      <h2>See also:</h2>"\n
4774          rm[0,1]
4775        fi
4776        a y
4777        gmd2html 0
4778        html.="      "{t}\n
4779
4780      elif "find(#0,'Default value:')>=0 || find(#0,'Default values:')>=0"
4781
4782        # Found 'Default value(s):' description
4783        s +,{':'} autocrop {'" "'}
4784        if $!>2" && "h#1==1" && "i("#1")==_':'
4785          html.="\n<h2>Default values:</h2>"\n
4786          rm[0,1]
4787        fi
4788        a y
4789        gmd2html 0
4790        html.="<p style=\"word-wrap:break-word\">"{t}"</p>"\n
4791
4792      elif i==_'$'" && "i[1]!=_'$'
4793
4794        # Found 'Example of use' description.
4795        rows 1,100% autocrop {'" "'}
4796        if !0$_pdf_output
4797          replace_str "image.jpg","<a href=\"image.jpg\" class=\"highslide\" onclick=\"return hs.expand(this)\">"\
4798                      "image.jpg</a>"
4799        fi
4800        example={/{t}}
4801        n_example+=1
4802        if $n_example>1 basename=${name}_$n_example.jpg else basename=${name}.jpg fi
4803        if $n_example==1
4804           if !$is_tutorial_tag html.="<tutorial_tag>" is_tutorial_tag=1 fi
4805           html.="\n      <h2>Example"${"if "$nb_examples">1 u s else u \"\" fi"}" of use:</h2>"\n fi
4806        if $nb_examples!=1 html.="      <h3>&bull;&nbsp;Example \#"$n_example"</h3>"\n fi
4807        html.="      <div class=\"gmd_code_block\">$ gmic "$example"</div><br/>"\n
4808
4809        if 0$_pdf_output html.="<div style=\"text-align: center;\">"\n fi
4810        if isfile('img/f_$basename') # Single output image
4811          if !0$_pdf_output
4812            html.="      <span><a href=\"img/"f_$basename"\" class=\"highslide\" onclick=\"return hs.expand(this)\">"\
4813                  "<img class=\"center_image\" src=\"img/"t_$basename"\"/></a>"\
4814                  "<div class=\"highslide-caption\">Command: <b>"$example"</b></div></span>"\n
4815          else
4816            html.="<img style=\"max-width:45%\" src=\"img/"t_$basename"\"/>"\n
4817          fi
4818        elif isfile('img/f0_$basename') # Multiple output images
4819          i=0
4820          html.="        <div class=\"center\">"
4821          for isfile('img/f${i}_$basename')
4822            if !0$_pdf_output
4823              html.="        <span><a href=\"img/"f${i}_$basename"\" class=\"highslide\" "\
4824                    "onclick=\"return hs.expand(this)\">"\
4825                    "<img src=\"img/"t${i}_$basename"\"/></a>"\
4826                    "<div class=\"highslide-caption\">Command: <b>"$example"</b></div></span>"\n
4827            else
4828              html.="<img style=\"max-width:45%\" src=\"img/"t${i}_$basename"\"/>"\n
4829            fi
4830            i+=1
4831          done
4832          html.="        </div>"
4833        fi
4834        if 0$_pdf_output html.="</div>"\n fi
4835
4836      elif i==_'$'" && "i[1]==_'$'
4837
4838        # Found link to tutorial page.
4839        if !$is_tutorial_tag html.="<tutorial_tag>" is_tutorial_tag=1 fi
4840        if h==2
4841          url=https://gmic.eu/tutorial/$name
4842          l[] it $url if "find(#-1,'404 Not Found')>=0" url=https://gmic.eu/oldtutorial/_$name fi rm endl
4843        else
4844          rows 2,100 autocrop {'" "'} url={t}
4845        fi
4846
4847        tutorial_html.="      <p>This command has a <span class=\"ref_tutorial_button\"><a href=\""$url"\">"\
4848                       "tutorial page</a></span>.</p>"
4849      else
4850
4851        # Other kind of description line
4852        +autocrop {"' '"} is_list={isin(i,_'*',_'-',_'.')} rm.
4853        ('\n') a y
4854        replace_str "\\n","\n" gmd2html 0
4855        if $line==1" && "!$is_list html.="      <br/>"\n fi
4856        html.={t}
4857        if i[-1,2]!=_'\n' html.="\n" fi
4858        line+=1
4859      fi
4860    endl done
4861
4862    # Generate page.
4863    html.=\n\
4864"<!-- end_content -->"\n\n\
4865"<!-- ref_navigation_bottom -->"\n\
4866"    </div><div class=\"section_end\"></div>"\n\
4867"    <div id=\"include_footer\"></div>"\n\
4868"  </body>"
4869    rm ({'$html'}:y)
4870    replace_str. "<tutorial_tag>",$tutorial_html # Insert tutorial button
4871    nm $name ot $url_name
4872  endl done
4873  rm
4874  e[] "\r  > "${_vt100_g}{`copy(vector64(_'" "'),'"Parsing done!"')`}$_vt100_n
4875
4876parse_cli_trigger_html :
4877  parse_cli_trigger_print $*
4878
4879#
4880# Implements 'image' mode for command 'parse_cli'.
4881#
4882parse_cli_images :
4883  v 0 use_vt100 e[] ""
4884  old_category,category= n_category,n_example=0
4885  if !isfile('image.jpg') l[] sp bottles,640 onfail testimage2d 500 endl o. image.jpg rm. fi
4886  n_global=0
4887  repeat $! if s=['{$>,n}'];s[0]==_'_' continue fi l[$>]
4888    ('{n}') l. s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm endl
4889
4890    if ['$category']!=['$old_category']
4891      n_category+=1
4892      e[] $_vt100_r"\n ** Section \#"$n_category": "$category"."$_vt100_n"\n"
4893    fi
4894    s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'}
4895    n_example=0
4896
4897    repeat $! l[$>]
4898      if "i==_':' && (
4899            for (p = 1, p<h && i[p]<=_' ', ++p);
4900            i[p]==_'$' && i[p+1]!=_'$')"
4901
4902        s +,{'$'} rm[0,1] a y autocrop {'" "'} example={/{t}}
4903        n_example+=1
4904        n_global+=1
4905        if $n_example>1
4906          basename=${name}_$n_example.jpg
4907        else
4908          basename=${name}.jpg
4909        fi
4910        e[] $_vt100_c[$n_global]$_vt100_n" Command '"$_vt100_g$name$_vt100_n"': $ "$example
4911        skip_rendering={"['"$name"'][0]==_'_' || "\
4912                        "(isfile(['"{/${_parse_cli_images_path}f_$basename}"']) &&"\
4913                        " isfile(['"{/${_parse_cli_images_path}t_$basename}"'])) || "\
4914                        "(isfile(['"{/${_parse_cli_images_path}f0_$basename}"']) && "\
4915                        "isfile(['"{/${_parse_cli_images_path}t0_$basename}"']))"}
4916
4917        if !$skip_rendering # Generate picture only if the file doesn't yet exist
4918          m "run_example : "$example
4919          l[]
4920            reset etime=$|
4921            run_example
4922            etime="done in "$_vt100_n{_round($|-$etime,0.01)}"s"$_vt100_m
4923            _parse_cli_images
4924            if $!
4925              if $!>1 repeat $! o[$>] ${_parse_cli_images_path}f$>_$basename,85 done
4926              else o ${_parse_cli_images_path}f_$basename,85 fi
4927              rr2d 480,320,0,2
4928              if $!>1 repeat $! o[$>] ${_parse_cli_images_path}t$>_$basename,75 done
4929              else o ${_parse_cli_images_path}t_$basename,75 fi
4930              rm
4931            fi
4932          onfail
4933            rm gui_error_preview "Unable to generate preview image"
4934            o ${_parse_cli_images_path}f_$basename,85
4935            rr2d 480,320,0,2
4936            o ${_parse_cli_images_path}t_$basename,75
4937            rm
4938            etime="failed"
4939          endl
4940          um run_example
4941        else etime="skipped"
4942        fi
4943        e[] "\r"$_vt100_c[$n_global]$_vt100_n" Command '"$_vt100_g$name$_vt100_n"': $ "\
4944            $example" "${_vt100_m}"("$etime")."$_vt100_n
4945      fi
4946    endl done
4947    old_category=$category
4948    rm 0
4949  endl done rm
4950
4951# Generate a single image from a list of images, for the reference documentation.
4952_parse_cli_images :
4953  if !$! rm return fi
4954  W,H={[640,480]}
4955
4956  repeat $! l[$>] nm={n}
4957    label=[$>]:" "'$nm'
4958    if ${-is_3d} # 3D vector object
4959      label2="("{i[6]}" vert., "{i[7]}" prim.)"
4960      r3d 1,1,0,-80 r3d 0,1,0,80 snapshot3d {max($W,$H)}
4961    else # Regular 1D,2D or 3D image
4962      label2="("{w}x{h}x{d}x{s}")"
4963      r 100%,100%,100%,3,{s==1?1:0}
4964      if d>1 # Volumetric image -> Render as a 2D image
4965        +slices 50%
4966        +z[0] 50%,0,0,50%,100%,100% permute. zyxc
4967        +z[0] 0,50%,0,100%,50%,100% permute. xzyc
4968        rr2d... $W,$H,0,2
4969        r2dy.. {-3,h},1
4970        r2dx. {-3,w},1
4971        s={min(w#-2,h)}
4972        if $s>64 projections3d[0] 50%,50%,50%,1 mv[0] $! r3d. 1,1,0,-80 r3d. 0,1,0,80 snapshot3d. $s
4973        else rm[0] 0
4974        fi
4975
4976        eW,eH={[w#0+w#1,h#0+h#2]}
4977        if $eW>$W" || "$eH>$H fact={min($W/$eW,$H/$eH)*100} r $fact%,$fact%,1,100%,2 fi
4978        n 0,255
4979
4980        fs={0,max(w,h)*7%}
4981        if {0,h>1.5*$fs} to[0] XY,4,2,$fs fi
4982        if {1,h>1.5*$fs} to[1] XZ,4,2,$fs fi
4983        if {2,h>1.5*$fs} to[2] YZ,4,2,$fs fi
4984        if w to[3] 3D,4,2,$fs frame[3] 1,1,200 fi
4985        frame[0-2] 1,1,200
4986        a[0,1] x a[1,2] x a y
4987      fi
4988    fi
4989
4990    if w>5*h r $W,{$H/3},1,100%,1
4991    elif h>5*w r {$W/3},$H,1,100%,1
4992    else rr2d $W,$H,0,{w<$W&&h<$H?1:2}
4993    fi
4994    n 0,255
4995
4996    r {[w,h]+2},1,100%,0,0,0.5,0.5
4997    - 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
4998
4999    0 t. $label" "$label2,5,0,32,1,1
5000    if w>w#0" || "h>h#0
5001       rm. 0 t. $label\n$label2,5,0,32,1,1
5002       if w>w#0" || "h>h#0 rr2d. {0,[w,h]},0,2 fi
5003    fi
5004    *. -1 n. 0,255 to_rgb.
5005    - 245 a y,0.5 + 245
5006
5007  endl done
5008  c 0,255
5009
5010parse_cli_trigger_images :
5011  parse_cli_trigger_print $*
5012
5013#
5014# Implements 'list' mode for command 'parse_cli'.
5015# (return a list of all commands, separated by commas).
5016#
5017parse_cli_list :
5018  res=
5019  repeat $! ('{$>,n}') l. s -,{'@'} res.=$c{0,t} c=, rm endl done
5020  rm u $res
5021
5022#
5023# Implements 'print' mode for command 'parse_cli'.
5024#
5025parse_cli_print :
5026  v 0 e[] ""
5027  sort_list +,n
5028  repeat $! l[$>]
5029    ('{n}') l. s -,{'@'} name={0,t} rm endl
5030    +e[] $name
5031  endl done rm
5032
5033parse_cli_trigger_print :
5034  if "['$1']!='*'"
5035    1,$!,1,1,"
5036      begin(ref(lowercase(['$1']),str));
5037      ref(lowercase(name(#y)),nm);
5038      arobace = find(nm,_'@');
5039      inrange(find(nm,str),0,arobace - 1)?y:-1"
5040    discard. -1 k[{^}]
5041  fi
5042
5043#@cli parse_gmd
5044#@cli : Parse and tokenize selected images, viewed as text strings formatted with the G'MIC markdown syntax.
5045parse_gmd :
5046  e[^-1] "Parse and tokenize images$? viewed as text strings formatted with the G\47MIC markdown syntax."
5047  y 1 a[^-1] .,y rm.
5048  eval[^] ">"${-_gmd_tokens}"
5049    begin(
5050      section = subsection = subsubsection = subsubsubsection = anchor =
5051      bullet = subbullet = subsubbullet = center = right = table = blockquote = detail_block = code_block =
5052      shell = bold_italic_a = bold_italic_u = bold_a = bold_u = italic_a = italic_u = strikethrough = underline =
5053      monospace = value_set = word_highlight = url = page_link = text_link1 = text_link2 = img1 = img2 = pipeline =
5054      formula = opening_offset = -1;
5055
5056      blank(c) = isin(c,_' ',_'\n',s_whitespace,s_tab,0);
5057      semiblank(c) = isin(c,_' ',_'\n',_'.',_',',_';',_':',_'!',_'?',
5058                            _')',_'(',_'[',_']',_'|',_'-',s_whitespace,s_tab,0);
5059      newline(c) = isin(c,_'\n',0);
5060      reset(c) = (#c = -1);
5061      res = crop();
5062    );
5063
5064    pc = j[-1]; c = i; nc = j[1]; ac = j[2];
5065    ym1 = y - 1; y1 = y + 1; y2 = y + 2;
5066    linestart = newline(pc);
5067
5068    is_raw = max(blockquote,code_block,shell,monospace,url,page_link,text_link2,img2,pipeline,formula)>=0;
5069    non_escaped = is_raw || (pc!=_'\\');
5070
5071    # Manage document layout.
5072    #------------------------
5073
5074    # Escaped character.
5075    !non_escaped &&
5076    isin(c,_'%',_'$',_'\\',_'\'',_'`',_'*',_'_',_'{',_'}',_'[',_']',_'<',_'>',_'(',_')',_'#',_'+',_'-',_'.',_'!')?(
5077      res[ym1] = i[ym1] = 0;
5078    ):
5079
5080    # Section: '# Section name'.
5081    linestart && c==_'#' && nc==_' '?( # Opening
5082      section = y;
5083    ):
5084    section>=0 && newline(c)?( # Closing
5085      opening_offset = section;
5086      res[section++] = s_section;
5087      while (blank(res[section]), res[section++] = 0); # Remove leading whitespaces
5088      res[y] = e_section;
5089      for (p = opening_offset - 1, p>=0 && res[p]==_'\n', res[p--] = 0); # Remove newlines preceding
5090      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
5091      for (p = y1, p<size(res) && blank(res[p]), res[p++] = 0); # Remove newlines following
5092    ):
5093
5094    # Subsection: '## Subsection name'.
5095    linestart && c==_'#' && nc==c && ac==_' '?( # Opening
5096      subsection = y;
5097    ):
5098    subsection>=0 && newline(c)?( # Closing
5099      opening_offset = subsection;
5100      res[subsection++] = s_subsection; res[subsection++] = 0;
5101      while (blank(res[subsection]), res[subsection++] = 0); # Remove leading whitespaces
5102      res[y] = e_subsection;
5103      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
5104      for (p = y1, p<size(res) && res[p] && blank(res[p]), res[p] = 0); # Remove newlines following
5105    ):
5106
5107    # Subsubsection: '### Subsubsection name'.
5108    linestart && c==_'#' && nc==c && ac==c && j[3]==_' '?( # Opening
5109      subsubsection = y;
5110    ):
5111    subsubsection>=0 && newline(c)?( # Closing
5112      opening_offset = subsubsection;
5113      res[subsubsection++] = s_subsubsection; copy(res[subsubsection],0,2,1,0); subsubsection+=2;
5114      while (blank(res[subsubsection]), res[subsubsection++] = 0); # Remove leading whitespaces
5115      res[y] = e_subsubsection;
5116      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
5117      for (p = y1, p<size(res) && res[p] && blank(res[p]), res[p] = 0); # Remove newlines following
5118    ):
5119
5120    # Subsubsubsection: '#### Subsubsubsection name'.
5121    linestart && c==_'#' && nc==c && ac==c && j[3]==c && j[4]==_' '?( # Opening
5122      subsubsubsection = y;
5123    ):
5124    subsubsubsection>=0 && newline(c)?( # Closing
5125      opening_offset = subsubsubsection;
5126      res[subsubsubsection++] = s_subsubsubsection; copy(res[subsubsubsection],0,3,1,0); subsubsubsection+=3;
5127      while (blank(res[subsubsubsection]), res[subsubsubsection++] = 0); # Remove leading whitespaces
5128      res[y] = e_subsubsubsection;
5129      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
5130      for (p = y1, p<size(res) && res[p] && blank(res[p]), res[p] = 0); # Remove blanks following
5131    ):
5132
5133    # Anchor: '= Anchor name'.
5134    linestart && c==_'=' && nc==_' '?( # Opening
5135      anchor = y;
5136    ):
5137    anchor>=0 && newline(c)?( # Closing
5138      opening_offset = anchor;
5139      res[anchor++] = s_anchor;
5140      while (blank(res[anchor]), res[anchor++] = 0); # Remove leading whitespaces
5141      res[y] = e_anchor;
5142      res[y1]==_'\n'?(res[y1] = 0);
5143      for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks
5144    ):
5145
5146    # List bullet: '* List item'.
5147    linestart && isin(c,_'-',_'*',_'+') && nc==_' ' && bullet<0?( # Opening
5148      bullet = y;
5149    ):
5150    bullet>=0 && newline(c)?( # Closing
5151      opening_offset = bullet;
5152      res[bullet++] = s_bullet; res[bullet] = 0;
5153      res[y] = e_bullet;
5154    ):
5155
5156    # Sublist bullet: '  * Sub-list item'.
5157    linestart && c==_' ' && nc==_' ' && isin(ac,_'-',_'*',_'+') && j[3]==_' ' && subbullet<0?( # Opening
5158      subbullet = y;
5159    ):
5160    subbullet>=0 && newline(c)?( # Closing
5161      opening_offset = subbullet;
5162      res[subbullet++] = s_subbullet; copy(res[subbullet],0,2,1,0);
5163      res[y] = e_subbullet;
5164    ):
5165
5166    # Subsublist bullet: '    * Subsub-list item'.
5167    linestart && c==_' ' && nc==_' ' && ac==_' ' && j[3]==_' ' && isin(j[4],_'-',_'*',_'+') && j[5]==_' ' &&
5168      subsubbullet<0?( # Opening
5169      subsubbullet = y;
5170    ):
5171    subsubbullet>=0 && newline(c)?( # Closing
5172      opening_offset = subsubbullet;
5173      res[subsubbullet++] = s_subsubbullet; copy(res[subsubbullet],0,4,1,0);
5174      res[y] = e_subsubbullet;
5175    ):
5176
5177    # Centered block: '\n===\n Centered text\n===\n'.
5178    linestart && c==_'=' && nc==c && ac==c && newline(j[3])?(
5179      center<0?( # Opening
5180        center = y;
5181      ):
5182      center>=0 && y>center + 3?( # Closing
5183        opening_offset = center;
5184        res[center++] = s_center; copy(res[center],0,3,1,0); center+=3;
5185        newline(res[ym1])?(res[ym1] = 0);
5186        res[y] = e_center; copy(res[y + 1],0,3,1,0);
5187      );
5188    ):
5189
5190    # Right block: '\n>>>\n Right-aligned text\n>>>\n'.
5191    linestart && c==_'>' && nc==c && ac==c && newline(j[3])?(
5192      right<0?( # Opening
5193        right = y;
5194      ):
5195      right>=0 && y>right + 3?( # Closing
5196        opening_offset = right;
5197        res[right++] = s_right; copy(res[right],0,3,1,0); right+=3;
5198        newline(res[ym1])?(res[ym1] = 0);
5199        res[y] = e_right; copy(res[y + 1],0,3,1,0);
5200      );
5201    ):
5202
5203    # Table : '\n| foo | bar\n' (with first delimiter '||', '|+' or '|-').
5204    table<0 && linestart && c==_'|'?( # Opening
5205      table = y;
5206    ):
5207    table>=0 && newline(c) && nc!=_'|'?( # Closing
5208      opening_offset = table;
5209      d = i[table + 1];
5210      d==_'|'?(res[table++] = s_htable; res[table++] = 0):
5211      d==_'-'?(res[table++] = s_vtable; res[table++] = 0):
5212      d==_'+'?(res[table++] = s_hvtable; res[table++] = 0):(res[table++] = s_table);
5213
5214      i[table]==_' '?(res[table++] = 0);
5215      newline(i[table]) && i[table + 1]==_'|'?(copy(res[table],0,2,1,0); table+=2);
5216      for (p = table, p<y, ++p, # Find column / line separators inside table
5217        i[p]==_'|'?(
5218          res[p] = newline(i[p - 1]) || newline(i[p + 1])?0:m_table;
5219          i[p - 1]==_' '?(res[p - 1] = 0);
5220          i[p + 1]==_' '?(res[p + 1] = 0);
5221        ):newline(i[p])?(
5222          res[p] = n_table;
5223        );
5224      );
5225      res[y] = e_table;
5226    ):
5227
5228    # Blockquote : '\n> line1\n> line2'.
5229    code_block<0 && blockquote<0 && linestart && c==_'>' && blank(nc)?( # Opening
5230      blockquote = y;
5231    ):
5232    blockquote>=0 && newline(c) && (nc!=_'>' || !blank(ac))?( # Closing
5233      opening_offset = blockquote;
5234      res[blockquote++] = s_blockquote; res[blockquote] = 0;
5235      # Remove leading '>' when multi-lines:
5236      for (p = blockquote, p<y, ++p, newline(res[p - 1]) && res[p]==_'>'?copy(res[p],0,res[p + 1]==_' '?2:1,1,0));
5237      res[y] = e_blockquote;
5238    ):
5239
5240    # Detail block: '\n??? Details:\nDetail block\n???\n'.
5241    linestart && c==_'?' && nc==c && ac==c && blank(j[3])?(
5242      detail_block<0?( # Opening
5243        detail_block = y;
5244      ):
5245      detail_block>=0 && y>detail_block + 3 && newline(j[3])?( # Closing
5246        opening_offset = detail_block;
5247        res[detail_block++] = s_detail_block; copy(res[detail_block],0,2,1,0);
5248        res[ym1] = e_detail_block; copy(res[y],0,4,1,0);
5249      );
5250    ):
5251
5252    # Code block: '\n~~~\n Code block\n~~~\n'.
5253    linestart && isin(c,_'~','`') && nc==c && ac==c && newline(j[3])?(
5254      code_block<0?( # Opening
5255        code_block = y;
5256      ):
5257      code_block>=0 && y>code_block + 3?( # Closing
5258        opening_offset = code_block;
5259        res[code_block++] = s_code_block; copy(res[code_block],0,3,1,0); code_block+=3;
5260        copy(res[code_block],i[code_block],y - code_block); # Cancel tokens found inside
5261        for (p = code_block, p<y, ++p, res[p]==_' '?(res[p] = s_whitespace)); # Keep leading whitespaces.
5262        res[ym1] = e_code_block; copy(res[y],0,4,1,0);
5263      );
5264    ):
5265
5266    # Shell command: '\n"""\n Shell command\n"""\n'.
5267    linestart && c==_'\"' && nc==c && ac==c && newline(j[3])?(
5268      shell<0?( # Opening
5269        shell = y;
5270      ):
5271      shell>=0 && y>shell + 3?( # Closing
5272        opening_offset = shell;
5273        res[shell++] = s_shell; copy(res[shell],0,3,1,0); shell+=3;
5274        copy(res[shell],i[shell],y - shell); # Cancel tokens found inside
5275        res[ym1] = e_shell; copy(res[y],0,4,1,0);
5276      );
5277    ):
5278
5279    # Horizontal rule: '---', '___' or '***'.
5280    linestart && isin(c,_'-','_','*') && nc==c && ac==c && newline(j[3])?(
5281      res[y] = s_hrule;
5282      copy(res[y1],0,3,1,0);
5283    ):
5284
5285    # Manage text style.
5286    #-------------------
5287
5288    # Bold italic (asterisk): '***bold italic text***'.
5289    non_escaped && c==_'*' && nc==c && ac==c?( # Opening
5290      bold_italic_a<0?(
5291        bold_italic_a = y;
5292      ):
5293      bold_italic_a>=0 && y>bold_italic_a + 3 && j[3]!=_'*'?( # Closing
5294        opening_offset = bold_italic_a;
5295        res[bold_italic_a++] = s_bold_italic_a; res[bold_italic_a++] = 0; res[bold_italic_a] = 0;
5296        res[y] = e_bold_italic_a; res[y1] = res[y2] = 0;
5297      );
5298    ):
5299
5300    # Bold italic (underscore): '___bold italic text___'.
5301    non_escaped && c==_'_' && nc==c && ac==c?(
5302      bold_italic_u<0 && semiblank(pc)?( # Opening
5303        bold_italic_u = y;
5304      ):
5305      bold_italic_u>=0 && y>bold_italic_u + 3 && semiblank(j[3])?( # Closing
5306        opening_offset = bold_italic_u;
5307        res[bold_italic_u++] = s_bold_italic_u; res[bold_italic_u++] = 0; res[bold_italic_u] = 0;
5308        res[y] = e_bold_italic_u; res[y1] = res[y2] = 0;
5309      );
5310    ):
5311
5312    # Bold (asterisk): '**bold text**'.
5313    non_escaped && c==_'*' && nc==c?( # Opening
5314      bold_a<0?(
5315        bold_a = y;
5316      ):
5317      bold_a>=0 && y>bold_a + 2 && ac!=_'*'?( # Closing
5318        opening_offset = bold_a;
5319        res[bold_a++] = s_bold_a; res[bold_a] = 0;
5320        res[y] = e_bold_a; res[y1] = 0;
5321      );
5322    ):
5323
5324    # Bold (underscore): '__bold text__'.
5325    non_escaped && c==_'_' && nc==c?(
5326      bold_u<0 && semiblank(pc)?( # Opening
5327        bold_u = y;
5328      ):
5329      bold_u>=0 && y>bold_u + 2 && semiblank(ac)?( # Closing
5330        opening_offset = bold_u;
5331        res[bold_u++] = s_bold_u; res[bold_u] = 0;
5332        res[y] = e_bold_u; res[y1] = 0;
5333      );
5334    ):
5335
5336    # Italic (asterisk): '*italic text*'.
5337    non_escaped && c==_'*' && pc!=c?( # Opening
5338      italic_a<0?(
5339        italic_a = y;
5340      ):
5341      italic_a>=0 && y>italic_a + 1 && ac!=_'*'?( # Closing
5342        opening_offset = italic_a;
5343        res[italic_a] = s_italic_a;
5344        res[y] = e_italic_a;
5345      );
5346    ):
5347
5348    # Italic (underscore): '_italic text_'.
5349    non_escaped && c==_'_' && pc!=c?(
5350      italic_u<0 && semiblank(pc)?( # Opening
5351        italic_u = y
5352      ):
5353      italic_u>=0 && y>italic_u + 1 && semiblank(nc)?( # Closing
5354        opening_offset = italic_u;
5355        res[italic_u] = s_italic_u;
5356        res[y] = e_italic_u;
5357      );
5358    ):
5359
5360    # Strikethrough: '~~strikethrough text~~'.
5361    non_escaped && c==_'~' && nc==c && pc!=c?( # Opening
5362      strikethrough<0?(
5363        strikethrough = y;
5364      ):
5365      strikethrough>=0 && y>strikethrough + 2?( # Closing
5366        opening_offset = strikethrough;
5367        res[strikethrough++] = s_strikethrough; res[strikethrough] = 0;
5368        res[y] = e_strikethrough; res[y1] = 0;
5369      );
5370    ):
5371
5372    # Underline: '==strikethrough text=='.
5373    non_escaped && c==_'=' && nc==c && pc!=c?( # Opening
5374      underline<0?(
5375        underline = y;
5376      ):
5377      underline>=0 && y>underline + 2?( # Closing
5378        opening_offset = underline;
5379        res[underline++] = s_underline; res[underline] = 0;
5380        res[y] = e_underline; res[y1] = 0;
5381      );
5382    ):
5383
5384    # Monospace: '`monospace text`'.
5385    non_escaped && c==_'`'?(
5386      monospace<0?( # Opening
5387        monospace = y;
5388      ):
5389      monospace>=0 && y>monospace + 1?( # Closing
5390        opening_offset = monospace;
5391        res[monospace++] = s_monospace;
5392        copy(res[monospace],i[monospace],y - monospace); # Cancel tokens found inside
5393        for (p = monospace, p<y, ++p, res[p]==_' '?(res[p] = s_whitespace)); # Keep leading whitespaces.
5394        res[y] = e_monospace;
5395      );
5396    ):
5397
5398    # Value Set: '{ value1 | value2 | value3 }'.
5399    non_escaped && c==_'{' && value_set<0 && semiblank(pc)?( # Opening
5400      value_set = y;
5401    ):
5402    non_escaped && c==_'}' && value_set>=0 && y>value_set + 1 && semiblank(nc)?( # Closing
5403      opening_offset = value_set;
5404      res[value_set++] = s_value_set;
5405      copy(res[value_set],i[value_set],y - value_set); # Cancel tokens found inside
5406      res[y] = e_value_set;
5407    ):
5408
5409    # Word highlight: ' 'word_highlight' '.
5410    non_escaped && c=='\'' && nc!=c?(
5411      word_highlight<0 && semiblank(pc)?( # Opening
5412        word_highlight = y;
5413      ):
5414      word_highlight>=0 && y>word_highlight + 1 && semiblank(nc)?( # Closing
5415        opening_offset = word_highlight;
5416        res[word_highlight++] = s_word_highlight;
5417        copy(res[word_highlight],i[word_highlight],y - word_highlight); # Cancel tokens found inside
5418        res[y] = e_word_highlight;
5419      );
5420    ):
5421
5422    # URL: '<https://url.com>'.
5423    non_escaped && c==_'<' && url<0 &&
5424     (crop(0,y1,1,8)=='https://' || crop(0,y1,1,7)=='http://' || crop(0,y1,1,6)=='ftp://')?( # Opening
5425      url = y;
5426    ):
5427    non_escaped && c==_'>' && url>=0 && y>url + 1?( # Closing
5428      opening_offset = url;
5429      res[url++] = s_url;
5430      copy(res[url],i[url],y - url); # Cancel tokens found inside
5431      res[y] = e_url;
5432    ):
5433
5434    # Page link (command or reference): ' ''command or page name'' ' (not a URL!).
5435    non_escaped && c==_'\'' && nc==c?(
5436      page_link<0 && semiblank(pc)?( # Opening
5437        page_link = y;
5438      ):
5439      page_link>=0 && y>page_link + 2 && semiblank(ac)?( # Closing
5440        opening_offset = page_link;
5441        res[page_link++] = s_page_link; res[page_link++] = 0;
5442        copy(res[page_link],i[page_link],y - page_link); # Cancel tokens found inside
5443        res[y] = e_page_link; res[y1] = 0;
5444      );
5445    ):
5446
5447    # Text link: '[link text](https://url.com)'.
5448    non_escaped && c==_'[' && text_link1<0 && text_link2<0 && img1<0?( # Opening
5449      text_link1 = y;
5450    ):
5451    non_escaped && c==_']' && nc==_'(' && text_link1>=0 && text_link2<0 && img1<0?( # Middle
5452      text_link2 = y;
5453    ):
5454    non_escaped && c==_')' && text_link2>=0 && img1<0?( # Closing
5455      opening_offset = text_link1;
5456      res[text_link1] = s_text_link;
5457      res[text_link2++] = m_text_link; res[text_link2++] = 0;
5458      copy(res[text_link2],i[text_link2],y - text_link2); # Cancel tokens found inside
5459      res[y] = e_text_link;
5460    ):
5461
5462    # Image: '![alt caption](https://url.com/image.png)'.
5463    non_escaped && c==_'!' && nc==_'[' && img1<0 && img2<0?( # Opening
5464      img1 = y;
5465    ):
5466    non_escaped && c==_']' && nc==_'(' && img1>=0 && img2<0?( # Middle
5467      img2 = y;
5468    ):
5469    non_escaped && c==_')' && img2>=0?( # Closing
5470      opening_offset = img1;
5471      res[img1++] = s_img; res[img1++] = 0;
5472      res[img2++] = m_img; res[img2++] = 0;
5473      copy(res[img1],i[img1],img2 - img1 - 2); copy(res[img2],i[img2],y - img2); # Cancel tokens found inside
5474      res[y] = e_img;
5475    ):
5476
5477    # G'MIC pipeline (image): '%% G'MIC pipeline %%'.
5478    non_escaped && c==_'%' && nc==c?(
5479      pipeline<0 && semiblank(pc)?( # Opening
5480        pipeline = y;
5481      ):
5482      pipeline>=0 && y>pipeline + 2 && semiblank(ac)?( # Closing
5483        opening_offset = pipeline;
5484        res[pipeline++] = s_pipeline; res[pipeline++] = 0;
5485        copy(res[pipeline],i[pipeline],y - pipeline); # Cancel tokens found inside
5486        while (blank(i[pipeline]), res[pipeline++] = 0); # Remove leading blanks
5487        for (p = ym1, blank(res[p]), res[p--] = 0); # Remove trailing blanks
5488        res[y] = e_pipeline; res[y1] = 0;
5489      );
5490    ):
5491
5492    # Formula in LaTeX: '$$ x = cos(x + 2) $$`'.
5493    non_escaped && c==_'$' && nc==c?(
5494      formula<0 && semiblank(pc)?( # Opening
5495        formula = y;
5496      ):
5497      formula>=0 && y>formula + 2 && semiblank(ac)?( # Closing
5498        opening_offset = formula;
5499        res[formula++] = s_formula; res[formula++] = 0;
5500        copy(res[formula],i[formula],y - formula); # Cancel tokens found inside
5501        while (blank(i[formula]), res[formula++] = 0); # Remove leading blanks
5502        for (p = ym1, blank(res[p]), res[p--] = 0); # Remove trailing blanks
5503        res[y] = e_formula; res[y1] = 0;
5504      );
5505    ):
5506
5507    # G'MIC word: '\G'MIC'.
5508    semiblank(pc) && c==_'\\' && nc==_'G' && ac==_'\'' && j[3]==_'M' && j[4]==_'I' && j[5]==_'C' && semiblank(j[6])?(
5509      res[y] = s_gmic;
5510      copy(res[y1],0,4,1,0);
5511      res[y + 5] = e_gmic;
5512    ):
5513
5514    # Whitespaces.
5515    c==_' '?(
5516
5517      # Keep consecutive whitespaces.
5518      c==_' ' && (linestart || pc==c)?(
5519        res[y] = s_whitespace;
5520      );
5521
5522      # Reset tokens that cannot contain whitespace.
5523      reset(word_highlight);
5524      reset(url);
5525      text_link2>=0?(reset(text_link1); reset(text_link2));
5526      img2>=0?(reset(img1); reset(img2));
5527    ):
5528
5529    # Escaped newline.
5530    c==_'\\' && nc==_'n'?(
5531      res[y] = _'\n'; res[y + 1] = 0;
5532    ):
5533
5534    # Tab.
5535    c==_'\t'?(
5536      res[y] = s_tab;
5537    ):
5538
5539    # Newline.
5540    c==_'\n'?(
5541
5542      # Reset tokens that cannot contain newlines.
5543      reset(bold_italic_a);
5544      reset(bold_italic_u);
5545      reset(bold_u);
5546      reset(bold_a);
5547      reset(italic_a);
5548      reset(italic_u);
5549      reset(strikethrough);
5550      reset(underline);
5551      reset(monospace);
5552      reset(word_highlight);
5553      reset(url);
5554      reset(page_link);
5555      text_link2>=0?(reset(text_link1); reset(text_link2));
5556      img2>=0?(reset(img1); reset(img2));
5557    );
5558
5559    # Cancel opened tokens starting after the opening offset of the latest closed token.
5560    #------------------------------------------------------------------------------------
5561    opening_offset>=0?(
5562      section>=opening_offset ? reset(section);
5563      subsection>=opening_offset ? reset(subsection);
5564      subsubsection>=opening_offset ? reset(subsubsection);
5565      subsubsubsection>=opening_offset ? reset(subsubsubsection);
5566      anchor>=opening_offset ? reset(anchor);
5567      bullet>=opening_offset ? reset(bullet);
5568      subbullet>=opening_offset ? reset(subbullet);
5569      subsubbullet>=opening_offset ? reset(subsubbullet);
5570      center>=opening_offset ? reset(center);
5571      right>=opening_offset ? reset(right);
5572      table>=opening_offset ? reset(table);
5573      blockquote>=opening_offset ? reset(blockquote);
5574      detail_block>=opening_offset ? reset(detail_block);
5575      code_block>=opening_offset? reset(code_block);
5576      shell>=opening_offset? reset(shell);
5577      bold_italic_a>=opening_offset ? reset(bold_italic_a);
5578      bold_italic_u>=opening_offset ? reset(bold_italic_u);
5579      bold_a>=opening_offset ? reset(bold_a);
5580      bold_u>=opening_offset ? reset(bold_u);
5581      italic_a>=opening_offset ? reset(italic_a);
5582      italic_u>=opening_offset ? reset(italic_u);
5583      strikethrough>=opening_offset ? reset(strikethrough);
5584      underline>=opening_offset ? reset(underline);
5585      monospace>=opening_offset ? reset(monospace);
5586      value_set>=opening_offset ? reset(value_set);
5587      word_highlight>=opening_offset ? reset(word_highlight);
5588      url>=opening_offset ? reset(url);
5589      page_link>=opening_offset ? reset(page_link);
5590      text_link1>=opening_offset || text_link2>=opening_offset ? (reset(text_link1); reset(text_link2));
5591      img1>=opening_offset || img2>=opening_offset ? (reset(img1); reset(img2));
5592      pipeline>=opening_offset ? reset(pipeline);
5593      formula>=opening_offset ? reset(formula);
5594      opening_offset = -1;
5595    );
5596
5597    end(copy(i[0],res))"
5598  discard 0
5599
5600#@cli gmd2html : _include_default_header_footer={ 0=none | 1=Reference | 2=Tutorial | 3=News } : (no arg)
5601#@cli : Convert selected gmd-formatted text images to html format.
5602#@cli : Default values: 'include_default_header_footer=1'.
5603gmd2html : skip "${1=}"
5604  l[] is_arg={isint("$1")} onfail is_arg=0 endl
5605  if $is_arg embed_html=$1 else embed_html=1 noarg fi
5606
5607  parse_gmd
5608  s_section,e_section={${-_gmd_tokens}"[s_section,e_section]"}
5609  repeat $! l[$>] nm$>={b} strvar ${nm$>} fnm=${}
5610    if "i=="$s_section" && find(#-1,"$e_section")>0"
5611      +rows 1,{"find(#-1,"$e_section")-1"} title$>={t} rm.
5612    else title$>=
5613    fi
5614    . # [0] = Output, [1] = Input
5615    eval. ">
5616      begin("${-_gmd_tokens}${-_gmd_write}");
5617      c = i;
5618      c>0?(
5619        c==_'\n' ? write('<br/>\n'):
5620        c==_'&' ? write('&amp;'):
5621        c==_'\47' ? write('&apos;'):
5622        c==_'>' ? write('&gt;'):
5623        c==_'\"' ? write('&quot;'):
5624        c==_'<' ? write('&lt;'):
5625        write(c);
5626      ):(
5627        isin(c,e_bold_a,e_bold_u,e_bold_italic_a,e_bold_italic_u,e_italic_a,e_italic_u,
5628             e_monospace,e_strikethrough,e_underline,e_word_highlight) ? write('</span>'):
5629        isin(c,e_bullet,e_subbullet,e_subsubbullet) ? write('</div>\n'):
5630        isin(c,e_section,e_subsection,e_subsubsection,e_subsubsubsection) ? write('</div>\n'):
5631
5632        c==s_section ? (
5633          ind_e = find(#1,e_section,y);
5634          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5635          ref(get('_gmd_name',1024,1),str_nam);
5636          write_nl();
5637          write('<a name=\"');
5638          len = find(str_nam,0);
5639          write(str_nam,len);
5640          write('\"></a><div class=\"gmd_section\">');
5641        ):
5642        c==s_subsection ? (
5643          ind_e = find(#1,e_subsection,y);
5644          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5645          ref(get('_gmd_name',1024,1),str_nam);
5646          write_nl();
5647          write('<a name=\"');
5648          len = find(str_nam,0);
5649          write(str_nam,len);
5650          write('\"></a><div class=\"gmd_subsection\">');
5651        ):
5652        c==s_subsubsection ? (
5653          ind_e = find(#1,e_subsubsection,y);
5654          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5655          ref(get('_gmd_name',1024,1),str_nam);
5656          write_nl();
5657          write('<a name=\"');
5658          len = find(str_nam,0);
5659          write(str_nam,len);
5660          write('\"></a><div class=\"gmd_subsubsection\">');
5661        ):
5662        c==s_subsubsubsection ? (
5663          ind_e = find(#1,e_subsubsubsection,y);
5664          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5665          ref(get('_gmd_name',1024,1),str_nam);
5666          write_nl();
5667          write('<a name=\"');
5668          len = find(str_nam,0);
5669          write(str_nam,len);
5670          write('\"></a><div class=\"gmd_subsubsubsection\">');
5671        ):
5672        c==s_anchor ? (
5673          ind_e = find(#1,e_anchor,y);
5674          run('_gmd2html_section. ',y + 1,',',ind_e - 1);
5675          ref(get('_gmd_name',1024,1),str_nam);
5676          write('<a name=\"');
5677          len = find(str_nam,0);
5678          write(str_nam,len);
5679          write('\"></a>\n');
5680          copy(i[y],0,ind_e - y + 1,1,0);
5681        ):
5682        c==s_bullet ? write('<div class=\"gmd_bullet\">'):
5683        c==s_subbullet ? write('<div class=\"gmd_subbullet\">'):
5684        c==s_subsubbullet ? write('<div class=\"gmd_subsubbullet\">'):
5685        c==s_center ? write('<div class=\"gmd_center\">\n'):
5686        c==e_center ? (write_nl(); write('</div>')):
5687        c==s_right ? write('<div class=\"gmd_right\">\n'):
5688        c==e_right ? (write_nl(); write('</div>')):
5689        c==s_table ? (write_nl(); write('<table class=\"gmd_table\">\n<tr><td>')):
5690        c==s_htable ? (write_nl(); write('<table class=\"gmd_htable\">\n<tr><td>')):
5691        c==s_vtable ? (write_nl(); write('<table class=\"gmd_vtable\">\n<tr><td>')):
5692        c==s_hvtable ? (write_nl(); write('<table class=\"gmd_hvtable\">\n<tr><td>')):
5693        c==m_table ? write('</td><td>'):
5694        c==n_table ? write('</td></tr>\n<tr><td>'):
5695        c==e_table ? write('</td></tr>\n</table>\n'):
5696        c==s_blockquote ? (write_nl(); write('<blockquote class=\"gmd_blockquote\">\n')):
5697        c==e_blockquote ? (write('\n</blockquote>\n')):
5698        c==s_detail_block ? (
5699          write_nl();
5700          write('<details class=\"gmd_details\">\n<summary>');
5701          ny = y + 1;
5702          nc = j[1];
5703          nc!=e_detail_block?(
5704            j[1] = 0;
5705            ind_e = find(#1,e_detail_block,ny);
5706            nc==_' '?( # Has a title
5707              ind_nl = find(#1,_'\n',ny);
5708              ind_nl<0 || ind_nl>ind_e?(ind_nl = ind_e);
5709              write(#1,y + 2,ind_nl - y - 2);
5710              copy(i[ny],0,ind_nl - y - 1,1,0);
5711              i[ind_nl]!=e_detail_block?(i[ind_nl] = 0);
5712            ):write('Details:');
5713            write('</summary>\n');
5714          )
5715        ):
5716        c==e_detail_block ? (write_nl(); write('</details>\n')):
5717        c==s_code_block ? write('<div class=\"gmd_code_block\">'):
5718        c==e_code_block ? write('</div>\n');
5719
5720        c==s_shell ? (
5721          ind_e = find(#1,e_shell,y);
5722          run('_gmd2html_shell. ',y + 1,',',ind_e -  1);
5723          ref(get('_gmd_command',1024,1),str_com);
5724          write('<div class=\"gmd_code_block\">$ ');
5725          len = find(str_com,0);
5726          write(str_com,len);
5727          write('<br/><br/>\n');
5728          write(#-1,0,h(#-1));
5729          run('rm.');
5730          write('</div>\n');
5731          copy(i[y],0,ind_e - y + 1,1,0);
5732        ):
5733        c==s_hrule ? (write_nl(); write('<hr/>\n')):
5734
5735        c==s_bold_italic_a ? write('<span class=\"gmd_bold_italic_a\">'):
5736        c==s_bold_italic_u ? write('<span class=\"gmd_bold_italic_u\">'):
5737        c==s_bold_a ? write('<span class=\"gmd_bold_a\">'):
5738        c==s_bold_u ? write('<span class=\"gmd_bold_u\">'):
5739        c==s_italic_a ? write('<span class=\"gmd_italic_a\">'):
5740        c==s_italic_u ? write('<span class=\"gmd_italic_u\">'):
5741        c==s_strikethrough? write('<span class=\"gmd_strikethrough\">'):
5742        c==s_underline? write('<span class=\"gmd_underline\">'):
5743        c==s_monospace ? write('<span class=\"gmd_monospace\">'):
5744        c==s_value_set ? write('<span class=\"gmd_value_set\">{'):
5745        c==e_value_set ? write('}</span>'):
5746        c==s_word_highlight ? write('<span class=\"gmd_word_highlight\">'):
5747
5748        c==s_url ? (
5749          ind_e = find(#1,e_url,y);
5750          write('<a href=\"');
5751          write(#1,y + 1,ind_e - y - 1);
5752          write('\" target=\"_blank\">');
5753        ):
5754        c==e_url ? write('</a>'):
5755
5756        c==s_page_link ? (
5757          ind_e = find(#1,e_page_link,y);
5758          run('_gmd2html_page_link. ',y + 1,',',ind_e - 1);
5759          ref(get('_gmd_link',1024,1),str_link);
5760          ref(get('_gmd_text',1024,1),str_text);
5761          write('<span class=\"gmd_page_link\"><a href=\"');
5762          len = find(str_link,0);
5763          write(str_link,len);
5764          write('\">');
5765          len = find(str_text,0);
5766          write(str_text,len);
5767          write('</a></span>');
5768          copy(i[y],0,ind_e - y + 1,1,0);
5769        ):
5770
5771        c==s_text_link ? (
5772          ind_m = find(#1,m_text_link,y);
5773          ind_e = find(#1,e_text_link,ind_m);
5774          write('<a href=\"');
5775          write(#1,ind_m + 1,ind_e - ind_m - 1);
5776          i[ind_m + 1]==_'#' || crop(#1,0,ind_m + 1,1,15)=='https://gmic.eu'?
5777            write('\">'): # Local link
5778            write('\" target=\"_blank\">');
5779        ):
5780        c==m_text_link ? (
5781          ind_e = find(#1,e_text_link,y);
5782          copy(i[y],0,ind_e - y + 1,1,0);
5783          write('</a>');
5784        ):
5785
5786        c==s_img ? (
5787          ind_m = find(#1,m_img,y);
5788          ind_e = find(#1,e_img,ind_m);
5789          ind_d = find(#1,_'.',ind_e - 1,-1);
5790          is_video = 0;
5791          ind_d>ind_m?( # Check for a video
5792            ref(crop(#1,0,ind_d + 1,1,3),ext3);
5793            ref(crop(#1,0,ind_d + 1,1,4),ext4);
5794            is_video = ext3=='mp4' || ext3=='ogg' || ext4=='webm'
5795          );
5796          is_video?(
5797            write('<video controls loop autoplay muted src=\"');
5798            write(#1,ind_m + 1,ind_e - ind_m - 1);
5799            write('\">');
5800          ):(
5801            write('<img class=\"gmd_image\" src=\"');
5802            write(#1,ind_m + 1,ind_e - ind_m - 1);
5803            write('\" alt=\"');
5804            write(#1,y + 1,ind_m - y - 1);
5805            write('\" title=\"');
5806          );
5807        ):
5808        c==m_img ? (
5809          ind_e = find(#1,e_img,y);
5810          copy(i[y],0,ind_e - y + 1,1,0);
5811          is_video?write('</video>'):write('\"/>');
5812        ):
5813
5814        c==s_pipeline ? (
5815          ind_e = find(#1,e_pipeline,y);
5816          run('_gmd2html_pipeline. ',y + 1,',',ind_e -  1)?( # If output images
5817            ref(get('_gmd_command',1024,1),str_com);
5818            ref(get('_gmd_filename',1024,1),str_fil);
5819            write('<img class=\"gmd_image\" src=\"');
5820            len = find(str_fil,0);
5821            write(str_fil,len);
5822            write('\" alt=\"');
5823            len = find(str_com,0);
5824            write(str_com,len);
5825            write('\"/>');
5826          );
5827          copy(i[y],0,ind_e - y + 1,1,0);
5828        ):
5829
5830        c==s_formula ? (
5831          ind_e = find(#1,e_formula,y);
5832          run('_gmd2html_formula. ',y + 1,',',ind_e - 1);
5833          ref(get('_gmd_filename',1024,1),str_fil);
5834          write('<img class=\"gmd_formula\" src=\"');
5835          len = find(str_fil,0);
5836          write(str_fil,len);
5837          write('\" alt=\"');
5838        ):
5839        c==e_formula ? write('\"/>'):
5840
5841        c==s_gmic ? write('<span class=\"gmd_gmic\">G&apos;MIC</span>'):
5842        c==s_whitespace ? write('&nbsp;'):
5843        c==s_tab ? write('&nbsp;&nbsp;&nbsp;&nbsp;');
5844      );
5845      end(resize(#0,1,_write_off,1,1,0))"
5846    k[0] autocrop 0 nm $fnm.html
5847  endl done
5848
5849  # Embed with header and footer if needed.
5850  if $embed_html
5851    section_title=${"arg "$embed_html",Reference,Tutorial,News"}
5852    repeat $! l[$>] nm={b} nm0=${nm$>}
5853      if ['${title$>}']!=0 title=${title$>} else title=$nm0 fi
5854      if isfile('../style.css') style="../style.css" else style="https://gmic.eu/style.css" fi
5855      i[0] ('"<!DOCTYPE html>"\n\
5856"<html lang=\"en\">"\n\
5857"  <head>"\n\
5858"    <meta charset=\"utf-8\">"\n\
5859"    <link rel=\"stylesheet\" href=\""$style"\">"\n\
5860"    <title>"$title"</title>"\n\
5861"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
5862"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
5863"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
5864"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\
5865"  </head>"\n\n\
5866"  <body>"\n\
5867"    <div id=\"include_header\"></div>"\n\n\
5868"    <div class=\"section_title\"><p>"$section_title"</p></div><div class=\"section_content\">"\n\n\
5869"<!-- begin_content -->"\n\n':y)
5870    ('\n\n\
5871"<!-- end_content -->"\n\n\
5872"    </div><div class=\"section_end\"></div>"\n\n\
5873" <div id=\"include_footer\"></div>"\n\
5874"  </body>"':y)
5875      a y nm $nm.html
5876    endl done
5877  fi
5878
5879# Manage sections / subsections / subsubsections / subsubsubsections.
5880_gmd2html_section :
5881  +rows. $1,$2 strvar {t} _gmd_name=${} rm.
5882
5883# Manage page links.
5884_gmd2html_page_link :
5885  +rows. $1,$2
5886  if i==_'-' # Force link to a G'MIC reference page
5887    rows. 1,100% _gmd_text={t} strvar {t} varname=${}
5888    if ['$varname']=='index' varname=_index fi
5889    _gmd_link=https://gmic.eu/reference/$varname.html
5890  else
5891    basename {t} _gmd_text=${} strvar $_gmd_text varname=${}
5892    if ['$varname']=='index' varname=_index fi
5893    _gmd_link=$varname.html
5894  fi
5895  rm.
5896
5897# Manage pipeline execution and result saving.
5898_gmd2html_pipeline :
5899  +rows. $1,$2
5900  _gmd_command={t} rm.
5901  strcasevar $_gmd_command
5902  _gmd_filename=img/${}.png
5903  is_output={fsize(['$_gmd_filename'])}
5904  if !isfile(['$_gmd_filename']) l[]
5905    l[]
5906      run $_gmd_command a x
5907      if !$!" || "($!==1" && "!h) is_output=0 fi
5908    onfail
5909      rm error[] "Command '"$_gmd_command"': "${}
5910    endl
5911    if $!
5912      if !isdir('img') x "mkdir -p img" fi
5913      o. $_gmd_filename rm.
5914    fi
5915  endl fi
5916  ({'$_gmd_command'})
5917  _gmd_ascii2html.
5918  _gmd_command={t} rm.
5919  u $is_output
5920
5921# Convert formula to image (using LaTeX).
5922_gmd2html_formula :
5923  +rows. $1,$2 replace. {'\n'},{'" "'} j.. .,0,$1
5924  formula={/{t}} rm.
5925  strcasevar $formula varname=${}
5926  _gmd_filename=img/$varname.png
5927  if !isfile(['$_gmd_filename']) l[]
5928    ({'"\\documentclass[preview]{standalone}"\n\
5929       "\\usepackage{amsmath,amssymb,amsfonts}"\n\
5930       "\\pagestyle{empty}"\n\
5931       "\\begin{document}"\n\
5932       "\\begin{align*}"\n\
5933       $formula\n\
5934       "\\end{align*}"\n\
5935       "\\end{document}"'})
5936    path_tmp=${-path_tmp}
5937    ot. ${path_tmp}$varname.tex rm.
5938    x "cd "$path_tmp"; rm -f "$varname".pdf; pdflatex -interaction=nonstopmode "$varname".tex >/dev/null"
5939    if isfile(['${path_tmp}$varname.pdf'])
5940      i ${path_tmp}$varname.pdf,800
5941      autocrop. lt. 128 r2dy. 20% n. 0,255 channels. -3,0
5942      if !isdir('img') x "mkdir -p img" fi
5943      o. $_gmd_filename
5944    fi
5945    rm
5946  endl fi
5947
5948# Manage shell execution.
5949_gmd2html_shell :
5950  _gmd_shell. $*
5951  _gmd_ascii2html.
5952
5953_gmd2ascii_shell :
5954  _gmd_shell. $*
5955
5956_gmd_shell :
5957  +rows. $1,$2 _gmd_command={t} rm.
5958  path_tmp=${-path_tmp}
5959  strcasevar $_gmd_command
5960  filename=$path_tmp${}.log
5961  x $_gmd_command" > "$filename" 2>&1"
5962  if isfile(['$filename']) it $filename else 0 fi
5963  autocrop. {'\n'}
5964
5965# Convert an ascii buffer into html.
5966_gmd_ascii2html :
5967  repeat $! l[$>]
5968    . # [0] = Output, [1] = Input
5969    eval. ">
5970      begin("${-_gmd_write}${-_gmd_vt100}" spans = 0);
5971      c = i;
5972      c==_'&'?write('&amp;'):
5973      c==_'\"'?write('&quot;'):
5974      c==_'\''?write('&apos;'):
5975      c==_'<'?write('&lt;'):
5976      c==_'>'?write('&gt;'):
5977      c==_'\n'?write('<br/>\n'):
5978      c==_'\33' && j[1]==_'['?( # VT100 sequence
5979        ref(crop(0,y,1,8),C);
5980        same(C,vt100_b,size(vt100_b))?(
5981          write('<span class=\"gmd_bold_a\">');
5982          copy(i[y],0,size(vt100_b),1,0);
5983          ++spans
5984        ):same(C,vt100_c,size(vt100_c))?(
5985          write('<span style=\"color: darkcyan\">');
5986          copy(i[y],0,size(vt100_c),1,0);
5987          ++spans
5988        ):
5989        same(C,vt100_g,size(vt100_g))?(
5990          write('<span style=\"color: darkgreen\">');
5991          copy(i[y],0,size(vt100_g),1,0);
5992          ++spans
5993        ):
5994        same(C,vt100_i,size(vt100_i))?(
5995          write('<span class=\"gmd_italic_a\">');
5996          copy(i[y],0,size(vt100_i),1,0);
5997          ++spans
5998        ):
5999        same(C,vt100_m,size(vt100_m))?(
6000          write('<span style=\"color: darkmagenta\">');
6001          copy(i[y],0,size(vt100_m),1,0);
6002          ++spans
6003        ):
6004        same(C,vt100_n,size(vt100_n))?(
6005          repeat (spans,write('</span>'));
6006          copy(i[y],0,size(vt100_n),1,0);
6007          spans = 0
6008        ):
6009        same(C,vt100_r,size(vt100_r))?(
6010          write('<span style=\"color: darkred\">');
6011          copy(i[y],0,size(vt100_r),1,0);
6012          ++spans
6013        ):
6014        same(C,vt100_s,size(vt100_s))?(
6015          write('<span class=\"gmd_strikethrough\">');
6016          copy(i[y],0,size(vt100_s),1,0);
6017          ++spans
6018        ):
6019        same(C,vt100_u,size(vt100_u))?(
6020          write('<span class=\"gmd_underline\">');
6021          copy(i[y],0,size(vt100_u),1,0);
6022          ++spans
6023        ):
6024        write(c)
6025      ):write(c);
6026    "
6027    k[0] discard 0
6028  endl done
6029
6030#@cli gmd2ascii : _max_line_length>0,_indent_forced_newlines>=0 : (no arg)
6031#@cli : Convert selected gmd-formatted text images to ascii format.
6032#@cli : Default values: 'max_line_length=80' and 'indent_forced_newline=0'.
6033gmd2ascii :
6034  is_arg=1 l[] check "isint(${1=80}) && $1>0 && isint(${2=0}) && $2>=0" onfail is_arg=0 endl
6035  max_line_length,indent_forced_newline=${1-2}
6036  if !$is_arg
6037    if !narg($_shell_cols) _shell_cols={${-shell_cols}-5} fi
6038    max_line_length,indent_newline=$_shell_cols,0 noarg
6039  fi
6040
6041  use_vt100 parse_gmd
6042  repeat $! l[$>] nm={b} strvar $nm fnm=${}
6043    . # [0] = Output, [1] = Input
6044    eval. ">
6045      begin("${-_gmd_tokens}${-_gmd_write}${-_gmd_vt100}"
6046        is_blockquote = is_code_block = is_center = is_right = 0;
6047        ref(vector1024(),tmp);
6048        const nbcols = "${-shell_cols}";
6049        ref(vector(#nbcols,_' '),whiteline);
6050      );
6051      c = i;
6052
6053      c==_'\n'?(
6054        is_blockquote ? write([vt100_n,'\n  > ',vt100_m]):
6055        is_code_block ? write('\n  '):
6056        write(c);
6057        is_center ? write(whiteline,nbcols/4):
6058        is_right ? write(whiteline,nbcols/2);
6059      ):
6060      c>0?write(c):
6061      (
6062        isin(c,e_bold_a,e_bold_u,e_bold_italic_a,e_bold_italic_u,e_italic_a,e_italic_u,
6063             e_strikethrough,e_underline,e_url,e_page_link,e_formula) ? write(vt100_n):
6064        isin(c,e_bullet,e_subbullet,e_subsubbullet) ? write(_'\n'):
6065        isin(c,e_section,e_subsection,e_subsubsection,e_subsubsubsection) ? write([vt100_n,'\n\n']):
6066
6067        c==s_section ? (write_nl(); write([_'\n',vt100_r,vt100_b,'# '])):
6068        c==s_subsection ? (write_nl(); write([_'\n',vt100_r,vt100_b,'## '])):
6069        c==s_subsubsection ? (write_nl(); write([_'\n',vt100_r,vt100_b,'### '])):
6070        c==s_subsubsubsection ? (write_nl(); write([_'\n',vt100_r,vt100_b,'#### '])):
6071        c==s_anchor ? (
6072          ind_e = find(#1,e_anchor,y);
6073          copy(i[y],0,ind_e - y + 1,1,0);
6074        ):
6075        c==s_bullet ? write(' * '):
6076        c==s_subbullet ? write('   - '):
6077        c==s_subsubbullet ? write('     + '):
6078        c==s_center ? (write(whiteline,nbcols/4); is_center = 1):
6079        c==e_center ? (write(_'\n'); is_center = 0):
6080        c==s_right ? (write(whiteline,nbcols/2); is_right = 1):
6081        c==e_right ? (write(_'\n'); is_right = 0):
6082        c==s_tab ? write(whiteline,4):
6083        isin(c,s_table,s_htable,s_vtable,s_hvtable,n_table) ? (write_nl(); write([vt100_r,' | ',vt100_n])):
6084        c==m_table ? write([vt100_r,' | ',vt100_n]):
6085        c==s_blockquote ? (write([['  > '],vt100_m]); is_blockquote = 1):
6086        c==e_blockquote ? (write([vt100_n,'\n']); is_blockquote = 0):
6087        c==s_detail_block ? (
6088          ny = y + 1;
6089          nc = j[1];
6090          nc!=e_detail_block?(
6091            j[1] = 0;
6092            ind_e = find(#1,e_detail_block,ny);
6093            nc==_' '?( # Has a title
6094              ind_nl = find(#1,_'\n',ny);
6095              ind_nl<0 || ind_nl>ind_e?(ind_nl = ind_e);
6096              write([vt100_c,vt100_b,'+ ']);
6097              write(#1,y + 2,ind_nl - y - 2);
6098              write([vt100_n,vt100_c,'\n\n']);
6099              copy(i[ny],0,ind_nl - y - 1,1,0);
6100              i[ind_nl]!=e_detail_block?(i[ind_nl] = 0);
6101            ):write([vt100_c,vt100_b,'Details:',vt100_n,vt100_c,'\n\n']);
6102          )
6103        ):
6104        c==e_detail_block ? write([vt100_n,'\n']):
6105        c==s_code_block ? (_write_off?write(_'\n'); write([['  '],vt100_c]); is_code_block = 1):
6106        c==e_code_block ? (write([vt100_n,'\n\n']); is_code_block = 0):
6107        c==s_shell ? (
6108          ind_e = find(#1,e_shell,y);
6109          run('_gmd2ascii_shell. ',y + 1,',',ind_e -  1);
6110          ref(get('_gmd_command',1024,1),str_com);
6111          write('$ ');
6112          len = find(str_com,0);
6113          write(str_com,len);
6114          write('\n\n');
6115          write(#-1,0,h(#-1));
6116          run('rm.');
6117          write(_'\n');
6118          copy(i[y],0,ind_e - y + 1,1,0);
6119        ):
6120        c==s_hrule ? (write_nl(); write('+------------------------------+\n')):
6121
6122        c==s_bold_italic_a ? write([vt100_m,vt100_b,vt100_i]):
6123        c==s_bold_italic_u ? write([vt100_b,vt100_i]):
6124        c==s_bold_a ? write([vt100_m,vt100_b]):
6125        c==s_bold_u ? write(vt100_b):
6126        c==s_italic_a ? write([vt100_m,vt100_i]):
6127        c==s_italic_u ? write(vt100_i):
6128        c==s_strikethrough? write(vt100_s):
6129        c==s_underline? write(vt100_u):
6130        c==s_monospace ? write([_'\47',vt100_c]):
6131        c==e_monospace ? write([vt100_n,_'\47']):
6132        c==s_value_set ? write([vt100_g,vt100_i,_'{']):
6133        c==e_value_set ? write([_'}',vt100_n]):
6134        c==s_word_highlight ? write([_'\47',vt100_g]):
6135        c==e_word_highlight ? write([vt100_n,_'\47']):
6136
6137        isin(c,s_url,s_page_link,s_text_link) ? write(vt100_u):
6138        c==m_text_link ? (
6139          ind = find(#1,e_text_link,y);
6140          copy(i[y],0,ind - y + 1,1,0);
6141          write(vt100_n);
6142        ):
6143        c==s_img ? (
6144          ind_m = find(#1,m_img,y);
6145          ind_e = find(#1,e_img,ind_m);
6146          write(vt100_u);
6147          write(#1,ind_m + 1,ind_e - ind_m -1);
6148          write([vt100_n,_' ',vt100_g,vt100_i,'[image: \'']);
6149        ):
6150        c==m_img ? (
6151          ind = find(#1,e_img,y);
6152          copy(i[y],0,ind - y + 1,1,0);
6153          write([['\']'],vt100_n]);
6154        ):
6155        c==s_pipeline ? (
6156          ind = find(#1,e_pipeline);
6157          write([vt100_g,vt100_i,'[image]',vt100_n]);
6158          copy(i[y],0,ind - y + 1,1,0);
6159        ):
6160        c==s_formula ? write(vt100_g);
6161
6162        c==s_gmic ? (
6163          write([vt100_b,'G\47MIC',vt100_n]);
6164        ):
6165        c==s_whitespace ? write(_' ');
6166      );
6167      end(resize(#0,1,_write_off,1,1,0))"
6168    k[0] discard 0
6169    _gmd2ascii_cut $max_line_length,$indent_forced_newline
6170    nm $fnm.txt
6171
6172  endl done
6173
6174# Cut long ascii lines in selected text images (ignoring the VT100 codes for the length).
6175# $1: Max length of each line >0.
6176# $2: Indentation put after each forced newline.
6177_gmd2ascii_cut :
6178  repeat $! l[$>]
6179    . # [0] = Output, [1] = Input
6180    eval ${-_gmd_write}"
6181      const N = max(8,$1);
6182      ref(vector(#$2+1,_' '),nl); nl[0] = _'\n';
6183
6184      # p = pos of block start, q = pos of latest endable (>=p), l = text length in block (w/o VT100 codes).
6185      p = q = r = 0; l = 1;
6186      while (r<h,
6187        i[r]=='\33' && i[r + 1]==_'['?( # VT100-code -> ignore
6188          while (i[r++]!=_'m', 0);
6189        ):i[r]==_'\n'?(
6190          len = r - p + 1;
6191          write(#1,p,len);
6192          p = q = r + 1;
6193          l = 0;
6194        ):l>N?(
6195          q - p<0.6*N?(q = r - 1);
6196          len = q - p + 1;
6197          write(#1,p,len);
6198          write(nl);
6199          p = r = ++q;
6200          l = size(nl) - 1;
6201        ):(
6202          isin(i[r],_',',_' ')?(q = r);
6203        );
6204        ++r; ++l;
6205      );
6206      p<h?(len = h - p; write(#1,p,len))"
6207
6208    k[0] autocrop 0
6209  endl done
6210
6211# Define variables associated to markdown tokens, for the math parser.
6212_gmd_tokens :
6213  u "const s_section = -1;
6214     const e_section = -2;
6215     const s_subsection = -3;
6216     const e_subsection = -4;
6217     const s_subsubsection = -5;
6218     const e_subsubsection = -6;
6219     const s_subsubsubsection = -7;
6220     const e_subsubsubsection = -8;
6221     const s_anchor = -9;
6222     const e_anchor = -10;
6223     const s_bullet = -11;
6224     const e_bullet = -12;
6225     const s_subbullet = -13;
6226     const e_subbullet = -14;
6227     const s_subsubbullet = -15;
6228     const e_subsubbullet = -16;
6229     const s_center = -17;
6230     const e_center = -18;
6231     const s_right = -19;
6232     const e_right = -20;
6233     const s_table = -21;
6234     const s_htable = -22;
6235     const s_vtable = -23;
6236     const s_hvtable = -24;
6237     const m_table = -25;
6238     const n_table = -26;
6239     const e_table = -27;
6240     const s_blockquote = -28;
6241     const e_blockquote = -29;
6242     const s_detail_block = -30;
6243     const e_detail_block = -31;
6244     const s_code_block = -32;
6245     const e_code_block = -33;
6246     const s_shell = -34;
6247     const e_shell = -35;
6248     const s_hrule = -36;
6249
6250     const s_bold_italic_a = -37;
6251     const e_bold_italic_a = -38;
6252     const s_bold_italic_u = -39;
6253     const e_bold_italic_u = -40;
6254     const s_bold_a = -41;
6255     const e_bold_a = -42;
6256     const s_bold_u = -43;
6257     const e_bold_u = -44;
6258     const s_italic_a = -45;
6259     const e_italic_a = -46;
6260     const s_italic_u = -47;
6261     const e_italic_u = -48;
6262     const s_strikethrough = -49;
6263     const e_strikethrough = -50;
6264     const s_underline = -51;
6265     const e_underline = -52;
6266     const s_monospace = -53;
6267     const e_monospace = -54;
6268     const s_value_set = -55;
6269     const e_value_set = -56;
6270     const s_word_highlight = -57;
6271     const e_word_highlight = -58;
6272
6273     const s_url = -59;
6274     const e_url = -60;
6275     const s_page_link = -61;
6276     const e_page_link = -62;
6277     const s_text_link = -63;
6278     const m_text_link = -64;
6279     const e_text_link = -65;
6280     const s_img = -66;
6281     const m_img = -67;
6282     const e_img = -68;
6283     const s_pipeline = -69;
6284     const e_pipeline = -70;
6285     const s_formula = -71;
6286     const e_formula = -72;
6287
6288     const s_gmic = -73;
6289     const e_gmic = -74;
6290     const s_whitespace = -75;
6291     const s_tab = -76;"
6292
6293# Define variables corresponding to VT100 sequences, for the math parser.
6294_gmd_vt100 :
6295  use_vt100
6296  u "ref('"$_vt100_b"',vt100_b);
6297     ref('"$_vt100_c"',vt100_c);
6298     ref('"$_vt100_g"',vt100_g);
6299     ref('"$_vt100_i"',vt100_i);
6300     ref('"$_vt100_m"',vt100_m);
6301     ref('"$_vt100_n"',vt100_n);
6302     ref('"$_vt100_r"',vt100_r);
6303     ref('"$_vt100_s"',vt100_s);
6304     ref('"$_vt100_u"',vt100_u);"
6305
6306# Define variables and functions to write strings and characters in a streamed way for the math parser.
6307_gmd_write :
6308  u "write(str) = write(str,0);
6309     write(str,len) = (
6310       ref(str,_str); unref(_siz); const _siz = size(_str); ref(len,_len);
6311       _siz?( # String
6312         l = _len?_len:_siz;
6313         _write_off + l>h(#0)?resize(#0,1,8 + l + 5*h(#0)/3,1,1,0);
6314         copy(i[#0,_write_off],_str,l);
6315         _write_off+=l;
6316       ):( # Character
6317         _write_off>=h(#0)?resize(#0,1,8 + 5*h(#0)/3,1,1,0);
6318         i[#0,_write_off++] = _str;
6319       )
6320     );
6321     write(ind,start,len) = (
6322       ref(len,_len);
6323       _write_off + _len>h(#0)?resize(#0,1,8 + _len + 5*h(#0)/3,1,1,0);
6324       copy(i[#0,_write_off],i[#ind,start],_len);
6325       _write_off+=_len;
6326     );
6327     write_nl() = ( # Force newline
6328       i[#0,_write_off -1 ]!=_'\n'?write(_'\n');
6329     );
6330     _write_off = 0;"
6331
6332#@cli parse_gui : _outputmode,_{ * | filter_name}
6333#@cli : Parse selected filter definitions and generate info about filters in selected output mode.
6334#@cli : 'outputmode' can be { gmicol | json | list | print | ts | update | zart }.\n
6335#@cli : It is possible to define a custom output mode, by implementing the following commands
6336#@cli : ('outputmode' must be replaced by the name of the custom user output mode):
6337#@cli : . 'parse_gui_outputmode' : A command that outputs the parsing information with a custom format.
6338#@cli : . 'parse_gui_parseparams_outputmode' (optional): A simple command that returns 0 or 1. It tells the parser \
6339# whether parameters of matching filter must be analyzed (slower) or not.
6340#@cli : . 'parse_gui_trigger_outputmode' (optional): A command that is called by the parser just before parsing \
6341# the set of each matching filters.\n
6342#@cli : Here is the list of global variables set by the parser, accessible in command 'parse_gui_outputmode':\n
6343#@cli : '$_nbfilters': Number of matching filters.
6344#@cli : '$_nongui' (stored as an image): All merged lines in the file that do not correspond to '#@gui' lines.\n
6345#@cli : For each filter `\#F` ('F' in range `[0,$_nbfilters-1]`):
6346#@cli : * '$_fF_name' : Filter name.
6347#@cli : * '$_fF_path' : Full path.
6348#@cli : * '$_fF_locale' : Filter locale (empty, if not specified).
6349#@cli : * '$_fF_command' : Filter command.
6350#@cli : * '$_fF_commandpreview' : Filter preview command (empty, if not specified).
6351#@cli : * '$_fF_zoomfactor' : Default zoom factor (empty, if not specified).
6352#@cli : * '$_fF_zoomaccurate' : Is preview accurate when zoom changes ? (can be { 0=false | 1=true }).
6353#@cli : * '$_fF_inputmode' : Default preferred input mode (empty, if not specified).
6354#@cli : * '$_fF_hide' : Path of filter hid by current filter (for localized filters, empty if not specified).
6355#@cli : * '$_fF_nbparams' : Number of parameters.\n
6356#@cli : For each parameter `\#P` of the filter \#F ('P' in range `[0,$_fF_nbparams-1]`):
6357#@cli : * '$_fF_pP_name' : Parameter name.
6358#@cli : * '$_fF_pP_type' : Parameter type.
6359#@cli : * '$_fF_pP_responsivity' : Parameter responsivity (can be { 0 | 1 }).
6360#@cli : * '$_fF_pP_visibility' : Parameter visibility.
6361#@cli : * '$_fF_pP_propagation' : Propagation of the parameter visibility.
6362#@cli : * '$_fF_pP_nbargs' : Number of parameter arguments.\n
6363#@cli : For each argument `\#A` of the parameter \#P ('A' in range `[0,$_fF_pP_nbargs-1]`):
6364#@cli : * '$_fF_pP_aA' : Argument value\n
6365#@cli : Default parameters: 'filter_name=*' and 'output_format=print'.
6366parse_gui : skip "${1=print},${2=*}"
6367  e[^-1] "Parse '#@gui' filters '$2' and output in '$1' mode."
6368
6369  # Check that specified output mode is actually implemented.
6370  l[] ({'$$parse_gui_$1'}) rm
6371  onfail error[0--2] "Command 'parse_gui': Invalid output mode '$1'."
6372  endl
6373
6374  # Input .gmic data.
6375  use_vt100
6376  if !$! l[] it ${_path_rc}update$_version.gmic onfail endl fi
6377  if !$! return fi
6378  y a y merge_multiline_comments
6379
6380  # Extract all filter chunks with full path.
6381  e[] ""
6382  e[] "\r  > Extract filter chunks."
6383  macros="
6384    is_blank(p) = ((_c=source[p])<=_' ' && _c!=_'\n');
6385    skip_blank(p) = (while (is_blank(p), ++p));
6386    clean(name) = (
6387      _l = find(name,0);
6388      _l>0?(
6389        _p = 0; while (1,
6390          _p = find(name,_'<',_p);
6391          _p<0 || _p>=_l?break();
6392          isin(name[_p+1],_'b',_'i') && name[_p+2]==_'>'?( # Opening tag
6393            copy(name[_p],name[_p+3],_l-_p-2);
6394            _l-=3;
6395          ):name[_p+1]==_'/' && isin(name[_p+2],_'b',_'i') && name[_p+3]==_'>'?( # Closing tag
6396            copy(name[_p],name[_p+4],_l-_p-3);
6397            _l-=4;
6398          ):++_p;
6399        );
6400        _p = 0; while (1, _p = find(name,_'/',_p); _p<0 || _p>=_l?break(); name[_p++] = _'-'); # Replace '/' by '-'
6401      );
6402    );"
6403
6404  eval $macros"
6405    store_block(p,q,path,name,hide) = (
6406      run('+rows[0] ',p,',',q,' discard. -1 autocrop. 10 autocrop. 32 nm. \"',path,'/',name,'\"');
6407
6408      # Insert hide() info at the end of the chunk, with magic number '-2'.
6409      hide[0]?run([40,45,50,44,123,39,34],hide,[34,39,125,41],' y. a[-2,-1] y');
6410    );
6411
6412    # Init variables.
6413    #----------------
6414    ref(crop(),source);
6415    path = prev_path = name = prev_name = hide = prev_hide = vector1024();
6416
6417    # Start parsing.
6418    #---------------
6419    prev_p0 = p0 = p = 0;
6420    while (p<size(source),
6421      p = find(source,'#@gui',p)%size(source);
6422
6423      !p || (p>0 && source[p - 1]==_'\n')?( # Found line starting with '#@gui'
6424        p0 = p; # Remember starting position of line
6425        p+=5;
6426        source[p]==_'_' && (l = find(source,_' ',++p))>=0?(p = l + 1); # Skip locale
6427
6428        # Detect new filter, folder or hide() directive.
6429        #-----------------------------------------------
6430        skip_blank(p);
6431        source[p]!=_':'?(
6432          hide[0] = 0;
6433          q = find(source,_':',p)%size(source);
6434          r = find(source,_'\n',p)%size(source);
6435          is_hide = q>r && (l=find(source,'hide(',p)%size(source))<r;
6436          r<q && !is_hide?(
6437
6438            # Definition of a new folder.
6439            #----------------------------
6440            r0 = r; while (is_blank(--r),0);
6441            copy(name,source[p],r-p+1); name[r-p+1] = 0;
6442            for (levelup = 0, name[levelup]==_'_', ++levelup);
6443            levelup?(
6444              copy(name,name[levelup],r-p+2-levelup); # Clean folder name
6445              while (levelup--, # Update path
6446                lp = find(path,0); l = max(0,find(path,_'/',lp-1,0)); path[l] = 0;
6447              );
6448            );
6449            clean(name);
6450            name[0]?(
6451              ln = find(name,0); # Update path
6452              lp = find(path,0);
6453              lp?(path[lp] = _'/'; copy(path[lp+1],name,ln); path[lp+ln+1] = 0):(copy(path,name,ln); path[ln] = 0);
6454            );
6455            copy(i[#0,p0],-1,r0-p0+1,1,0); # Discard folder definition line (will be removed later)
6456            p = r0 + 1;
6457          ):!is_hide?(
6458
6459            # Definition of a new filter.
6460            #----------------------------
6461            while (is_blank(--q),0);
6462            copy(name,source[p],q-p+1); name[q-p+1] = 0;
6463            clean(name);
6464            store_block(prev_p0,p0-1,prev_path,prev_name,prev_hide);
6465
6466            prev_p0 = p0;
6467            prev_path = path;
6468            prev_name = name;
6469            prev_hide = hide;
6470            p = q + 1;
6471          ):(
6472
6473            # Manage hide() directive.
6474            #--------------------------
6475            l0 = l + 5; l = find(source,_')',l0);
6476            l<r?(
6477              copy(hide,source[l0],l-l0); hide[l-l0] = 0;
6478              copy(i[#0,p0],-1,r-p0+1,1,0);
6479            );
6480            p = r + 1;
6481          )
6482        )
6483      ):++p
6484    );
6485    prev_p0!=p0?store_block(prev_p0,size(source)-1,prev_path,prev_name,prev_hide) # Insert last block
6486    "
6487  rm[0]
6488
6489  # Isolate only requested filters.
6490  if "['$2']!='*'"
6491    1,$!,1,1,"
6492      begin(ref(lowercase(['$2']),str));
6493      ref(lowercase(name(#y)),nm);
6494      find(nm,str)>=0?y:-1"
6495    discard. -1 k[{^}]
6496  fi
6497  l parse_gui_trigger_$1 onfail endl # Allow global pre-processing before parsing each filter.
6498
6499  # Parse each filter.
6500  nbchunks=$!
6501  f=0 repeat $nbchunks,c _f${f}_path={$>,f} if {$>,h} l[$>]
6502    if crop(0,0,1,5)!='#@gui' # Non-gui : merge code and skip chunk
6503      autocrop {'\n'} store nongui$c 0 continue
6504    fi
6505
6506    # Init variables.
6507    _f${f}_locale=
6508    _f${f}_commandpreview=
6509    _f${f}_zoomfactor=
6510    _f${f}_zoomaccurate=
6511    _f${f}_inputmode=
6512    _f${f}_nbparams=0
6513    _f${f}_hide=
6514
6515    # Detect locale.
6516    if crop(0,0,1,6)=='#@gui_' _f${f}_locale={`crop(0,6,1,2)`} fi
6517
6518    # Detect hid filter.
6519    p={"find(#0,-2,h-1,0)"}
6520    if $p>=0 +rows. {$p+1},100% _f${f}_hide={t} rm. fi
6521
6522    # Separate '#@gui' lines from other lines, and merge them.
6523    eval $macros"
6524      ref(crop(),source);
6525      is_first = 1;
6526      p = 0;
6527      while (p<size(source),
6528        p = find(source,'#@gui',p)%size(source);
6529        !p || (p>0 && source[p - 1]==_'\n')?( # Found line starting with '#@gui'
6530          p0 = p; # Remember starting position of line
6531          p+=5;
6532          source[p]==_'_' && (l = find(source,_' ',++p))>=0?(p = l + 1); # Skip locale
6533          skip_blank(p);
6534          source[p]==_':'?(++p; skip_blank(p));
6535          r = find(source,_'\n',p)%size(source); # Find end of line
6536          run('+rows[0] ',p,',',r);
6537          source[r]==_'\n'?(i[#-1,h(#-1)-1] = is_first?0:_' ');
6538          copy(i[#0,p0],-1,min(r,size(source)-1)-p0+1,1,0); # Part to be discarded
6539          p = r + 1;
6540          is_first = 0;
6541        ):++p
6542      )"
6543    a[^0] y discard.. -1
6544    autocrop[0] {'\n'} store[0] nongui$c
6545    s -,0 # Split in two images : definition and parameters
6546    f "max(_' ',i)" # Replace all blank characters by space
6547
6548    # Parse filter definition.
6549    l[0]
6550      _f${f}_zoomaccurate=0
6551      s -,{':'} autocrop {'" "'}
6552      _f${f}_name={`$macros" name=[['"{0,t}"'],0]; clean(name); name"`} rm[0] # Filter name
6553      if $! l[0]
6554        s -,{','} autocrop {'" "'}
6555        _f${f}_command={0,t} # Filter command
6556        if $!>1 l[1]
6557          s -,{'('}
6558          _f${f}_commandpreview={0,t} # Filter preview command
6559          if $!>1 l[1]
6560            s +,{'+'}
6561            discard[0] {')'}
6562            _f${f}_zoomfactor={0,t} # Default zoom factor
6563            if $!>1" && "i[0]==_'+' _f${f}_zoomaccurate=1 fi # Is zoom accurate ?
6564          endl fi
6565        endl fi
6566        rm
6567      endl fi
6568      if $! _f${f}_inputmode={0,t} fi # Default input mode
6569      rm
6570    endl
6571    e[] "\r  > "$_vt100_c[#$f]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
6572
6573    # Parse filter parameters.
6574    parseparams=1
6575    l[] parseparams=${-parse_gui_parseparams_$1} onfail endl
6576
6577    if !$!" || "!$parseparams _f${f}_nbparams=0
6578    else
6579      eval $macros"
6580        ref(crop(),source);
6581        ref(vector256(),name);
6582        ref(vector256(),type);
6583        ref(vector65536(),argument);
6584
6585        p = nbparams = 0;
6586        while (p<size(source),
6587          q = find(source,_'=',p)%size(source);
6588          source[q]==_'='?( # Found new parameter
6589            skip_blank(p);
6590            r = q + 1; skip_blank(r);
6591            while (is_blank(--q),0);
6592            copy(name,source[p],q-p+1); name[q-p+1] = 0; # Parameter name
6593            store(name,'tmparg',q-p+1);
6594            p = r;
6595            q = min(find(source,_'(',p)%size(source),
6596                    find(source,_'{',p)%size(source),
6597                    find(source,_'[',p)%size(source));
6598            c = source[q]==_'('?_')':source[q]==_'{'?_'}':_']';
6599            r = q + 1; skip_blank(r);
6600            while (is_blank(--q),0);
6601
6602            responsivity = 1;
6603            source[p]==_'_'?(responsivity = 0; ++p);
6604            copy(type,source[p],q-p+1); type[q-p+1] = 0; # Parameter type
6605            for (i = 0, type[i], ++i, type[i] = lowercase(type[i])); # Force lowercase
6606            run('$tmparg _f"${f}"_p',nbparams,'_name={t} rm. _f"${f}"_p',nbparams,'_type=',
6607                 type,' _f"${f}"_p',nbparams,'_responsivity=',responsivity);
6608
6609            # Parse list of arguments.
6610            p = r;
6611            r = find(source,c,p)%size(source);
6612            nbargs = 0;
6613
6614            type[0,5]==[['bool'],0] ||
6615            type[0,7]==[['button'],0] ||
6616            type[0,6]==[['value'],0] ||
6617            type[0,7]==[['filein'],0] ||
6618            type[0,8]==[['fileout'],0] ||
6619            type[0,7]==[['folder'],0] ||
6620            type[0,5]==[['note'],0] ||
6621            type[0,10]==[['separator'],0]?( # Single-argument parameter
6622              p!=r?(
6623                skip_blank(p);
6624                pe = r;
6625                while (is_blank(--pe),0);
6626                copy(argument,source[p],pe-p+1); argument[pe-p+1] = 0; # Parameter argument
6627                store(argument,'tmparg',1,pe-p+1); run('$tmparg _f"${f}"_p',nbparams,'_a0={t} rm.');
6628                nbargs = 1;
6629              ):(argument[0] = 0);
6630              p = r + 1;
6631            ):( # Multiple-arguments parameter -> parse list of arguments
6632              while (p<r,
6633                skip_blank(p);
6634                pe = q = min(r,find(source,_',',p)%size(source));
6635                while (is_blank(--pe),0);
6636                copy(argument,source[p],pe-p+1); argument[pe-p+1] = 0; # Parameter argument
6637                store(argument,'tmparg',1,pe-p+1); run('$tmparg _f"${f}"_p',nbparams,'_a',nbargs,'={t} rm.');
6638                ++nbargs;
6639                p = q + 1;
6640              );
6641            );
6642            run('_f"${f}"_p',nbparams,'_nbargs=',nbargs);
6643
6644            # Extract visibility state.
6645            p = r + 1;
6646            p<size(source) && source[p]==_'_' && isin(source[p+1],_'0',_'1',_'2')?(
6647              r = p + 1;
6648              run('_f"${f}"_p',nbparams,'_visibility=',source[r]-_'0');
6649              isin(source[r+1],_'+',_'-',_'*')?run('_f"${f}"_p',nbparams,'_propagation=',[source[++r]]);
6650            );
6651
6652            p = r + 1;
6653            skip_blank(p);
6654            source[p]==_','?++p;
6655            ++nbparams;
6656          ):++p;
6657        );
6658        run('_f"${f}"_nbparams=',nbparams)"
6659    fi
6660    if !$! 0 fi
6661    f+=1
6662  endl fi done
6663  _nbfilters=$f rm
6664
6665  # Merge all non-gui code.
6666  repeat $nbchunks if narg(${nongui$>}) ${nongui$>} if !w rm. fi fi done
6667  if $! ('\n') a[^-1] .,y rm. a y else 0 fi
6668  store. _nongui
6669
6670  # Done!
6671  e[] "\r  > "${_vt100_g}{`copy(vector64(_'" "'),'"Parsing done!"')`}$_vt100_n
6672  v + parse_gui_$1 # Output result of the parsing.
6673
6674#
6675# Implements 'print' mode for command 'parse_gui' (default output mode).
6676#
6677parse_gui_parseparams_print : u 0 # Tell parser to not parse filter parameters
6678parse_gui_trigger_print : sort_list +,n
6679parse_gui_print :
6680  e[] "\r"${_vt100_g}{`vector68(_'" "')`}$_vt100_n
6681  e[] "  > "${_vt100_g}"List of matching filters:"$_vt100_n"\n\n"
6682  repeat $_nbfilters
6683    +e[] "  [#"{1+$>}"] "${_f$>_path}${_f$>_name}
6684  done
6685
6686# Implements 'thumbnails' mode for command 'parse_gui'.
6687parse_gui_parseparams_thumbnails : u 1 # Tell parser to parse filter parameters
6688parse_gui_thumbnails :
6689  e[] "  >> Generate output, in 'thumbnails' mode.\n"
6690  if !isdir('thumbnails') x "mkdir -p thumbnails" fi
6691  sp landscape r2dy 256 r 256,256,1,3,0,0,0.5,0.5 store img
6692  _preview_width,_preview_height=256
6693
6694  repeat $_nbfilters,f
6695    nb$f=${_f${f}_nbparams}
6696  done
6697
6698  repeat $_nbfilters,f
6699    command=${_f${f}_commandpreview}
6700    if ['$command']==0 command=${_f${f}_command} fi
6701    pipeline=
6702    c=
6703    if s='$command';s!='_none_'&&s!='fx_whatsnew_preview'
6704      pipeline.=$command" "
6705      repeat ${_f${f}_nbparams},p
6706        type=${_f${f}_p${p}_type}
6707        arg0=${_f${f}_p${p}_a0}
6708        if s='$type';s=='file'||s=='filein'||s=='fileout'||s=='folder'||s=='value'
6709          if !${_f{$f}_p${p}_nbargs} pipeline.=$c\"\"
6710          else
6711            args= ac=
6712            repeat ${_f${f}_p${p}_nbargs},a args.=$ac${_f${f}_p${p}_a${a}} ac=, done
6713            s=\"
6714            if ['$args']==0 s=\" elif ['$args'][0]==_'\"' s= fi
6715            pipeline.=$c$s$args$s
6716          fi
6717          c=,
6718        elif s='$type';s=='text'
6719          val,off=0
6720          N=${_f${f}_p${p}_nbargs}
6721          l[] if isint($arg0) val=$arg0 N-=1 off=1 fi onfail endl
6722          args= ac=
6723          repeat $N,a na={$a+$off} args.=$ac${_f${f}_p${p}_a${na}} ac=, done
6724          s=\"
6725          if ['$args']==0 s=\" elif ['$args'][0]==_'\"' s= fi
6726          pipeline.=$c$s$args$s
6727          c=,
6728        elif s='$type';s=='float'||s=='int'
6729          pipeline.=$c$arg0 c=,
6730        elif s='$type';s=='button'
6731          pipeline.=${c}0 c=,
6732        elif s='$type';s=='bool'
6733          val=0
6734          if s='$arg0';s==1||s=='true' val=1 fi
6735          pipeline.=$c$val c=,
6736        elif s='$type';s=='choice'
6737          val=0
6738          l[] if isint($arg0) val=$arg0 fi onfail endl
6739          pipeline.=$c$val c=,
6740        elif s='$type';s=='color'
6741          if ${_f${f}_p${p}_nbargs}==1" && "['$arg0'][0]==_'#' # Convert colors specified as '#RRGGBB[AA]'
6742            l[] ('$arg0') autocrop. {'#'} s x,-2 _f${f}_p${p}_nbargs=$!
6743              repeat $!,a hex2dec {$>,t} _f${f}_p${p}_a${a}=${} done rm
6744            endl
6745          fi
6746          repeat ${_f${f}_p${p}_nbargs},a pipeline.=$c${_f${f}_p${p}_a${a}} c=, done
6747        elif s='$type';s=='point'
6748          pipeline.=$c$arg0,${_f${f}_p${p}_a1} c=,
6749        fi
6750      done
6751    fi
6752    +e[] ${_vt100_c}"  [#"{1+$>}"] "${_vt100_n}${_f${f}_name}": "${_vt100_g}$pipeline${_vt100_n}
6753    if ['$pipeline']!=0
6754      filename thumbnails/thumb.png,$f file_out=${}
6755      l[]
6756        $img run $pipeline gui_merge_layers r2din 200,200,2
6757      onfail
6758        e[] "Error "$_vt100_r${}$_vt100_n"\n"
6759        rm
6760      endl
6761    fi
6762
6763    if !$! $img gui_no_preview , fi
6764    c. 0,255 r2din. 200,200,2 r. 100%,100%,1,{min(s,4)} to_rgba. r. 200,200,1,4,0,0,0.5,0.5
6765    if {*} w. ${"fitscreen ."} fi
6766    o. $file_out rm.
6767
6768  done
6769  e[] "  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
6770
6771#
6772# Implements 'whatsnew' mode for command 'parse_gui'.
6773#
6774parse_gui_parseparams_whatsnew : u 0 # Tell parser to not parse filter parameters
6775parse_gui_whatsnew :
6776  e[] "  >> Generate output, in 'whatsnew' mode.\n"
6777  l[] if $_nbfilters
6778    1,1,1,1x$_nbfilters
6779    repeat $! nm[$>] -${_f$>_path}${_f$>_name} f[$>] $> done
6780    sort_list +,n a x
6781    repeat w n={0,i[$>]} ('${_f${n}_path}" "${_f${n}_name}\n') done
6782    rm[0]
6783  fi endl
6784  y a y
6785  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
6786
6787#
6788# Implements 'list' mode for command 'parse_gui'.
6789#
6790parse_gui_parseparams_list : u 0 # Tell parser to not parse filter parameters
6791parse_gui_list :
6792  e[] "  >> Generate output, in 'list' mode.\n"
6793  ('"*** List of filters in the G\47MIC plug-in ("$_nbfilters" filters, on "\
6794               {`v=date(1);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}"/"\
6795               {`v=date(2);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}", "\
6796               {`v=date(4);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}":"\
6797               {`v=date(5);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}") ***\n\n"')
6798
6799  ('"* List of filters, sorted by path:\n\n"')
6800  l[]
6801    1,1,1,1x$_nbfilters
6802    repeat $! nm[$>] -${_f$>_path}${_f$>_name} f[$>] $> done
6803    sort_list +,n a x
6804    repeat w n={0,i[$>]}
6805      ('${_f${n}_path}${_f${n}_name}" (command: '"${_f${n}_command}"')\n"')
6806    done
6807    rm[0]
6808  endl
6809
6810  ('"\n* List of filters, sorted alphabetically:\n\n"')
6811  l[]
6812    1,1,1,1x$_nbfilters
6813    repeat $! nm[$>] -${_f$>_name} f[$>] $> done
6814    sort_list +,n a x
6815    repeat w n={0,i[$>]}
6816      ('${_f${n}_name}" (in '"${_f${n}_path}"')\n"')
6817    done
6818    rm[0]
6819  endl
6820
6821  ('"\n*** End of list ***\n"')
6822  y a y
6823  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
6824
6825#
6826# Implements 'update' mode for command 'parse_gui'.
6827#
6828parse_gui_parseparams_update : u 1 # Tell parser to parse filter parameters
6829parse_gui_update :
6830  e[] "  >> Generate output, in 'update' mode.\n"
6831  path=/
6832  N={$_nbfilters-1}
6833  is_af={date([1,2])==[4,1]" && "(date(4)%2)} # April fool mode ?
6834  repeat $_nbfilters,f
6835    e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
6836
6837    # Insert directives to change folder if necessary.
6838    if ;['$path']!=['${_f${f}_path}']
6839      command_path={`"
6840        src = ['"$path"'];
6841        dest = ['"${_f${f}_path}'"];
6842        ref(vector1024(),command);
6843        for (p = 0, p<min(size(src),size(dest)) && src[p]==dest[p], ++p); # Find common base
6844        while (p>0 && src[p]!=_'/' && dest[p]!=_'/', --p);
6845
6846        nb_levelup = 0;
6847        for (q = p + 1, q<size(src), ++nb_levelup, q = find(src,_'/',q) + 1);
6848        nb_levelup?(copy(command,'#@gui '); copy(command[6],_'_',nb_levelup,1,0));
6849
6850        is_testing = !find(dest,'Testing/');
6851        is_about = !find(dest,'About/');
6852
6853        dest[p]==_'/'?++p;
6854        while (p<size(dest),
6855          q = find(dest,_'/',p)%size(dest);
6856          eoc = find(command,0);
6857          !eoc || command[eoc-1]!=_'_'?(copy(command[eoc],'#@gui '); eoc+=6);
6858          is_italic = (is_testing && find(dest,'Testing/',p)<0) || is_about;
6859          copy(command[eoc],is_italic?'<i>':'<b>');
6860          copy(command[eoc + 3],dest[p],q-p);
6861          copy(command[eoc + 3 + q - p],is_italic?'</i>\n':'</b>\n');
6862          p = q + 1;
6863        ); command"`}
6864      ('$command_path\n')
6865    fi
6866
6867    path=${_f${f}_path}
6868
6869    if narg(${_f${f}_commandpreview}) commandpreview=,${_f${f}_commandpreview} else commandpreview= fi
6870    if narg(${_f${f}_locale}) locale=_${_f${f}_locale} else locale= fi
6871    if narg(${_f${f}_zoomfactor}) zoomfactor=(${_f${f}_zoomfactor}) else zoomfactor= fi
6872    if ${_f${f}_zoomaccurate} zoomaccurate=+ else zoomaccurate= fi
6873    if narg(${_f${f}_inputmode}) inputmode=" : "${_f${f}_inputmode} else inputmode= fi
6874    strcapitalize ${_f${f}_name} name=${}
6875
6876    if ['${_f${f}_hide}']!=0 ('"#@gui"$locale" hide("${_f${f}_hide}")\n"') fi
6877    ('"#@gui"$locale" "$name:${_f${f}_command}$commandpreview$zoomfactor$zoomaccurate$inputmode\n')
6878
6879    repeat ${_f${f}_nbparams},p
6880      responsivity,visibility=
6881      if !${_f${f}_p${p}_responsivity} responsivity=_ fi
6882      if narg(${_f${f}_p${p}_visibility}) visibility=_${_f${f}_p${p}_visibility}${_f${f}_p${p}_propagation} fi
6883      is_choice={['${_f${f}_p${p}_type}']=='choice'}
6884      is_color={['${_f${f}_p${p}_type}']=='color'}
6885
6886      # Build list of arguments.
6887      args= c=
6888      if $is_choice # Capitalize arguments of choice()
6889        repeat ${_f${f}_p${p}_nbargs},a
6890          if $is_af straprilfool ${_f${f}_p${p}_a${a}}
6891          else strcapitalize ${_f${f}_p${p}_a${a}}
6892          fi
6893        args.=$c${} c=, done
6894      elif $is_color # Force html color code for color()
6895        if inrange(${_f${f}_p${p}_nbargs},3,4)" && "\
6896           inrange(${_f${f}_p${p}_a0},0,255)" && "\
6897           inrange(${_f${f}_p${p}_a1},0,255)" && "\
6898           inrange(${_f${f}_p${p}_a2},0,255)" && "\
6899           (${_f${f}_p${p}_nbargs}==3" || "inrange(${_f${f}_p${p}_a3},0,255))
6900          args=#
6901          repeat ${_f${f}_p${p}_nbargs},a
6902            args.={`"tab = [ _'0',_'1',_'2',_'3',_'4',_'5',_'6',_'7',_'8',_'9',_'a',_'b',_'c',_'d',_'e',_'f' ];
6903                     const v = "${_f${f}_p${p}_a${a}}"; [ tab[(v>>4)&15],tab[v&15] ]"`}
6904          done
6905        else repeat ${_f${f}_p${p}_nbargs},a args.=$c${_f${f}_p${p}_a${a}} c=, done
6906        fi
6907      else
6908        repeat ${_f${f}_p${p}_nbargs},a args.=$c${_f${f}_p${p}_a${a}} c=, done
6909      fi
6910      sep1,sep2={`"
6911        s = ['"$args"'];
6912        size(s)?(
6913          find(s,_'(')<0 && find(s,_')')<0?[_'(',_',',_')']:
6914          find(s,_'{')<0 && find(s,_'}')<0?[_'{',_',',_'}']:
6915          [_'[',_',',_']']
6916        ):[_'(',_',',_')'];
6917      "`}
6918      if s=['${_f${f}_p${p}_type}'];"s=='link' || s=='note' || s=='separator' || s=='value'" name=_
6919      else strcapitalize ${_f${f}_p${p}_name} name=${}
6920      fi
6921      ('"#@gui"$locale" :"{/$name}=$responsivity${_f${f}_p${p}_type}{``$sep1}{``{/$args}}{``$sep2}$visibility\n')
6922
6923    done
6924  done
6925#  repeat $! l[$>] utf82html endl done
6926  $_nongui y a y
6927
6928  # Clean generated output, add footer and header.
6929  compress_gmic.
6930  i[0] ('"#@gmic"\n\
6931         "#"\n\
6932         "#  File         : update"$_version".gmic"\n\
6933         "#                 ( G\47MIC command file )"\n\
6934         "#"\n\
6935         "#  Description  : Update file for G\47MIC commands and filters (for version "${-strver}")."\n\
6936         "#                 ( https://gmic.eu )"\n\
6937         "#"\n\
6938         "#  License      : CeCILL v2.1"\n\
6939         "#                 ( https://cecill.info/licences/Licence_CeCILL_V2.1-en.html )"\n\
6940         "#"\n\
6941         "#  Generated on : "\
6942         {date(0)}"/"\
6943         {`v=date(1);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}"/"\
6944         {`v=date(2);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}", "\
6945         {`v=date(4);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}":"\
6946         {`v=date(5);sv=vtos(v);v<10?[_'0',sv]:[sv,0]`}\n\
6947         "#"\n')
6948  ('"\n# Local Variables:"\n\
6949    "# mode: sh"\n\
6950    "# End:"\n\
6951    "#"\n\
6952    "# (End of G\47MIC update file)"')
6953  y a y
6954  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
6955
6956# April Fool version!
6957straprilfool : skip "${1=}"
6958  if ['"$1"']==0 u "" return fi
6959  l[] ('{/"$1"}')
6960    f "(i<=_' ' || i==_'_') && i!=_'\n'?_' ':i" # Replace blank characters and underscores
6961    f "x%2?lowercase(i):uppercase(i)"
6962    f "c = lowercase(i);
6963       c==_'o'?_'0':
6964       c==_'i'?_'1':
6965       c==_'e'?_'3':
6966       c==_'a'?_'4':
6967       c==_'s'?_'5':
6968       c==_'t'?_'7':
6969       c==_'b'?_'8':
6970       i"
6971  u {t} rm endl
6972
6973#
6974# Implements 'json' mode for command 'parse_gui'.
6975#
6976parse_gui_parseparams_json : u 1 # Tell parser to parse filter parameters
6977parse_gui_trigger_json :
6978  repeat $! l[$>] # Keep only 1-level folders
6979    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`}
6980  endl done
6981  sort_list +,n
6982
6983parse_gui_json :
6984  e[] "  >> Generate output, in 'json' mode.\n"
6985  ('"{\n  \"format_version\": \"gmic_json_1.0\",\n  \"gmic_version\": \""${-strver}"\",\n  \"categories\": [\n"')
6986
6987  current_category=
6988  N={$_nbfilters-1}
6989  repeat $_nbfilters,f
6990    e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
6991
6992    # Manage categories.
6993    0 nm. {`s=['${_f${f}_path}'];s[0,size(s)-1]`} _parse_gui_json[] {b} path=${} rm.
6994    if ['$current_category']!=['$path']
6995      if ['$current_category']!=0 ('"    ]\n  },\n"') fi
6996      ('"  {\n"')
6997      ('"    \"name\": \""$path"\", \"filters\": [\n"')
6998      current_category=$path
6999    else
7000      if i[-1,2]==_'\n' z. 0,{w-2} fi
7001      ('",\n"')
7002    fi
7003
7004    # Filter definition.
7005    _parse_gui_json[] ${_f${f}_name} fname=${}
7006
7007    locale=en
7008    if narg(${_f${f}_locale}) locale=${_f${f}_locale} fi
7009
7010    ('"    {\n      \"name\": \""$fname"\", \"lang\": \""$locale"\", \"command\": \""${_f${f}_command}"\", "\
7011      "\"command_preview\": \""${_f${f}_commandpreview}"\", \"parameters\": [\n"')
7012
7013    sepf={`$<?_',':0`}
7014    ppos=1
7015    repeat ${_f${f}_nbparams},p
7016      _parse_gui_json ${_f${f}_p{$p}_name} name={/${}}
7017      type=${_f${f}_p{$p}_type}
7018      nbargs=${_f${f}_p{$p}_nbargs}
7019      arg0= repeat $nbargs ('${_f${f}_p${p}_a$>}') autocrop. {'\"'} _parse_gui_json {t} arg$>=${} rm. done
7020      sepp={`$<?_',':0`}
7021      if narg(${_f${f}_p{$p}_visibility})
7022        visibility=", \"visibility\": \""${_f${f}_p{$p}_visibility}"\""
7023      else
7024        visibility=
7025      fi
7026
7027      if ['$type']=='bool'
7028        if lowercase(['$arg0'])=='false' arg0=0 elif lowercase(['$arg0'])=='true' arg0=1 elif !isnum($arg0) arg0=0 fi
7029        ('"      { \"type\": \"bool\", \"name\": \""$name"\", \"default\": \""$arg0"\", \"pos\": "\
7030          "\""$ppos"\""$visibility" }"$sepp\n')
7031        ppos+=1
7032
7033      elif ['$type']=='button'
7034        if narg($arg0) alignment=", \"alignment\": \""$arg0"\"" else alignment= fi
7035        ('"      { \"type\": \"button\", \"name\": \""$name"\""$alignment", \"pos\": \""$ppos"\""$visibility" }"\
7036         $sepp\n')
7037        ppos+=1
7038
7039      elif ['$type']=='choice'
7040        default=0 n=0 choices=
7041        l[] if isnum($arg0)&&isint($arg0) default=$arg0 n+=1 fi onfail endl
7042        c= repeat $nbargs-$n,a choices.=${c}"\""$>"\": \""${arg{$n+$a}}"\"" c=", " done
7043        ('"      { \"type\": \"choice\", \"name\": \""$name"\", \"default\": \""$default"\", \"pos\": \""$ppos"\", "\
7044          "\"choices\": { "$choices" }"$visibility" }"$sepp\n')
7045        ppos+=1
7046
7047      elif ['$type']=='color'
7048        if $nbargs==1" && "['$arg0'][0]==_'#' # Convert colors specified as '#RRGGBB[AA]'
7049          l[] ('$arg0') autocrop. {'#'} s x,-2 nbargs=$! repeat $!,a hex2dec {$>,t} arg$a=${} done rm endl
7050        fi
7051        args= c= repeat $nbargs,a args.=$c${arg$a} c="," done
7052        ('"      { \"type\": \"color\", \"name\": \""$name"\", \"default\": \""$args"\", \"pos\": "\
7053          "\""$ppos"\""$visibility" }"$sepp\n')
7054        ppos+=$nbargs
7055
7056      elif s=['$type'];s=='int'
7057        ('"      { \"type\": \"int\", \"name\": \""$name"\", \"default\": \""$arg0"\", \"min\": \""$arg1"\", "\
7058          "\"max\": \""$arg2"\", \"pos\": \""$ppos"\""$visibility" }"$sepp\n')
7059        ppos+=1
7060
7061      elif s=['$type'];s=='float'
7062         ('"      { \"type\": \"float\", \"name\": \""$name"\", \"default\": \""$arg0"\", \"min\": \""$arg1"\", "\
7063           "\"max\": \""$arg2"\", \"pos\": \""$ppos"\""$visibility" }"$sepp\n')
7064        ppos+=1
7065
7066      elif ['$type']=='file'" || "['$type']=='filein'" || "['$type']=='fileout'
7067        ('"      { \"type\": \"file\", \"name\": \""$name"\", \"default\": \""{/$arg0}"\", \"pos\": "\
7068          "\""$ppos"\""$visibility" }"$sepp\n')
7069        ppos+=1
7070
7071      elif ['$type']=='folder'
7072        ('"      { \"type\": \"folder\", \"name\": \""$name"\", \"default\": \""{/$arg0}"\", \"pos\": "\
7073          "\""$ppos"\""$visibility" }"$sepp\n')
7074        ppos+=1
7075
7076      elif ['$type']=='link'
7077        align=-1 name= url= n=0
7078        l[] if isnum($arg0) align=$arg0 n+=1 fi onfail endl
7079        if $nbargs-$n>1 name=${arg$n} url=${arg{$n+1}}
7080        else url,name=${arg$n}
7081        fi
7082        if $align==0 align=left elif $align==1 align=right else align=center fi
7083        ('"      { \"type\": \"link\", \"name\": \""$name"\", \"url\": \""{/$url}"\", \"align\": "\
7084          "\""$align"\""$visibility" }"$sepp\n')
7085
7086      elif ['$type']=='note'
7087        ('"      { \"type\": \"note\", \"text\": \""{/$arg0}"\""$visibility" }"$sepp\n')
7088
7089      elif ['$type']=='point'
7090        ('"      { \"type\": \"point\", \"name\": \""$name"\", \"position\": \""$arg0,$arg1"\", \"pos\": "\
7091          "\""$ppos"\""$visibility" }"$sepp\n')
7092        ppos+=2
7093
7094      elif ['$type']=='separator'
7095        ('"      { \"type\": \"separator\""$visibility" }"$sepp\n')
7096
7097      elif ['$type']=='text'
7098        ('"      { \"type\": \"text\", \"name\": \""$name"\", \"default\": \""{/$arg0}"\", \"pos\": "\
7099          "\""$ppos"\""$visibility" }"\
7100         $sepp\n')
7101        ppos+=1
7102
7103      elif ['$type']=='value'
7104        ('"      { \"type\": \"value\", \"value\": \""{/$arg0}"\", \"pos\": \""$ppos"\""$visibility" }"$sepp\n')
7105        ppos+=1
7106
7107      else # Unknown parameter
7108        ('"      { \"type\": \"unknown\", \"name\": \""$name"\""$visibility" }"$sepp\n')
7109      fi
7110
7111    done
7112    ('"      ]\n    }\n"')
7113  done
7114  if ['$current_category']!=0 ('"    ]\n  }\n"') fi
7115  ('"  ]\n}\n"')
7116  y a y html2utf8
7117  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
7118
7119_parse_gui_json :
7120  l[]
7121    ('{/"$*"}')
7122    replace_str "\\","\\\\"
7123    replace_str "\n","\\n"
7124    replace_str "\r","\\r"
7125    replace_str "\"","\\\""
7126    replace_str "&amp;","&"
7127    replace_str "&nbsp;"," "
7128    replace_str. "<i>",""
7129    replace_str. "</i>",""
7130    replace_str. "<b>",""
7131    replace_str. "</b>",""
7132    replace_str. "<small>",""
7133    replace_str. "</small>",""
7134    u {t} rm.
7135  onfail u "" endl
7136
7137#
7138# Implements 'gmicol' mode for command 'parse_gui'.
7139#
7140parse_gui_parseparams_gmicol : u 1 # Tell parser to parse filter parameters
7141parse_gui_trigger_gmicol :
7142  repeat $! l[$>] # Keep only 1-level folders
7143    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`}
7144  endl done
7145  sort_list +,n
7146
7147parse_gui_gmicol :
7148  e[] "  >> Generate output, in 'gmicol' mode.\n"
7149  ('"<?xml version=\"1.0\"?>\n\n<categories>\n"')
7150
7151  current_category=
7152  N={$_nbfilters-1}
7153  repeat $_nbfilters,f
7154    e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
7155
7156    # Manage categories.
7157    0 nm. {`s=['${_f${f}_path}'];s[0,size(s)-1]`} ('{b}') _gmd_ascii2html. path={t} rm[-2,-1]
7158    if ['$current_category']!=['$path']
7159      if ['$current_category']!=0 ('"\n</category>\n"') fi
7160      ('"\n<!-- Category: "$path" -->\n<category name=\""$path"\">\n"')
7161      current_category=$path
7162    fi
7163
7164    # Filter definition.
7165    ('${_f${f}_name}') _gmd_ascii2html. fname={t} rm.
7166
7167    locale=en
7168    if narg(${_f${f}_locale}) locale=${_f${f}_locale} fi
7169    zoom_factor=
7170    if narg(${_f${f}_zoomfactor}) zoom_factor=" zoom_factor=\""${_f${f}_zoomfactor}"\"" fi
7171    zoom_accurate=
7172    if ${_f${f}_zoomaccurate}!=0 zoom_accurate=" zoom_accurate=\""${_f${f}_zoomaccurate}"\"" fi
7173
7174    ('"\n  <!-- Filter: "$fname" -->\n"\
7175      "  <filter name=\""$fname"\" command=\""${_f${f}_command}"\" preview_command=\""${_f${f}_commandpreview}"\""\
7176      $zoom_factor" "$zoom_accurate\
7177      ">\n"')
7178
7179    sepf={`$<?_',':0`}
7180    repeat ${_f${f}_nbparams},p
7181      ({'${_f${f}_p{$p}_name}'}) _gmd_ascii2html. name={t} rm.
7182      type=${_f${f}_p{$p}_type}
7183      nbargs=${_f${f}_p{$p}_nbargs}
7184      arg0= repeat $nbargs ('${_f${f}_p${p}_a$>}') autocrop. {'\"'} _gmd_ascii2html. arg$>={/{t}} rm. done
7185      sepp={`$<?_',':0`}
7186
7187      visibility=${_f${f}_p{$p}_visibility}
7188      if !narg($visibility) visibility=1 fi
7189      if $visibility!=1 visibility=" visibility=\""$visibility"\"" else visibility= fi
7190
7191      responsivity=${_f${f}_p{$p}_responsivity}
7192      if !narg($responsivity) responsivity=1 fi
7193      if $responsivity!=1 responsivity=" responsivity=\""$responsivity"\"" else responsivity= fi
7194
7195      if ['$type']=='bool'
7196        if lowercase(['$arg0'])=='false' arg0=0 elif lowercase(['$arg0'])=='true' arg0=1 elif !isnum($arg0) arg0=0 fi
7197        ('"    <bool name=\""$name"\" value=\""$arg0"\""$responsivity$visibility"/>\n"')
7198
7199      elif ['$type']=='button'
7200        if !$nbargs arg0=0.5 fi
7201        ('"    <button name=\""$name"\" alignment=\""$arg0"\""$responsivity$visibility"/>\n"')
7202
7203      elif ['$type']=='choice'
7204        default=0 n=0 choices=
7205        l[] if isnum($arg0)&&isint($arg0) default=$arg0 n+=1 fi onfail endl
7206        ('"    <choice name=\""$name"\" value=\""$default"\""$responsivity$visibility">\n"')
7207        repeat $nbargs-$n,a
7208          choice=${arg{$n+$a}}
7209          ('"      <item name=\""$choice"\"/>\n"')
7210        done
7211        ('"    </choice>\n"')
7212
7213      elif ['$type']=='color'
7214        if $nbargs==1" && "['$arg0'][0]==_'#' # Convert colors specified as '#RRGGBB[AA]'
7215          l[] ('$arg0') autocrop. {'#'} s x,-2 nbargs=$! repeat $!,a hex2dec {$>,t} arg$a=${} done rm endl
7216        fi
7217        if $nbargs>3
7218          ('"    <rgba name=\""$name"\" red=\""$arg0"\" green=\""$arg1"\" blue=\""$arg2"\" alpha=\""$arg3"\""\
7219            $responsivity$visibility"/>\n"')
7220        else
7221          ('"    <rgb name=\""$name"\" red=\""$arg0"\" green=\""$arg1"\" blue=\""$arg2"\""\
7222            $responsivity$visibility"/>\n"')
7223        fi
7224
7225      elif s=['$type'];s=='int'
7226         ('"    <int name=\""$name"\" value=\""$arg0"\" min=\""$arg1"\" max=\""$arg2"\""\
7227           $responsivity$visibility"/>"\n')
7228
7229      elif s=['$type'];s=='float'
7230         ('"    <float name=\""$name"\" value=\""$arg0"\" min=\""$arg1"\" max=\""$arg2"\""\
7231           $responsivity$visibility"/>"\n')
7232
7233      elif ['$type']=='file'" || "['$type']=='filein'" || "['$type']=='fileout'
7234        ('"    <file name=\""$name"\" value=\""$arg0"\""$responsivity$visibility"/>\n"')
7235
7236      elif ['$type']=='folder'
7237        ('"    <folder name=\""$name"\" value=\""$arg0"\""$responsivity$visibility"/>\n"')
7238
7239      elif ['$type']=='link'
7240        align=0 name= url= n=0
7241        l[] if isnum($arg0) align=$arg0 n+=1 fi onfail endl
7242        if $nbargs-$n>1 name=${arg$n} url=${arg{$n+1}}
7243        else url,name=${arg$n}
7244        fi
7245        ('"    <link name=\""$name"\" href=\""$url"\" alignment=\""$align"\""$visibility"/>\n"')
7246
7247      elif ['$type']=='note'
7248        ('"    <note name=\""$name"\""$visibility">\n"$arg0"\n    </note>"\n')
7249
7250      elif ['$type']=='point'
7251        if narg($arg2) removable=" removable=\""$arg2"\"" else removable= fi
7252        if narg($arg3) burst=" burst=\""$arg3"\"" else burst= fi
7253        if narg($arg4) red=" red=\""$arg4"\"" else red= fi
7254        if narg($arg5) green=" green=\""$arg5"\"" else green= fi
7255        if narg($arg6) blue=" blue=\""$arg6"\"" else blue= fi
7256        if narg($arg7) alpha=" alpha=\""$arg7"\"" else alpha= fi
7257        if narg($arg8) radius=" radius=\""$arg8"\"" else radius= fi
7258         ('"    <point name=\""$name"\" x=\""$arg0"\" y=\""$arg1"\""$removable$burst$red$green$blue$alpha\
7259           $responsivity$visibility"/>\n"')
7260
7261      elif ['$type']=='separator'
7262        ('"    <separator"$visibility"/>\n"')
7263
7264      elif ['$type']=='text'
7265        multiline=0 n=0
7266        l[] if isnum($arg0) multiline=$arg0 n+=1 fi onfail endl
7267        ('"    <text name=\""$name"\" multiline=\""$multiline"\""\
7268          $responsivity$visibility">"')
7269        if ['${arg$n}']!=0
7270          if $multiline ('\n${arg$n}\n</text>\n') else ('${arg$n}</text>\n') fi
7271        else ('"</text>\n"') fi
7272
7273      elif ['$type']=='value'
7274        ('"    <value name=\""$name"\" value=\""$arg0"\"/>\n"')
7275
7276      else # Unknown parameter
7277        ('"    <unknown name=\"$name\"/>\n"')
7278      fi
7279
7280    done
7281    ('"  </filter>\n"')
7282  done
7283  if ['$current_category']!=0 ('"\n</category>\n"') fi
7284  ('"\n</categories>"')
7285  y a y html2utf8
7286  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
7287
7288#
7289# Implements 'strings' mode for command 'parse_gui'.
7290# (Extract all strings from filters, for translation purpose, and put them in a .csv file format).
7291#
7292parse_gui_strings :
7293  e[] "  >> Generate output, in 'strings' mode.\n"
7294  N={$_nbfilters-1}
7295  repeat $_nbfilters,f
7296    if !narg(${_f${f}_locale})" && "find(['${_f${f}_path}'],'Testing/')<0  # Consider only English, non-Testing filters
7297      e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
7298      _parse_gui_strings ${_f${f}_name}
7299      repeat ${_f${f}_nbparams},p
7300        type=${_f${f}_p${p}_type}
7301        if s=['$type'];s!='link'&&s!='note'&&s!='separator'&&s!='value' _parse_gui_strings ${_f${f}_p${p}_name} fi
7302        if ['$type']==['choice']
7303          repeat ${_f${f}_p${p}_nbargs},a
7304            arg=${_f${f}_p${p}_a${a}}
7305            l[] if !isnum($arg) ({'$arg'}) discard. {'\"'} arg={t} rm. _parse_gui_strings $arg fi onfail endl
7306          done
7307        fi
7308      done
7309    fi
7310  done
7311  # Sort strings and remove duplicates.
7312  sort_list +,N repeat $!-1 i,ni={[$<,$<+1]} if ['{$i,n}']==['{$ni,n}'] +[$i,$ni] fi done sort_list -,i
7313
7314  # Generate output in commented .ts format.
7315  repeat $! l[$>]
7316    nm={n}
7317    ({'$nm" , "'}:y) k. nm $nm
7318  endl done
7319  if $! y i[1--1] (10) a y else 0 fi
7320
7321_parse_gui_strings :
7322  str="$*"
7323  if size(['$str'])>2 l[]
7324    if !isexpr($str)
7325      ({'$str'}) replace_str. "---","-" replace_str. "--","-" str={t} rm.
7326      (1) nm. $str
7327    fi
7328  onfail endl fi
7329
7330#@cli pass : _shared_state={ -1=status only | 0=non-shared (copy) | 1=shared | 2=adaptive } : (+)
7331#@cli : Insert images from parent context of a custom command or a local environment.
7332#@cli : Command selection (if any) stands for a selection of images in the parent context.
7333#@cli : By default (adaptive shared state), selected images are inserted in a shared state if they do not belong
7334#@cli : to the context (selection) of the current custom command or local environment as well.
7335#@cli : Typical use of command 'pass' concerns the design of custom commands that take images as arguments.
7336#@cli : This commands return the list of corresponding indices in the status.
7337#@cli : Default value: 'shared_state=2'.
7338#@cli : $ command "average : pass$""1 add[^-1] [-1] remove[-1] div 2" sample ? +mirror y +average[0] [1]
7339
7340#@cli plot : _plot_type,_vertex_type,_xmin,_xmax,_ymin,_ymax,_exit_on_anykey={ 0 | 1 } : \
7341# 'formula',_resolution>=0,_plot_type,_vertex_type,_xmin,xmax,_ymin,_ymax,_exit_on_anykey={ 0 | 1 } : (+)
7342#@cli : Display selected images or formula in an interactive viewer (use the instant display window [0] if opened).
7343#@cli : 'plot_type' can be { 0=none | 1=lines | 2=splines | 3=bar }.
7344#@cli : 'vertex_type' can be { 0=none | 1=points | 2,3=crosses | 4,5=circles | 6,7=squares }.
7345#@cli : 'xmin', 'xmax', 'ymin', 'ymax' set the coordinates of the displayed xy-axes.
7346#@cli : Default values: 'plot_type=1', 'vertex_type=1', 'xmin=xmax=ymin=ymax=0 (auto)' and 'exit_on_anykey=0'.
7347
7348#@cli p : eq. to 'print'. : (+)
7349
7350#@cli print : (+)
7351#@cli : Output information on selected images, on the standard error (stderr).
7352#@cli : (eq. to 'p').
7353#@cli : When invoked with a '+' prefix (i.e. '+print'), the command output its message on stdout rather than stderr.
7354
7355#@cli random_pattern : _width>0,_height>0,_min_detail_level>=0
7356#@cli : Insert a new RGB image of specified size at the end of the image list, rendered with a random pattern.
7357#@cli : Default values: 'width=height=512' and 'min_detail_level=2'.
7358#@cli : $ repeat 6 random_pattern 256 done
7359+random_pattern : check "isint(${1=512}) && $1>0 && isint(${2=$1}) && $2>0 && ${3=2}>=0"
7360  e[^-1] "Generate a RGB image $1x$2, with a random pattern."
7361  m "rgb2rgb:"
7362
7363  # Generate the 3 channels independently.
7364  repeat 3
7365    do
7366      expr="z = [ "{u(20)}"*((x+0.5)/w-0.5), "{u(20)}"*((y+0.5)/h-0.5) + 0.5/h ]; "${-_random_pattern_expr}
7367      {round([$1,$2]*128/min($1,$2))},1,2,*$expr
7368      replace_naninf. 0 norm. n. 0,255 equalize. 256 c. 0,255
7369      cond=${-_random_pattern_check_image.\ $3}
7370      if !$cond rm. fi
7371    while !$cond
7372    rm. $1,$2,1,2,*$expr
7373    replace_naninf. 0 norm. n. 0,255 equalize. 256 c. 0,255
7374  done
7375  a[-3--1] c
7376
7377  # Convert result from random colorspace to RGB.
7378  ${"arg "{1+int(u(6))%6}",ycbcr2rgb,hsi82rgb,hsl82rgb,hsv82rgb,lab82rgb,rgb2rgb"}.
7379
7380  # Local normalization in random colorspace.
7381  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"}
7382
7383  # Random color balance.
7384  ac. "*. '[{u([0.25,0.25,0.25],[2,2,2])}]'",${"arg "{1+int(u(6))%6}",lab,ycbcr,hsi,hsl,hsv,rgb"}
7385
7386  # Equalize
7387  +equalize. 256 j.. .,0,0,0,0,{u(0.25,1)} rm. n. 0,255
7388  um rgb2rgb nm random_pattern
7389
7390# $1 : probability of a termination node in [0,1]
7391# $2 : level of recursion (avoid stack overflow due to recursivity)
7392_random_pattern_expr : skip "${1=0},${2=0}"
7393  nl={$2+1}
7394  if (u<$1" && "$2>1)" || "$2>6 # Termination node
7395    r={u}
7396    if $r<0.75 u z
7397    else u [{_round(u([-1,-1],[1,1]),0.1)}]
7398    fi
7399  else # Function or operator
7400    p1,p2={[$1,$1]+u([0.2,0.2])}
7401    r={u(27)}
7402    if $r<1 u ccos(${$0\ $p1,$nl})
7403    elif $r<2 u csin(${$0\ $p1,$nl})
7404    elif $r<3 u ctan(${$0\ $p1,$nl})
7405    elif $r<4 u ccosh(${$0\ $p1,$nl})
7406    elif $r<5 u csinh(${$0\ $p1,$nl})
7407    elif $r<6 u ctanh(${$0\ $p1,$nl})
7408    elif $r<7 u cexp(${$0\ $p1,$nl})
7409    elif $r<8 u clog(${$0\ $p1,$nl})
7410    elif $r<9 u [cabs(${$0\ $p1,$nl}),0]
7411    elif $r<10 u [0,cabs(${$0\ $p1,$nl})]
7412    elif $r<11 u [carg(${$0\ $p1,$nl}),0]
7413    elif $r<12 u [0,carg(${$0\ $p1,$nl})]
7414    elif $r<13 u cconj(${$0\ $p1,$nl})
7415    elif $r<14 u (${$0\ $p1,$nl})+(${$0\ $p2,$nl})
7416    elif $r<15 u (${$0\ $p1,$nl})-(${$0\ $p2,$nl})
7417    elif $r<16 u ${$0\ $p1,$nl}**${$0\ $p2,$nl}
7418    elif $r<17 u ${$0\ $p1,$nl}//${$0\ $p2,$nl}
7419    elif $r<18 u (${$0\ $p1,$nl})^^0.5
7420    elif $r<19 u (${$0\ $p1,$nl})^^2
7421    elif $r<20 u (${$0\ $p1,$nl})^^3
7422    elif $r<21 u ${$0\ $p1,$nl}*${$0\ $p2,$nl}
7423    elif $r<22 u ${$0\ $p1,$nl}/(0.01+cabs(${$0\ $p2,$nl}))
7424    elif $r<23 u abs(${$0\ $p1,$nl})^0.5
7425    elif $r<24 u (${$0\ $p1,$nl})^2
7426    elif $r<25 u [(${$0\ $p1,$nl})[0],0]
7427    elif $r<26 u [0,(${$0\ $p1,$nl})[1]]
7428    else u (${$0\ $p1,$nl})^3
7429    fi
7430  fi
7431
7432# Check that rendered image has enough details in it.
7433# $1 = Minimum detail level.
7434_random_pattern_check_image :
7435  cond=1
7436  +s. xy,4
7437  l[^0]
7438    repeat $! if $cond l[$>]
7439      gradient_norm.
7440      if ic<$1 cond=0 fi
7441    endl fi done
7442    rm
7443  endl
7444  u $cond
7445
7446#@cli screen : _x0[%],_y0[%],_x1[%],_y1[%] : (+)
7447#@cli : Take screenshot, optionally grabbed with specified coordinates, and insert it
7448#@cli : at the end of the image list.
7449
7450#@cli select : feature_type,_X[%]>=0,_Y[%]>=0,_Z[%]>=0,_exit_on_anykey={ 0 | 1 },_is_deep_selection={ 0 | 1 } : (+)
7451#@cli : Interactively select a feature from selected images (use the instant display window [0] if opened).
7452#@cli : 'feature_type' can be { 0=point | 1=segment | 2=rectangle | 3=ellipse }.
7453#@cli : Arguments 'X','Y','Z' determine the initial selection view, for 3D volumetric images.
7454#@cli : The retrieved feature is returned as a 3D vector (if 'feature_type==0') or as a 6d vector
7455#@cli : (if 'feature_type!=0') containing the feature coordinates.
7456#@cli : Default values: 'X=Y=Z=(undefined)', 'exit_on_anykey=0' and 'is_deep_selection=0'.
7457
7458#@cli serialize : _datatype,_is_compressed={ 0 | 1 },_store_names={ 0 | 1 } : (+)
7459#@cli : Serialize selected list of images into a single image, optionnally in a compressed form.
7460#@cli : 'datatype' can be { auto | uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }.
7461#@cli : Specify 'datatype' if all selected images have a range of values constrained to a particular datatype,
7462#@cli : in order to minimize the memory footprint.
7463#@cli : The resulting image has only integers values in [0,255] and can then be saved as a raw image of
7464#@cli : unsigned chars (doing so will output a valid .cimg[z] or .gmz file).
7465#@cli : If 'store_names' is set to '1', serialization uses the .gmz format to store data in memory
7466#@cli : (otherwise the .cimg[z] format).
7467#@cli : Default values: 'datatype=auto', 'is_compressed=1' and 'store_names=1'.
7468#@cli : $ image.jpg +serialize uchar +unserialize[-1]
7469
7470#@cli shape_circle : _size>=0
7471#@cli : Input a 2D circle binary shape with specified size.
7472#@cli : Default value: 'size=512'.
7473#@cli : $ shape_circle ,
7474+shape_circle : check "${1=512}>=0"
7475  e[^-1] "Input a $1x$1 circle binary shape."
7476  ir={round($1)}
7477  if !$ir 0
7478  elif $ir<2 $ir,$ir,1,1,1
7479  else
7480    {int($ir/2)+($ir%2)},{int($ir/2)+($ir%2)} =. 1,100%,100%
7481    distance. 1 <=. {(i+0.4)/sqrt(2)}
7482    +mirror. x
7483    if $ir>1" && "($ir%2) r. {w-1},100%,1,1,0,0,1 fi
7484    a[-2,-1] x +mirror. y
7485    if $ir>1" && "($ir%2) r. 100%,{h-1},1,1,0,0,0,1 fi
7486    a[-2,-1] y
7487  fi
7488  nm. "[2D circle shape]"
7489
7490#@cli shape_cupid : _size>=0
7491#@cli : Input a 2D cupid binary shape with specified size.
7492#@cli : Default value: 'size=512'.
7493#@cli : $ shape_cupid ,
7494+shape_cupid : check "${1=512}>=0"
7495  e[^-1] "Input a $1x$1 cupid binary shape."
7496  ir={round($1)}
7497  if !$ir 0
7498  else
7499    base642img[] \
7500"MiBzaG9ydCBsaXR0bGVfZW5kaWFuCjEgMjMwMSAxIDEgIzI2NDQKeJx1mOl3lEUWxm+n6aS7IUSQRQQEFA37FtkFVFAEBxHZEoKACwRQEUHU0fGM58y"\
7501"Zxa9z5i9AFBAhAZKwg8qACMM6CDgZIIQ9Kx0CmAHqzq/u2yR80A91nue5daveWm5tb2moNBSSkAiptCoq9RqToa6p3NVmMtyli2pzecp1IHWUke6R30"\
7502"wjyB9hfh0o97AMIw11D8kQ0mDXRga6VpLlWsoA10L6uQzpQ909XVy6u6hkulTp6lJkyamw1GhTGc33b2iGHHYR+RT8RVvIUReThKbI3+H/0wflGD512"\
7503"kT+oa3EaRt0M7mlEanFZ4X2QqdTLiLX8flK+5i+rWn36eb0MYqOyErT7eBp8rX2gz8Ej9H3iGww3Y5vZRg/QztvaqMuox13tAvpAdMX3JO0J+CX3AuG"\
7504"l904wytuvOFV9yLYF5wgG8Fy9xK6j1S4iWBvcBL23lLpXkH3kio3Bd1Tqt10sIdcczOkULtLws0Eu4GzwEypdbPJfxx8S3aC190i2aWPge+iu0idW4r"\
7505"uJDfcMvARuek+Ajsa7tT24O9lB33zuE3b4rdY1mtrw3zGPFtTZbm+K9/Qv+mM1Re6iPFKl2mM1Qp9R1Yxd1NJX8FXYptCDK2Ef0m5yfitgtcy7qsNMy"\
7506"i7iLY9IGsMW1Cvx5aylm94XGfYSgp0seF6w9aMyWLKt6HP76HbShFYCxaDCXCTYTvZqksYq4fp01KwA31dKjXE6ff6PthZ9uoyxrSL7AOr3KNyQD9gz"\
7507"B+Tg2CFe1yOGHaTf4Plrrv8BNYRq7eZi1OUqSN+7zJPJfCDGpJu6tdNXzmDPqRh6aHPShn8MDHXQ5+Ti8bD0l3HyiXa4u3ddZxcpT2ed9PxUtHAX5RK"\
7508"fA4Z/51UJXkmvJp+HYQ/Aa+BH2J8M3WCXKPfB+GPUzbgacYTxmNWNsGYHqSdmWb3vBn1jIcvop4IcZzKfKbKSdp1Td+2b9ajsyl/ClsC22H8/NrKoew"\
7509"p+pLQt8x2kzSDeT6lz1N2ofWjDlsue8hJ+p/QBeZXR30ziaEy2ndXx7BmA3st9le1JWPzstmOoBPYX2WNXzHbfPOrwT6LuKzQSazT+dbGauqaTcxW6S"\
7510"vUH/hVYZtDPNckbUewVZjtYeoNbEepqxzba8R/gvoCW4Q5iWLrQP3etoAYiMhlK8v60Yn2XW+7ZGUfZe+ZSPvyiJGIXKDOOdqVcXuJdr0hJ7CVYZvNG"\
7511"r3DHFToa4xRRErN70O+P0d+hp81/QHfno2OEEdpDboE/V+rY5lpz0tMv8/YzEJHrIzXl3Wm6VM2TkvJD/RJ00tMn0afsDFbgn8uuokct/F/j/wZfDsV"\
7512"nYZenNQROYYtlzi5ojmWf9Ri4B3ak2Nt9/P1BvFdT4xc1Wwr42NzHrFzhxgpT/p5W55+zDjk2Bj8i7RAPyHefX6EdRjoqqT/ftJC/QPzOMP8f2zQgf8"\
7513"+2vkW+lpS7yXf60SyfKA/RU+3Nv2zIT+Vto4z2+5knSU60vq2O9mGEh1h+rsGPczG8lvrwyeM21Cbi2+tn38iHoaY3kWb5uqf6fNg0ztIb+pf6GOjfl"\
7514"3/RhsCvd3m/nNibbDVv83m5nPiKsj3eib+9ejT6C2kHP0r6yf4ntfT9VvL22zzcMD4JvvuAfPZRB3zdCfnZ5rpYjAPfRheZHyH7QGFxrfbPN3j3mej8"\
7515"W3GN1hdW22drbe+brH59/xN3ZTkUfpY1GCfpRtsvRVYXwosnvLh2bqWtRTwV3QVa8jzKGdJAeOdZvbp1FkKrjP/rbae1uGTQ9sumD3K3rPL1uhaUo5+"\
7516"RwymwqP4f0+sRThfPP/B5t3bp+t+1nLApzJGt5I+U+D1ST4Jfsd4TCbC1fxj8rz+aPgcuA4co/usnjGmozIazDfcT39jhuvBZ8ENGpdnwI3oUfgVgk9"\
7517"RvhgcqLuZv7hk0eYt6CzdQ2x4/IEz2tv3c341Bc/JfwyvyDn24QFazvmSDlYRcxnST6tZHy05XyroV2swHuqhTUMlrPOpmh46Q53Z2jp0mTpytHPoOn"\
7518"t0rj4Rus39aoYOD03WJqGb+gTndkqoWjszJimh89QzWUOhMvZjj+eo/2VV5iVDxuovrLV0xuMW6yVOf2/anvU02IbUWuukhV6TGO2N6iVJ1YvSRM9Lm"\
7519"BTSUql3P8stt4+7Uzr3j48Z8xhzlsk5wz3PfWjzcZF23EDXOn9OtSD2s8kPs889wNqPc89qTmxPw5ZCrMe5fzWFT6P/IcY2Lhe5a966Tx/nPnXP99e4"\
7520"n4+fXHbSPyYnuHcFPjE56XIb/Eu4f93zP+3mWJu8zxn3GmUD/7PudePe55x7Ax74lLk3OTe9PSrn3dwkj9HWeQ38ksuDh83H8xtJ+xU3v4FfdQuMF+F"\
7521"T7hbCw2avuI9Xci+sS/r/Fq9yb8ODtnl+Pdn+Knxqf4VX41OL/+Yk93f0+/kWxqcGfs14jLhPI969DnM/8+unOfW8TaymmB5LTG1J6m2UHUfMbeXsqM"\
7522"bfr4Px+hD2Rj2BM3o7ugr/Hfjncj9u1DH2md7gQvbdMOsnxnnWp0Hvwn8W97VGHUP3x28BayiFPT3OHjwgqcOcAT7f6/nsLWHWoc/PQuex76SgfX6j3"\
7523"m35T1JvHudCmHPH5z9JvXnEclj2WHsGYl8g5/G/p/fR/zL0Xmv/IM7DRaZ/QOey3x/izC2l/I/W3zGss0Wm96NzuHuVcmafxX+/rfEXqHsxazKFemKs"\
7524"/Qj311Gc4Uuwhbn/xeUFzrxK7g1eH0I/j67mnnGaMofRY/BPcE/xdRxBP82ZeF0/Mv9j6BE6nJj5mG+G2cfjMpgz6iZno/c/jh5An3/hbPX+J9D90PW"\
7525"cx2dt7TaVPujb+kfrw8/oXui7+pn1we9xPbTYfE+zz3WDe78zdm8tMn4Wn65w7+/3wke10Ph59pLOcO9zEXtH3Wj8Mrw93Ptcxacd55Ln5fC2ut54Jb"\
7526"wV3PvXEKMtOYu8PQHP0Hzj19n30nWd8Trs8SS/RdlUzjTP6+Fh/cbafwcuugb+oDj3NdhCbrvVYHP2vlVgnHfLV2CU9fOl3V0qDbmPui/AJuxxcxmrI"\
7527"cYv2v4yyOo+z9vvBrHj+Tn2plp4Kd8/w15Ww3hudZPpU5Zs5s15mRgu5k16gVjf4J6hTD/Jd8MY676y1vVlzvvIN64r8dhb1vCe2swbdDXvsULePavY"\
7528"YwvAlbx91rLWVrgmvON6yATXm/MsLMvdAOI5zf4jrOQtvIc+3WF+VruBxHMz+5+wxg0iljvyrcGG+W4IeR2lwA1Fd6JNw5I4FHsnKXTDkziM+h4x3I1"\
7529"/Efj9fVgMfsd+UES5Xdzni8Gd3PWLqH8774BivreV/aOI72/WNobFnGVFtG0j+04h7d0AFoEFnCke19HeIpfFG7UZ5bN4z0bx6897N9X0cuZhEzgN36"\
7530"mkALNkiqX+Msn1I/WViYzPBNdTXuQdOc5lyljel8/x1hzN+/MZ0tOuk4wijWzA4J9K8D+lvf1PCf6ltLU0iPdvkO7Xngf/XIbw/r2H98r4fzD9eWf3Z"\
7531"u5GVv4foPOEnDEgMjAgMSAxICMzOQp4nHNn8GWIYmBgSGfIZchkSGaIZzCAQj0gLxMoms5QBZQHAHyqBfY="
7532    decompress_rle. r. $ir,$ir,1,1,5 if $ir>480 b. 0.2% fi >=. 40%
7533  fi
7534  nm. "[2D cupid shape]"
7535
7536#@cli shape_diamond : _size>=0
7537#@cli : Input a 2D diamond binary shape with specified size.
7538#@cli : Default value: 'size=512'.
7539#@cli : $ shape_diamond ,
7540+shape_diamond : check "${1=512}>=0"
7541  e[^-1] "Input a $1x$1 diamond binary shape."
7542  ir={round($1)}
7543  if !$ir 0
7544  elif $ir<2 $ir,$ir,1,1,1
7545  else
7546    {int($ir/2)+($ir%2)},{int($ir/2)+($ir%2)} =. 1,100%,100%
7547    distance. 1,1 <=. {i/2}
7548    +mirror. x
7549    if $ir>1" && "($ir%2) r. {w-1},100%,1,1,0,0,1 fi
7550    a[-2,-1] x +mirror. y
7551    if $ir>1" && "($ir%2) r. 100%,{h-1},1,1,0,0,0,1 fi
7552    a[-2,-1] y
7553  fi
7554  nm. "[2D diamond shape]"
7555
7556#@cli shape_dragon : _size>=0,_recursion_level>=0,_angle
7557#@cli : Input a 2D Dragon curve with specified size.
7558#@cli : Default value: 'size=512', 'recursion_level=18' and 'angle=0'.
7559#@cli : $ shape_dragon ,
7560+shape_dragon : check "${1=512}>=0 && ${2=18}>=0" skip "${3=0}"
7561  e[^-1] "Input a $1x$1 Dragon curve, with recursion level $2 and angle $3."
7562  ir={round($1)}
7563  if !$ir 0
7564  else l[]
7565    (0,1^0,0) ind=1
7566    repeat $2 +f "begin(C = I["$ind"]; R = rot(90°)); R*(I - C) + C" ind={2*h} a y done
7567    s c -.. {-2,ia} -. {ia} a c / {max(abs(im),abs(iM))}
7568    angle={$3-atan2(i1,i0)*180/pi} f. "begin(R = rot("$angle"°)); R*I"
7569    n 0,{$1-1} round $1,$1,1,1 eval.. "!x?polygon(#1,2,I(0,y),I(1,y),1,1); I" k.
7570  endl fi
7571  nm. "[2D dragon shape]"
7572
7573#@cli shape_fern : _size>=0,_density[%]>=0,_angle,0<=_opacity<=1,\
7574# _type={ 0=Asplenium adiantum-nigrum | 1=Thelypteridaceae }
7575#@cli : Input a 2D Barnsley fern with specified size.
7576#@cli : Default value: 'size=512', 'density=50%', 'angle=30', 'opacity=0.3' and 'type=0'.
7577#@cli : $ shape_fern ,
7578+shape_fern : check "${1=512}>=0 && ${2=50%}>=0 && isnum(${3=30}) && ${4=0.3}>=0 && $4<=1 && isnum(${5=0})"
7579  e[^-1] "Input a $1x$1 Barnsley fern, with density $2, angle $3 and opacity $4."
7580  ir={round($1)}
7581  if !$ir 0
7582  else l[]
7583    N={${"is_percent $2"}?$ir^2*$2:$2}
7584    $N,1,1,2
7585    eval "
7586     if ($5==0,
7587       # Type 0: Asplenium adiantum-nigrum.
7588       f1 = [ 0,0,0,0.16 ];           g1 = [ 0,0 ];
7589       f2 = [ 0.2,-0.26,0.23,0.22 ];  g2 = [ 0,1.6 ];
7590       f3 = [ -0.15,0.28,0.26,0.24 ]; g3 = [ 0,0.44 ];
7591       f4 = [ 0.85,0.04,-0.04,0.85 ]; g4 = [ 0,1.6 ],
7592
7593       # Type 1: Thelypteridaceae.
7594       f1 = [ 0,0,0,0.25 ];             g1 = [ 0,-0.4 ];
7595       f2 = [ 0.035,-0.2,0.16,0.04 ];   g2 = [ -0.09,0.02 ];
7596       f3 = [ -0.04,0.2,0.16,0.04 ];    g3 = [ 0.12,0.07 ];
7597       f4 = [ 0.95,0.005,-0.005,0.93 ]; g4 = [ -0.002,0.5 ];
7598     );
7599     xy = [ 0,0 ];
7600     repeat ("$N",n,
7601       r = u(100);
7602       xy = r<=1?((f1*xy)+=g1):
7603            r<=8?((f2*xy)+=g2):
7604            r<=15?((f3*xy)+=g3):((f4*xy)+=g4);
7605       I[n] = xy
7606    )"
7607    permute xczy
7608    i.. 2,2,1,1,{"R=rot(-$3°); R[2] = -R[2]; R[3] = -R[3]; R"} m*
7609    repeat 2 sh. $>,$>,0,0 -. {im} rm. done n 0,{$ir-1}
7610    pointcloud -$4 r $ir,$ir,1,1,0,0,0.5,0.5
7611  endl fi
7612  nm. "[2D Barnsley fern]"
7613
7614#@cli shape_gear : _size>=0,_nb_teeth>0,0<=_height_teeth<=100,0<=_offset_teeth<=100,0<=_inner_radius<=100
7615#@cli : Input a 2D gear binary shape with specified size.
7616#@cli : Default value: 'size=512', 'nb_teeth=12', 'height_teeth=20', 'offset_teeth=0' and 'inner_radius=40'.
7617#@cli : $ shape_gear ,
7618+shape_gear : check "${1=512}>=0 && isint(${2=12}) && $2>0 && ${3=20}>=0 && $3<=100 && ${4=0}>=0 && $4<=100 &&
7619                     ${5=40}>=0 && $5<=100"
7620  e[^-1] "Input a $1x$1 gear binary shape with $2 teeth, teeth height $3, teeth offset $4 and inner radius $5."
7621  ir={round($1)}
7622  if !$ir 0
7623  else l[]
7624    $ir,$ir
7625    polygon {"
7626      const nb_teeth = $2;
7627      const height_teeth = $3%;
7628      const offset_teeth = 2*$4%;
7629      ref(vector2048(),pts);
7630      for (i = 0, i<size(pts),
7631        a = i*2*pi/size(pts);
7632        r =  1 - height_teeth + height_teeth*(int(a*nb_teeth/pi + 2*nb_teeth - offset_teeth)%2);
7633        pts[i++] = round(w/2*(1 + r*cos(a)));
7634        pts[i++] = round(h/2*(1 + r*sin(a)));
7635      );
7636      [size(pts)/2,pts];
7637    "},1,1
7638    if $5 circle. 50%,50%,{0.5*w*$5%},1,0 fi
7639  endl fi
7640  nm. "[2D gear shape]"
7641
7642#@cli shape_heart : _size>=0
7643#@cli : Input a 2D heart binary shape with specified size.
7644#@cli : Default value: 'size=512'.
7645#@cli : $ shape_heart ,
7646+shape_heart : check "${1=512}>=0"
7647  e[^-1] "Input a $1x$1 heart binary shape."
7648  ir={round($1)}
7649  if !$ir 0
7650  else l[]
7651      2048,1,1,1,"t = x*2*pi/w; 16*sin(t)^3"
7652      2048,1,1,1,"t = x*2*pi/w; 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t)"
7653      a c display_parametric $ir,$ir,1,0,0,0
7654      flood 50%,50%,0,0,0,1,0 ==. 0
7655    endl
7656  fi
7657  nm. "[2D heart shape]"
7658
7659#@cli shape_polygon : _size>=0,_nb_vertices>=3,_angle
7660#@cli : Input a 2D polygonal binary shape with specified geometry.
7661#@cli : Default value: 'size=512', 'nb_vertices=5' and 'angle=0'.
7662#@cli : $ repeat 6 shape_polygon 256,{3+$>} done
7663+shape_polygon : check "${1=512}>=0 && isint(${2=5}) && $2>=3" skip ${3=0}
7664  e[^-1] "Input a $1x$1 polygon binary shape, with $2 vertices and angle $3 deg."
7665  ir={round($1)}
7666  if !$ir 0
7667  else l[]
7668      (0;{2*pi}) + {($3-90)*pi/180} r. 1,{$2+1},1,1,3 rows. 0,{h-2}
7669      ir2={round($ir/2)}
7670      +sin. cos..
7671      a x n 0,{$ir-1} s x +.. {-2,0.5*($ir-im-iM)} +. {-1,0.5*($ir-im-iM)} a x
7672      $ir,$ir polygon. $2,{-2,^},1,1 rm..
7673    endl
7674  fi
7675  nm. "[2D $2-polygon shape]"
7676
7677#@cli shape_snowflake : size>=0,0<=_nb_recursions<=6
7678#@cli : Input a 2D snowflake binary shape with specified size.
7679#@cli : Default values: 'size=512' and 'nb_recursions=5'.
7680#@cli : $ repeat 6 shape_snowflake 256,$> done
7681+shape_snowflake : check "${1=512}>=0 && isint(${2=5}) && $2>=0 && $2<=6"
7682  e[^-1] "Input a $1x$1 snowflake binary shape, with $2 recursions."
7683  ir={round($1)}
7684  if !$ir 0
7685  else l[]
7686    $ir,$ir (0;120;240) *. {pi/180} +sin. cos.. a[-2,-1] c
7687    repeat $2
7688      1,{4*h},1,2
7689      f.. "
7690        p0 = I;
7691        p1 = I[(y+1)%h];
7692        t = (p1 - p0)/3;
7693        pm = (p0 + p1)/2 - 0.866*[ -t[1], t[0] ];
7694        k = 4*y;
7695        I[#-1,k++] = p0;
7696        I[#-1,k++] = p0 + t;
7697        I[#-1,k++] = pm;
7698        I[#-1,k] = p1 - t;
7699      "
7700      rm..
7701    done
7702    *. {0.5*$1} +. {$1/2} permute. cyzx polygon.. {h},{^},1,1 rm.
7703  endl fi
7704  nm. "[2D snowflake shape]"
7705
7706#@cli shape_star : _size>=0,_nb_branches>0,0<=_thickness<=1
7707#@cli : Input a 2D star binary shape with specified size.
7708#@cli : Default values: 'size=512', 'nb_branches=5' and 'thickness=0.38'.
7709#@cli : $ repeat 9 shape_star 256,{$>+2} done
7710+shape_star : check "${1=512}>=0 && ${2=5}>0 && ${3=0.5}>=0 && $3<=1"
7711  e[^-1] "Input a $1x$1 star binary shape, with $2 branches and thickness $3."
7712  ir={round($1)}
7713  if !$ir 0
7714  else l[]
7715      star3d $2,$3 col3d 1 c3d n3d *3d $1,$1
7716      $1,$1 j3d. ..,50%,50%,0,1,2 rm..
7717    endl
7718  fi
7719  nm. "[2D star shape]"
7720
7721#@cli sh : eq. to 'shared'. : (+)
7722
7723#@cli shared : x0[%],x1[%],y[%],z[%],c[%] : y0[%],y1[%],z[%],c[%] : z0[%],z1[%],c[%] : c0[%],c1[%] : c0[%] : \
7724# (no arg) : (+)
7725#@cli : Insert shared buffers from (opt. points/rows/planes/channels of) selected images.
7726#@cli : Shared buffers cannot be returned by a command, nor a local environment.
7727#@cli : (eq. to 'sh').
7728#@cli : $ image.jpg shared 1 blur[-1] 3 remove[-1]
7729#@cli : $ image.jpg repeat s shared 25%,75%,0,$> mirror[-1] x remove[-1] done
7730#@cli : $$ https://gmic.eu/oldtutorial/_shared
7731
7732#@cli sp : eq. to 'sample'.
7733+sp : skip "${1=?}",${2=0}
7734  v + _sample[] "$1",${2--1} v -
7735  if !${} noarg fi
7736
7737#@cli sample : _name1={ ? | apples | balloons | barbara | boats | bottles | butterfly | cameraman | car | cat | \
7738# cliff | chick | colorful | david | dog | duck | eagle | elephant | earth | flower | fruits | gmicky | \
7739# gmicky_mahvin | gmicky_wilber | greece | gummy | house | inside | landscape | leaf | lena | leno | lion | \
7740# mandrill | monalisa | monkey | parrots | pencils | peppers | portrait0 | portrait1 | portrait2 | portrait3 | \
7741# portrait4 | portrait5 | portrait6 | portrait7 | portrait8 | portrait9 | roddy | rooster | rose | square | swan | \
7742# teddy | tiger | tulips | wall | waterfall | zelda },_name2,...,_nameN,_width={ >=0 | 0 (auto) },\
7743# _height = { >=0 | 0 (auto) } : (no arg)
7744#@cli : Input a new sample RGB image (opt. with specified size).
7745#@cli : (eq. to 'sp').\n
7746#@cli : Argument 'name' can be replaced by an integer which serves as a sample index.
7747#@cli : $ repeat 6 sample done
7748+sample : skip "${1=?}",${2=0}
7749  v + _sample[] "$1",${2--1} v -
7750  if !${} noarg fi
7751
7752__sample :
7753  u apples,balloons,barbara,boats,bottles,butterfly,cameraman,car,cat,chick,cliff,colorful,\
7754    david,dog,duck,eagle,elephant,earth,flower,fruits,gmicky,gmicky_mahvin,gmicky_wilber,greece,gummy,house,\
7755    inside,landscape,leaf,lena,leno,lion,mandrill,monalisa,monkey,parrots,pencils,peppers,\
7756    portrait0,portrait1,portrait2,portrait3,portrait4,portrait5,portrait6,portrait7,portrait8,portrait9,\
7757    roddy,rooster,rose,square,swan,teddy,tiger,tulips,wall,waterfall,zelda
7758
7759_sample :
7760  l[]
7761    if "$#>=3 && isnum($-2) && isint($-2) && $-2>=0 && isnum($-1) && isint($-1) && $-1>=0" # W and H specified.
7762      N={$#-2} W=$-2 H=$-1
7763    elif "$#>=2 && isnum($-1) && isint($-1) && $-1>=0" # Only W specified.
7764      N={$#-1} W=$-1 H=0
7765    else # No dimensions specified
7766      N={$#} W,H=0
7767    fi
7768  onfail N={$#} W,H=0
7769  endl
7770
7771  # Check validity of given arguments.
7772  $=arg
7773  samples=${-__sample}
7774  M=${"arg2var _sp_name",$samples}
7775  is_arg=1
7776  repeat $N
7777    arg=${arg{1+$>}} is_rand{1+$>}=0
7778    if ['$arg']=='?' arg={round(u(1,$M))} is_rand{1+$>}=1
7779    elif !isnum($arg)" || "!isint($arg)
7780      repeat $M name=${_sp_name{1+$>}}
7781        if '$name'!=0" && "'$name'==['$arg'] arg={1+$>} break fi
7782      done
7783    else arg={($arg%$M)+1} fi
7784    if !isnum($arg)" || "!isint($arg) is_arg=0 break fi
7785    arg{1+$>}=$arg
7786  done
7787  if !$is_arg N=1 W=0 H=0 arg1={round(u(1,$M))} fi
7788
7789  # Input sample images.
7790  repeat $N
7791    name=${_sp_name${arg{1+$>}}}
7792
7793    # Retrieve image data.
7794    l[] input_cached img/sample_$name.png
7795    onfail testimage2d {m=max($W,$H);m>0?m:400}
7796    endl
7797
7798    # Resize to desired dimensions.
7799    if $W>0" && "$H==0 r2dx. $W round.
7800    elif $W==0" && "$H>0 r2dy. $H round.
7801    elif $W>0" && "$H>0
7802      if w/$W>h/$H r2dy. $H else r2dx. $W fi
7803      r. $W,$H,1,100%,0,0,0.5,0.5 round.
7804    fi
7805    nm. $name
7806    e[0--4] "Input sample image '"{n}"' (1 image "{w}x{h}x{d}x{s}")."
7807  done
7808  +. 0 u $is_arg
7809
7810#@cli srand : value : (no arg) : (+)
7811#@cli : Set random generator seed.
7812#@cli : If no argument is specified, a random value is used as the random generator seed.
7813
7814#@cli store : _is_compressed={ 0 | 1 },variable_name1,_variable_name2,... : (+)
7815#@cli : Store selected images into one or several named variables.
7816#@cli : Selected images are transferred to the variables, and are so removed from the image list.
7817#@cli : (except if the prepended variant of the command '+store[selection]' is used).
7818#@cli : If a single variable name is specified, all images of the selection are assigned
7819#@cli : to the named variable. Otherwise, there must be as many variable names as images
7820#@cli : in the selection, and each selected image is assigned to each specified named variable.
7821#@cli : Use command `input $variable_name` to bring the stored images back in the list.
7822#@cli : Default value: 'is_compressed=0'.
7823#@cli : $ sample eagle,earth store img1,img2 input $img2 $img1
7824#@cli : $$
7825
7826#@cli testimage2d : _width>0,_height>0,_spectrum>0
7827#@cli : Input a 2D synthetic image.
7828#@cli : Default values: 'width=512', 'height=width' and 'spectrum=3'.
7829#@cli : $ testimage2d 512
7830+testimage2d : check "${1=512}>0 && ${2=$1}>0 && ${3=3}>0"
7831  e[^-1] "Input 2D synthetic image of size $1x$2x$3."
7832  Dmax2={0.15*min($1,$2)^2}
7833  $1,$2,1,$3,"
7834    X = x - w/2;
7835    Y = y - h/2;
7836    a = atan2(Y,X);
7837    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))))"
7838  polygon. 4,20%,20%,60%,20%,70%,70%,35%,45%,0.9,0,255,0
7839  torus3d {$1/7},{$1/20} r3d. 0,1,1,-80 col3d. 128,200,255
7840  j3d.. .,30%,70%,0,1,5,0,0 rm. round. 1
7841  nm. "[2D test image]"
7842
7843#@cli um : eq. to 'uncommand'.
7844um :
7845  v + uncommand $* v -
7846
7847#@cli uncommand : command_name[,_command_name2,...] : * : (+)
7848#@cli : Discard definition of specified custom commands.
7849#@cli : Set argument to '*' for discarding all existing custom commands.
7850#@cli : (eq. to 'um').
7851
7852#@cli uniform_distribution : nb_levels>=1,spectrum>=1
7853#@cli : Input set of uniformly distributed spectrum-d points in [0,1]^spectrum.
7854#@cli : $ uniform_distribution 64,3 * 255 +distribution3d circles3d[-1] 10
7855+uniform_distribution : check "isint($1) && $1>0 && isint($2) && $2>0"
7856  e[^1] "Input set of $1 uniformly distributed $2-d points in [0,1]^$2."
7857  l[]
7858    dim=$2
7859    (0;1^$1;0)
7860    repeat $dim
7861      0
7862      eval "
7863        repeat (da_size(#0),s,
7864          V = I[#0,s];
7865          N = V[size(V) - 1];    # Number of elts in Sk
7866          off = V[size(V) - 2];  # Position offset of elts in subsets of Sk
7867          M = round(N^(1/$dim)); # Number of subsets
7868          n = ceil(N/M);         # Number of max elts in subsets of Sk
7869          k0 = 0;
7870          repeat (M,k,
7871            k1 = round(lerp(0,N,(k + 1)/M));
7872            m = k1 - k0;
7873            V[size(V) - 1] = m;
7874            V[size(V) - 2] = (n - m)/2;
7875            da_push(#-1,[ k + off,V ]);
7876            k0 = k1;
7877          );
7878        )"
7879      rv[0,-1] rm. dim-=1
7880    done
7881    r {[1,da_size(),1,s-2]},0 s c n 0,1 a c transpose
7882  endl
7883
7884#@cli unserialize : : (+)
7885#@cli : Recreate lists of images from serialized image buffers, obtained with command 'serialize'.
7886
7887#@cli up : eq. to 'update'.
7888up :
7889  v + _update
7890
7891#@cli update
7892#@cli : Update commands from the latest definition file on the G'MIC server.
7893#@cli : (eq. to 'up').
7894update :
7895  v + _$0
7896
7897_update :
7898  out=${_path_rc}update$_version.gmic
7899  e[0--3] "Update command definition file '"{/$out}"' from the G\47MIC server."
7900  l[] cimgz:https://gmic.eu/update$_version.gmic
7901    if h>7" && "same([{^}],'#@gmic',6) ot $out fi
7902    rm
7903  onfail error[0--3] "Command 'update' : Unreachable update file."
7904  endl
7905  m $out
7906
7907# Function that inserts a new 'Update information' filter for a previous version number,
7908# passed as an argument.
7909update_deprecate : check "isint($1) && $1>0 && $1<999"
7910  e[^-1] "Deprecate filter updates for G'MIC version $1."
7911  l[]
7912  file=${_path_rc}update$1.gmic
7913  i cimgz:"https://gmic.eu/update$1.gmic",uchar
7914  +l. s +,{'"UPDATE"" INFORMATION"'} is_deprecated={$!>1} rm endl # Check if deprecation has not been already done.
7915  if $is_deprecated
7916    e[0--4] "  > Version $1 already deprecated -> Removing deprecation."
7917    off1={"ref(crop(),data); find(data,'\n#@gui !<b><i>&gt;&gt; UPDATE"" INFORMATION</i></b>')"}
7918    if $off1>0
7919      +rows $off1,100% rows[0] 0,{$off1-1}
7920      off2={"ref(crop(),data); find(data,'\n\n')"}
7921      if $off2>0
7922        rows. {$off2+2},100% a y
7923        o cimgz:$file,uchar
7924        _upload[] $file
7925        e[0--6] "  > Done! Version $1 is not deprecated anymore."
7926      fi
7927    fi
7928  else
7929    s +,{'"#@cli :: "'}
7930    i[1] ('"\n\
7931           \#@gui !<b><i>&gt;&gt; UPDATE"" INFORMATION</i></b> : _none_, _none_\n\
7932           \#@gui : note = note{\"A <b>new version</b> of the <i>G\47MIC</i> plug-in is available!\\n\\n\n\
7933           \#@gui : You are strongly encouraged to upgrade your version,
7934           by visiting our <i>Download page</i>:\\n\\n\"}\n\
7935           \#@gui : url = link{\"Visit G\47MIC Download Page\",\"https://gmic.eu/download.html\"}\n\
7936           \#@gui : note = note{\"\\nOf course, your plug-in will continue to work, but please note that we
7937           won\47t be able\n\
7938           \#@gui : to provide filter updates anymore for your current plug-in version.\\n\\n\n\
7939           \#@gui : Best regards,\\n\\n <i>The G\47MIC team.</i>\"}\n\n"')
7940    y[1] a y
7941    o cimgz:$file,uchar
7942    _upload[] $file
7943    e[0--4] "  > Done! Version $1 is now deprecated."
7944  fi
7945  rm endl
7946
7947# update_download_version.
7948# Update file versions on G'MIC download page, with specified version number.
7949# $1 = version number (e.g. '3.0.0').
7950update_download_version : check "
7951    is_digit(c)=(c>=_'0' && c<=_'9');
7952    ver = ['${1=undefined}'];
7953    size(ver)==5 && is_digit(ver[0]) && ver[1]==_'.' && is_digit(ver[2]) && ver[3]==_'.' && is_digit(ver[4])"
7954  e[^-1] "Replace version number on G'MIC download page to '$1'."
7955  rm filename=$HOME/work/src/gmic/html/download.html it[] $filename
7956  eval "
7957    is_digit(c) = (c>=_'0' && c<=_'9');
7958    ver = ['$1'];
7959    ref(crop(),data);
7960    p = stop = N = 0;
7961    while (!stop,
7962      p = find(data,'?dwl=gmic_',p);
7963      p<0?(stop=1):(
7964        str = data[p + 10,5];
7965        is_digit(str[0]) && str[1]==_'.' && is_digit(str[2]) && str[3]=='.' && is_digit(str[4])?(
7966          copy(data[p + 10],ver,size(ver));
7967          ++N;
7968        );
7969        ++p;
7970      );
7971    );
7972    p = find(data,'/source/gmic_',0);
7973    p>0?(
7974      str = data[p + 13,5];
7975      is_digit(str[0]) && str[1]==_'.' && is_digit(str[2]) && str[3]=='.' && is_digit(str[4])?(
7976        copy(data[p + 13],ver,size(ver));
7977        ++N;
7978      );
7979    );
7980    copy(i[0],data,size(data));
7981    echo('           -> ',N,' occurrences found.');
7982    "
7983  ot $filename
7984
7985# Generate images used in the G'MIC installer for Windows (InnoSetup).
7986update_instimg :
7987  sp gmicky,600
7988
7989  # Large image.
7990  +l
7991    r2dx 164
7992    0 t. "G'MIC-Qt for GIMP",0,0,96,1,1
7993    0 t. "(version "${-strver}")",0,0,64,1,1
7994    a[-2,-1] y,0.5 rows. -5,100%
7995    *. -1 n. 0,255 to_rgb. r2dx. {0,0.95*w}
7996    - 255 a y,0.5 + 255
7997    - 255 r 164,314,1,3,0,0,0.5,0.25 + 255
7998    o $HOME/work/src/gmic/resources/gmic_instimg.bmp
7999    rm
8000  endl
8001
8002  # Small image.
8003  r2dx 55 - 255 r 55,55,1,3,0,0,0.5,0.5 + 255
8004  o $HOME/work/src/gmic/resources/gmic_instimg_small.bmp
8005  rm
8006
8007# Generate Color Presets page for the G'MIC website.
8008# Input images : samples you want to generate
8009# $1 = upload to G'MIC server, can be { 0 | 1 }.
8010update_color_presets_html : check "isbool(${1=0})"
8011  use_vt100
8012  path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/color_presets/
8013  if ['$path_current']!=['$path_ok']
8014    error[0--3] "Command 'update_color_presets_html: Command run from wrong path: '"$path_current"', "\
8015                "should be '"$path_ok"'."
8016  fi
8017
8018  # Load default set of images if none selected.
8019  if !$!
8020    files ../img/color_presets_sample* files=${}
8021    repeat narg($files) arg 1+$>,$files ${} done
8022  fi
8023
8024  # Init variables.
8025  jpeg_quality=70   # Set JPEG quality of output images.
8026  thumb_width=180   # Set thumbnail width.
8027  thumb_height=90   # Set mini thumbnail height.
8028  categories=creative,picturefx,pixlsus,abigailgonzalez,alexjordan,berat,ericellerbrock,inavision,jtsemple,\
8029             kylerholland,ohadperetz,shamoonabbasi,youssefhossam,others,\
8030             bw,instant_consumer,instant_pro,fujixtransiii,negative_color,negative_new,negative_old,print,colorslide
8031  category_names="Creative Pack",\
8032                 "PictureFX",\
8033                 "PIXLS.US",\
8034                 "Abigail Gonzalez's CLUTs",\
8035                 "Alex Jordan's CLUTs",\
8036                 "Berat's CLUTs",\
8037                 "Eric Ellerbrock's CLUTs",\
8038                 "InAvision's CLUTs",\
8039                 "J.T. Semple's CLUTs",\
8040                 "Kyler Holland's CLUTs",\
8041                 "Ohad Peretz's CLUTs",\
8042                 "Shamoon Abbasi's CLUTs",\
8043                 "Youssef Hossam's CLUTs",\
8044                 "Others",\
8045                 "Films: Black and White",\
8046                 "Films: Instant [Consumer]",\
8047                 "Films: Instant [Pro]",\
8048                 "Films: Fuji XTrans III",\
8049                 "Films: Negative [Color]",\
8050                 "Films: Negative [New]",\
8051                 "Films: Negative [Old]",\
8052                 "Films: Print Films",\
8053                 "Films: Slide [Color]"
8054  category_authors="RawTherapee",\
8055                   "Marc Roovers",\
8056                   "PIXLS.US contributors",\
8057                   "Abigail Gonzalez",\
8058                   "Alex Jordan",\
8059                   "Berat",\
8060                   "Eric Ellerbrock",\
8061                   "InAvision",\
8062                   "J.T. Semple",\
8063                   "Kyler Holland",\
8064                   "Ohad Peretz",\
8065                   "Shamoon Abbasi",\
8066                   "Youssef Hossam",\
8067                   0,\
8068                   "Pat David",\
8069                   "Pat David",\
8070                   "Pat David",\
8071                   "Stuart Sowerby",\
8072                   "Pat David",\
8073                   "Pat David",\
8074                   "Pat David",\
8075                   "Juan Melara",\
8076                   "Pat David"
8077  category_authors_url="https://rawpedia.rawtherapee.com/Film_Simulation",\
8078                       "https://marcrphoto.wordpress.com/specials/698-2/",\
8079                       "https://discuss.pixls.us/t/help-to-create-a-set-of-pixls-us-color-luts",\
8080                       "https://www.abigailgonzalez.com/",\
8081                       "https://freshluts.com/users/1",\
8082                       "https://freshluts.com/users/10185",\
8083                       "https://www.facebook.com/eric.ellerbrockom",\
8084                       "https://freshluts.com/users/64078",\
8085                       "https://freshluts.com/users/120",\
8086                       "https://sellfy.com/p/SGnG/",\
8087                       "https://freshluts.com/users/992",\
8088                       "https://www.facebook.com/shamoon",\
8089                       "https://freshluts.com/users/52679",\
8090                       0,\
8091                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
8092                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
8093                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
8094                       "http://blog.sowerby.me/fuji-film-simulation-profiles",\
8095                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
8096                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
8097                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html",\
8098                       "http://juanmelara.com.au/print-film-emulation-luts-for-download",\
8099                       "https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html"
8100
8101  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"
8102  m "_title : ('\"$""*\"') replace_str. \"_\",\" \" replace_str. \"iii\",\"III\" f. \"(!y || j(0,-1)==32) &&
8103     i>=_'a' && i<=_'z'?uppercase(i):i\" u {t} rm."
8104
8105  nb_presets=0
8106  repeat narg($categories)
8107    category=${arg\ {1+$>},$categories}
8108    presets=${-_fx_cluts_$category}
8109    nb_presets+={narg({/$presets})}
8110  done
8111
8112  e[] "\n > Update 'Color Presets' pages, for "$!" image samples and "$nb_presets" presets."
8113
8114  # Prepare folder structure.
8115  e[] $_vt100_g"\n  * Prepare folder structure."$_vt100_n
8116  x "mkdir -p img"
8117
8118  # Generate thumbnails.
8119  e[] $_vt100_g"\n  * Generate thumbnails from samples.\n"$_vt100_n
8120  x "mkdir -p original"
8121  nb_samples=$!
8122  to_rgb repeat $nb_samples l[$<]
8123    nm[0] sample_{1+$<}
8124    basename={0,b} basename$<=$basename
8125    e[] "    - "$basename
8126    +r2dx $thumb_width
8127    +to[0] "Reference",4,{0,h-28},20,2 frame. 1,1,0
8128    o. original/$basename.jpg,$jpeg_quality rm.
8129    +_thumb[1] o. original/thumb_$basename.jpg,$jpeg_quality rm.
8130    +r2dy[0] $thumb_height r. {h},{h},1,100%,0,0,0.5,0.5 _thumb.
8131    o. original/minithumb_$basename.jpg,$jpeg_quality rm.
8132  endl done
8133
8134  # Generate color grading data and rendering on each input sample.
8135  ind_preset=0
8136  repeat narg($categories)
8137    category=${arg\ {1+$>},$categories}
8138    presets=${-_fx_cluts_$category}
8139
8140    e[] $_vt100_g"\n  * Category ""#"{1+$>}": "$_vt100_b$category"\n"$_vt100_n
8141    x "mkdir -p "$category
8142    x "mkdir -p "$category/clut
8143
8144    repeat narg({/$presets})
8145      preset=${arg\ {1+$>},$presets}
8146      e[] "    - "$preset
8147      clut $preset,64 to_rgb.
8148      s={sqrt(w*h*d)} +r. $s,$s,1,3,-1
8149      if iM<=255 *. 257 fi  # Force PNG to be saved in 16bits.
8150      o. $category/clut/$preset.png # Save as HaldCLUT
8151      rm.
8152
8153      +r3dx. 13
8154      o. $category/clut/$preset.cube # Save as .cube
8155      x 0,"rm -f "$category/clut/$preset.zip
8156      x 0,"zip -j -9 "$category/clut/$preset.zip" "$category/clut/$preset.cube
8157      rm.
8158
8159      repeat $nb_samples l[{2*$>},{2*$>+1},-1]
8160        basename={0,b}
8161        if !isfile(['{/$category/$basename/$preset.jpg}'])
8162          x "mkdir -p \""$category/$basename"\""
8163          +map_clut[^-1] .
8164          _title $preset
8165          to.. ${},4,{-2,h-28},20,2 frame.. 1,1,0
8166          o.. $category/$basename/$preset.jpg,$jpeg_quality
8167          _thumb. o. $category/$basename/thumb_$preset.jpg,$jpeg_quality
8168          rm[-2,-1]
8169        fi
8170      endl done
8171
8172      rm.
8173
8174      ind_preset+=1
8175    done
8176    x 0,"zip -rj9 "$category/${category}_cube.zip" "$category/clut/*.cube
8177    x 0,"zip -rj9 "$category/${category}_haldclut.zip" "$category/clut/*.png
8178  done
8179
8180  rm[1--1:2]  # Remove thumbnails.
8181
8182  # Generate html gallery code.
8183  e[] "\n  * Generate html code.\n"
8184
8185  repeat narg($categories)
8186    category=${arg\ {1+$>},$categories}
8187    arg {1+$>},$category_names
8188    category_name=${}
8189    presets=${-_fx_cluts_$category}
8190    x 0,"rm -f "$category/clut/"*.cube"
8191
8192    repeat $nb_samples
8193      width={$>,64+w} height={$>,64+h}
8194      basename={$>,b}
8195      e[] "    - "$category_name" / "$basename
8196
8197      ('"<!DOCTYPE html>"\n\
8198"<html lang=\"en\">"\n\
8199"  <head>"\n\
8200"    <meta charset=\"utf-8\">"\n\
8201"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
8202"    <link rel=\"stylesheet\" href=\"../highslide/highslide.css\"/>"\n\
8203"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
8204"- Color Presets</title>"\n\
8205"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
8206"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
8207"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
8208"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
8209"    <script src=\"../highslide/highslide-full.js\"></script>"\n\
8210"    <script>"\n\
8211"      hs.graphicsDir = '../highslide/graphics/';"\n\
8212"      hs.wrapperClassName = 'wide-border';"\n\
8213"      hs.showCredits = 'false';"\n\
8214"    </script>"\n\
8215"  </head>"\n\n\
8216"  <body>"\n\
8217"    <div id=\"include_header\"></div>"\n\n\
8218"    <div class=\"section_title\"><p>Color Presets</p></div><div class=\"section_content\">"\n\n\
8219"      <p>Among all features available in <span class=\"gmd_gmic\">G&apos;MIC</span>, our <b>Color Presets</b> and "\
8220"<b>Simulate Film</b> filters are able to apply various pre-defined <b>color CLUTs</b> on your images "\
8221"(<b>600+</b> color CLUTs available).</p>"\n\
8222"      <p>Below, you can navigate through the different proposed presets and see how they modify the colors "\
8223"of some sample images."\n\
8224"        You can also download each color preset separately as its corresponding "\
8225"<a href=\"http://www.quelsolaar.com/technology/clut.html\">HaldCLUT</a> file (in <b>.png</b> format), to use it in "\
8226" other software that support this feature.</p>"\n\n\
8227"      <p><b>Image credits:</b> Sample images below have been borrowed from "\
8228"<a href=\"https://www.flickr.com/photos/patdavid/\">Patrick David</a>, "\
8229"<a href=\"https://www.flickr.com/photos/davelau/\">Chi King</a>, "\
8230"<a href=\"https://pixabay.com/en/tulips-tulip-field-tulpenbluete-3359902/\">Capri23auto</a> "\
8231"and <a href=\"https://pixabay.com/en/girl-face-colorful-colors-artistic-2696947/\">ivanovgood</a>,"\n\
8232"        distributed a minima under "\
8233"<a href=\"https://creativecommons.org/licenses/by-sa/2.0/\">CC-by-SA 2.0</a>.</p>"\n\n\
8234"      <p class=\"colorpresets_disclaimer\"><b>Disclaimer:</b><br/>"\n\
8235"        The trademarked names which may appear in the filenames of the HaldCLUT images are there for "\
8236"informational purposes only. They serve only to inform the user which film stock the given HaldCLUT image"\n\
8237"        is designed to approximate. As there is no way to convey this information other than by using the "\
8238"trademarked name, we believe this constitutes fair use. Neither the publisher nor the authors are affiliated"\n\
8239"        with or endorsed by the companies that own the trademarks.</p>"\n\n\
8240"<!-- end_content -->"\n\n\
8241"    </div><div class=\"section_end\"></div>"\n\n\
8242"    <a name="browse"></a><div class=\"section_title\"><p>Browse</p></div><div class=\"section_content\">"\n\n\
8243"      <table><tr>"\n\
8244"          <td style=\"width: 400px\">Select category:"\n\
8245"            <ul>\n"')
8246      repeat narg($categories)
8247        _category=${arg\ {1+$>},$categories}
8248        arg {1+$>},$category_names
8249        _category_name=${}
8250
8251        arg {1+$>},$category_authors_url
8252        if isnum(${}) _category_author_url_start= _category_author_url_end=
8253        else _category_author_url_start="<a href=\""${}"\">" _category_author_url_end="</a>"
8254        fi
8255
8256        arg {1+$>},$category_authors
8257        if isnum(${}) _category_author=""
8258        else _category_author=$_category_author_url_start${}$_category_author_url_end
8259        fi
8260
8261        if narg($_category_author)
8262          _category_author="<span class=\"smaller\">(by "$_category_author")</span>"
8263        fi
8264
8265        if ['$_category']==['$category']
8266          ('"              <li><span class=\"gmd_bold_a\"><b>"$_category_name"</b> "\
8267            $_category_author"</span></li>"\n')
8268        else
8269          ('"              <li><a href=\""${_category}_$basename.html"#browse\">"$_category_name"</a> "\
8270            $_category_author"</li>\n"')
8271        fi
8272      done
8273
8274      ('"            </ul>\n"\
8275        "          </td>"\n\
8276        "          <td>Select sample image:<br/><br/>\n"')
8277      repeat $nb_samples
8278        _basename=${basename$>}
8279        if ['$_basename']==['$basename']
8280          ('"            <a href=\""${category}_$_basename.html"#browse\">"\
8281            "<img alt=\"\" style=\"border: 3px solid \#cc7700\" "\
8282            "src=\""original/minithumb_$_basename.jpg"\"/></a>\n"')
8283        else
8284          ('"            <a href=\""${category}_$_basename.html"#browse\"><img alt=\"\" "\
8285            "src=\""original/minithumb_$_basename.jpg"\"/></a>\n"')
8286        fi
8287      done
8288      ('"          </td>"\n\
8289        "      </tr></table>"\n\
8290        "    </div><div class=\"section_end\"></div>"\n\n')
8291      ('"    <div class=\"section_title\"><p>Presets</p></div><div class=\"section_content\">"\n\n\
8292        "      <table class=\"colorpresets_table\">"\n\
8293        "        <tr><td>"\n\
8294        "            <div><a href=\"original/"$basename".jpg\" class=\"highslide\" "\
8295        "onclick=\"return hs.expand(this)\"><img alt=\"\" "\
8296        "src=\"original/thumb_"$basename".jpg\" /></a>"\n\
8297        "              <div class=\"highslide-caption\"><b>Reference Image</b></div></div>"\n\
8298        "        </td><td colspan=\"3\"></td></tr>"\n\
8299        "        <tr><td><b>Reference Image</b></td><td colspan=\"3\"></td></tr>"\n\n')
8300
8301      repeat narg({/$presets})
8302        preset=${arg\ {1+$>},$presets}
8303
8304        ('$preset') replace_seq. 39,"92,92,39" preset_esc={t} rm.
8305        _title $preset title=${}
8306        if $>%4==0 if $> ('"        </tr>\n"') fi ('"        <tr>\n"') fi
8307
8308        ('"          <td><table>"\n\
8309          "              <tr><td>"\n\
8310          "                  <div><a href=\"#\" onclick=\"return hs.htmlExpand(this, "\
8311          "{ width: "$width", height: "$height" })\">"\n\
8312          "                      <img alt=\"\" src=\""$category/$basename/thumb_$preset".jpg\" /></a>"\n\
8313          "                    <div class=\"highslide-maincontent\" style=\"text-align: center\">"\n\
8314          "                      <img alt=\"\" src=\""$category/$basename/$preset.jpg"\" "\
8315          "onclick=\"hs.close()\" "\
8316          "onmouseover=\"javascript:this.src='"$category/$basename/$preset_esc.jpg"';\" "\
8317          "onmouseout=\"javascript:this.src='original/"$basename".jpg';\" /></div>"\n\
8318          "                    <div class=\"highslide-caption\"><b>Preset:</b> <i>"$title"</i> "\
8319          "[<a href=\""$category/clut/$preset.png"\">Download .png HaldCLUT</a>] "\
8320          "or [<a href=\""$category/clut/$preset.zip"\">Download .cube</a>]"\
8321          "</div></div>"\n\
8322          "              </td></tr>"\n\
8323          "              <tr><td><span class=\"smaller\">"$title"</span></td></tr>"\n\
8324          "          </table></td>\n\n"')
8325      done
8326      r={narg({/$presets})%4}
8327      if $r%4 ('"          <td colspan=\""{4-$r}"\"></td>\n"') fi
8328      ('"      </tr></table>"\n\n\
8329        "      <hr/><p style=\"text-align: center\">"\n\
8330        "        <a href=\""$category/${category}_cube.zip"\"><img src=\"../img/menu_download.png\" /></a>&nbsp; "\
8331        "<span class=\"gmd_bold_a\">Get the Full Pack &quot;<i>"${category_name}"</i>&quot;:</span> "\
8332        "[<b><a href=\""$category/${category}_haldclut.zip"\">Download .png HaldCLUT</a></b>] "\
8333        "or [<b><a href=\""$category/${category}_cube.zip"\">Download .cube</a></b>]"\
8334        "&nbsp; <a href=\""$category/${category}_cube.zip"\"><img src=\"../img/menu_download.png\" /></a>"\n\
8335        "      </p><hr/>"\n\
8336        "<!-- end_content -->"\n\n\
8337        "    </div><div class=\"section_end\"></div>"\n\
8338        "    <div id=\"include_footer\"></div>"\n\
8339        "  </body>"')
8340      a[$nb_samples--1] x ot. ${category}_$basename.html rm.
8341    done
8342
8343  done
8344
8345  # All done, exiting.
8346  rm
8347  category=${arg\ 1,$categories}
8348  x "ln -fs "${category}_$basename0.html" index.html"
8349  if $1
8350    e[] "\n  * Transfer color presets on G'MIC server.\n"
8351    x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -eRL . /www/gmic/color_presets ; quit\""
8352  fi
8353  e[] "\n > All done, for "$nb_presets" presets.\n"
8354
8355# Generate gallery page for the G'MIC website.
8356# $1 = upload to G'MIC server, can be { 0 | 1 }.
8357update_gallery_html : check "isbool(${1=0})"
8358  e[^-1] "Generate gallery pages for the G'MIC website."
8359  use_vt100
8360  path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/gallery/
8361  if ['$path_current']!=['$path_ok']
8362    error[0--3] "Command 'update_gallery_html: Command run from wrong path: '"$path_current"', "\
8363                "should be '"$path_ok"'."
8364  fi
8365  x "rm -rf img && mkdir -p img"
8366
8367  # Define categories and sample pictures.
8368  categories=arrays,artistic,blackandwhite,colors,deformations,filtering,patterns,3dmeshes,stylization,codesamples
8369  nb_categories={narg($categories)}
8370  commands_arrays=_gallery_arrays,array,array_fade,array_mirror,frame_blur,frame_cube,frame_painting,img2ascii,\
8371                  imagegrid,rotate_tiles,vignette,tunnel
8372  commands_artistic=_gallery_artistic,boxfitting,cartoon,cubism,draw_whirl,fractalize,halftone,sketchbw,light_relief,\
8373                    mosaic,linify,polaroid,polygonize,poster_hope,rodilius,stencil,stained_glass
8374  commands_blackandwhite=_gallery_blackandwhite,pencilbw,old_photo,sepia
8375  commands_colors=_gallery_colors,solarize,hsv2rgb,transfer_rgb
8376  commands_deformations=_gallery_deformations,deform,map_sphere,seamcarve,spherize,twirl,warp_perspective,water,wave
8377  commands_filtering=_gallery_filtering,blur_angular,blur_linear,blur_radial,glow,smooth,bilateral,dog,deriche,\
8378                     distance,gradient_norm,normalize_local,sharpen,solidify
8379  commands_patterns=_gallery_patterns,tsp,chessboard,mandelbrot,maze_mask,plasma,shape_fern,shape_snowflake,\
8380                    sierpinski,syntexturize_matchpatch,truchet,turbulence,watermark_visible,weave
8381  commands_3dmeshes=_gallery_3dmeshes,add3d,distribution3d,elevation3d,gmic3d,imageblocks3d,imagerubik3d,\
8382                    skeleton3d,spherical3d,tensors3d,text3d,torus3d,weird3d
8383  commands_stylization=_gallery_stylization
8384  commands_codesamples=_gallery_codesamples
8385
8386  pics=apples,bottles,butterfly,car,cat,cliff,david,dog,duck,eagle,elephant,earth,flower,fruits,greece,gummy,inside,\
8387       landscape,leaf,leno,lion,mandrill,monalisa,parrots,pencils,rooster,rose,square,teddy,tiger,wall,waterfall,zelda
8388
8389  # Generate html pages.
8390  it[] $HOME/work/src/gmic/src/gmic_stdlib.gmic
8391  merge_multiline_comments. s -,10
8392  ('\n') a[0--3] .,y rm. a y
8393  nb_cols=3
8394
8395  repeat $nb_categories,ncat
8396    category=${arg\ 1+$>,$categories}
8397    if isfile('{/$category.html}') continue fi
8398    e[] "\n  * Process category '"$_vt100_b$category$_vt100_n"'."
8399
8400    commands=${commands_$category}
8401    nb_commands={narg($commands)}
8402    col,nex=0
8403
8404    repeat $nb_categories
8405      cat=${arg\ 1+$>,$categories}
8406      if '$cat'=='$category' td_$cat="<td style=\"background-color: \#dddddd\">"
8407      else td_$cat="<td>"
8408      fi
8409    done
8410
8411  html="<!DOCTYPE html>"\n\
8412"<html lang=\"en\">"\n\
8413"  <head>"\n\
8414"    <meta charset=\"utf-8\">"\n\
8415"    <link rel=\"stylesheet\" href=\"../style.css\">"\n\
8416"    <link rel=\"stylesheet\" href=\"../highslide/highslide.css\"/>"\n\
8417"    <link rel=\"stylesheet\" href=\"https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css\">"\n\n\
8418"    <title>G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\
8419"- Gallery</title>"\n\
8420"    <script src=\"../jquery-3.5.1.min.js\"></script>"\n\
8421"    <script>var jQuery_3_5_1 = $.noConflict(true);</script>"\n\
8422"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_header\").load(\"../header1.html\"); });</script>"\n\
8423"    <script>jQuery_3_5_1(function(){ jQuery_3_5_1(\"#include_footer\").load(\"../footer.html\"); });</script>"\n\n\
8424"    <script src=\"https://code.jquery.com/jquery-1.12.4.js\"></script>"\n\
8425"    <script src=\"https://code.jquery.com/ui/1.12.1/jquery-ui.js\"></script>"\n\n\
8426"    <script src=\"../highslide/highslide-full.js\"></script>"\n\
8427"    <script>"\n\
8428"      hs.graphicsDir = '../highslide/graphics/';"\n\
8429"      hs.wrapperClassName = 'wide-border';"\n\
8430"      hs.showCredits = 'false';"\n\
8431"    </script>"\n\
8432"  </head>"\n\n\
8433"  <body>"\n\
8434"    <div id=\"include_header\"></div>"\n\n\
8435"    <div class=\"section_title\"><p>Image Gallery</p></div><div class=\"section_content\">"\n\n\
8436"      <p>This gallery gives a quick overview of the kind of features and generic filters available in the "\
8437"<span class=\"gmd_gmic\">G&apos;MIC</span> open-source image processing framework.</p>"\n\
8438"      <p>All the images below have been processed by the CLI interface "\
8439"<samp><a href=\"reference/\">gmic</a></samp>"\
8440"  of G&apos;MIC, from a set of initial 2D color images."\n\
8441"        Click on an image to enlarge it and display the G&apos;MIC command-line "\
8442"used for the processing (<i>note: to reproduce this, you may have to escape some characters, "\
8443"according to the type of shell you use!</i>).</p>"\n\
8444"      <p>Remember, G&apos;MIC lets you define your own image pipelines through"\
8445"custom <a href=\"https://gmic.eu/gmic_stdlib.gmic\">command files</a>."\n\
8446"        Your custom filters can be easily added afterwards in the plug-in for "\
8447"<a href=\"http://www.gimp.org\">GIMP</a> or <a href=\"http://www.krita.org\">Krita</a>.</p>"\n\
8448"      <p>For more details, visit the <a href=\"https://gmic.eu/tutorial\">tutorial pages</a> as well as the "\
8449"<a href=\"https://gmic.eu/reference/\">technical reference</a> to get a full documentation on this "\
8450"software.</p>"\n
8451
8452  html_menu="      <table class=\"gallery_navigation\"><tr>\n          "\
8453            $td_arrays"<a href=\"arrays.html#menu\">Arrays &amp; Frames</a></td>"\
8454            $td_artistic"<a href=\"artistic.html#menu\">Artistic</a></td>"\
8455            $td_blackandwhite"<a href=\"blackandwhite.html#menu\">B&amp;W</a></td>"\
8456            $td_colors"<a href=\"colors.html#menu\">Colors</a></td>"\
8457            $td_deformations"<a href=\"deformations.html#menu\">Deformations</a></td>"\
8458            $td_filtering"<a href=\"filtering.html#menu\">Filtering</a></td>"\
8459            $td_patterns"<a href=\"patterns.html#menu\">Patterns</a></td>"\
8460            $td_3dmeshes"<a href=\"3dmeshes.html#menu\">3D Meshes</a></td>"\
8461            $td_stylization"<a href=\"stylization.html#menu\">Stylization</a></td>"\
8462            $td_codesamples"<a href=\"codesamples.html#menu\">Code samples</a></td>"\n\
8463            "      </tr></table><br/>\n\n"
8464
8465  html=${html}${html_menu}"      <table class=\"gallery_table\">\n"
8466
8467  repeat $nb_commands
8468    command=${"arg "1+$>,$commands}
8469      is_stylization={['$command']=='_gallery_stylization'}
8470
8471      e[] $_vt100_m"    - Command '"$_vt100_b$command$_vt100_n"' ["{1+$>}/$nb_commands"]."
8472      +l # Parse command definition and examples
8473        s -,{'"#@cli "$command" :"'}
8474        if $!<2 s -,{'"#@cli "$command"\n"'} i[1] ('\n') a[-2,-1] y fi
8475        if $!<2 warn[] "    ** Command '"$command"' not found! **"
8476        else
8477          k.
8478          eval "
8479            ref(crop(#-1),str);
8480            ind = 0;
8481            while ((ind = find(str,'\n#@cli ',ind))>=0,
8482              ++ind;
8483              str[ind+6]!=_':' ? break() :
8484              str[ind+7]==_' ' && str[ind+8]==_'$' && str[ind+9]==_' '?(
8485                beg = ind + 10;
8486                end = find(str,_'\n',beg) - 1;
8487                ref(vector1024(0),com);
8488                run('+rows[0] ',beg,',',end);
8489              );
8490            );
8491          ";
8492          rm[0]
8493
8494          nb_examples=$!
8495          repeat $nb_examples # For each example found
8496            example$nex={$>,t}
8497
8498            e[] $_vt100_g"        $ "${example$nex}$_vt100_n
8499            sample=${"arg 1+"{($nex+8*$ncat)%narg($pics)},$pics}
8500            is_codesample=0
8501
8502            l[] # Create a html representation of the command
8503              ('${example$nex}') y
8504              is_input={"find(#-1,'image.jpg')>=0"}
8505
8506              if !$is_input" && find(#-1,'sample ')>=0"
8507                 s +,{'"sample "'}
8508                 if {0,crop()=='"sample "'" && "$!>1} l[1]
8509                   s +,{'" "'}
8510                   if "find(#0,_',')==-1" sample={0,t} is_input=1 fi
8511                   a y
8512                 endl fi
8513                 a y
8514              fi
8515              replace_str. "image.jpg",\
8516                           "<a href=\"../img/sample_"$sample".png\" target=\"_blank\">"$sample".png</a>"
8517              replace_str. "_output_mode=1",""
8518              # Discard '_fps=?' and '_label=?'
8519              l. s +,{'" "'} repeat $! if {$<,crop(0,0,1,5)=='_fps='||crop(0,0,1,7)=='_label='}
8520                rm[$<]
8521              fi done a y endl
8522              l. s +,{'" "'} repeat $! if {$<,crop(0,0,1,8)=='https://'} l[$<] # Add hyperlink
8523                if crop(0,0,1,24)=='https://gmic.eu/samples/'
8524                  is_codesample=1
8525                  basename_codesample={`crop(0,24,1,h-24)`}
8526                  filename_codesample="../../resources/samples"/$basename_codesample
8527                  url_codesample="https://gmic.eu/samples/"$basename_codesample
8528                  if $1 x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"put -O /www/gmic/samples \\\""\
8529                          $filename_codesample"\\\"; quit\" >/dev/null" fi
8530                  i[0] ('"<a onclick=\"javascript:$( '\#dialog"$nex"' ).dialog( 'open' );\"
8531                          style=\"cursor: pointer;\">"')
8532                else i[0] ('"<a href=\""{$<,t}"\" target=\"_blank\">"')
8533                fi
8534                ('</a>') y a y
8535              endl fi done a y endl
8536              replaced_example$nex={t} rm
8537            endl
8538            ('${example$nex}') replace_str. "https://gmic.eu/samples/","../../resources/samples/" example$nex={t} rm.
8539            m "_run : _preview_width,_preview_height=450,300 "${example$nex}
8540
8541            if $is_input sample_=${sample}_ else sample_= fi
8542            filename_original=img/${category}_${sample_}original_$nex.jpg
8543            filename_thumb_original=img/${category}_${sample_}thumb_original_$nex.jpg
8544            if "find(['"${example$nex}"'],' _fps=')>=0"
8545              filename_full=img/${category}_${sample_}full_$nex.gif
8546              filename_thumb=img/${category}_${sample_}thumb_$nex.gif
8547              _is_animated=1
8548            else
8549              filename_full=img/${category}_${sample_}full_$nex.jpg
8550              filename_thumb=img/${category}_${sample_}thumb_$nex.jpg
8551              _is_animated=0
8552            fi
8553
8554            etime=
8555            _label=
8556            if !isfile('{/$filename_thumb}') l[]
8557              if $is_input sp $sample,600 o image.jpg rm fi
8558
8559              db3d m3d md3d f3d l3d sl3d ss3d 0.8 srand 512
8560              etime=$| _run etime={_round($|-$etime,0.01)}
8561
8562              if $is_stylization
8563                +l _gallery o $filename_full,70 rm endl k.
8564                _gallery
8565                width,height={[w,h]}
8566              else
8567                _gallery
8568                width,height={[w,h]}
8569                if $_is_animated o $filename_full,$_fps else o $filename_full,70 fi
8570              fi
8571              e[] "\r"$_vt100_g"        $ "${example$nex}" (done in "$_vt100_n${etime}"s"$_vt100_g")."$_vt100_n
8572
8573              crop 5,5,{w-6},{h-6} # Remove frame added by '_gallery'
8574              frame 3%,3%,255 rr2d 440,440,0,3 drop_shadow 2,2,2
8575              if $_is_animated rr2d 230,230,0,3 else rr2d 300,300,0,3 fi
8576              100%,100%,1,3,245 blend[^-1] .,alpha,1,1 rm.
8577              if $_is_animated o $filename_thumb,$_fps else o $filename_thumb,60 fi
8578              rm
8579
8580              if $is_input
8581                image.jpg _gallery
8582                rr2d $width,$height,0,5 c 0,255
8583                to "Input",2%,2%,6%
8584                - 255 r $width,$height,1,3,0,0,0.5,0.5 + 255
8585                o $filename_original,60
8586
8587                crop 5,5,{w-6},{h-6} # Remove frame added by '_gallery'
8588                frame 3%,3%,255 rr2d 440,440,0,3 drop_shadow 2,2,2
8589                if $_is_animated rr2d 230,230,0,3 else rr2d 300,300,0,3 fi
8590                100%,100%,1,3,245 blend[^-1] .,alpha,1,1 rm.
8591                o $filename_thumb_original,60
8592                rm
8593              fi
8594            endl fi
8595
8596            is_samesize=0
8597            l[]
8598              $filename_full
8599              width,height={round([w,h]*(max(w,h)<300?1.75:1))}
8600              if isfile(['{/${filename}_original}']) $filename_original is_samesize={w==w#0" && "h==h#0} fi
8601              rm
8602            endl
8603            if !$is_samesize filename_original=$filename_full fi
8604            if !($col%$nb_cols) html=${html}"        <tr>\n" fi
8605            if $nb_examples==1 counter=
8606            else counter=" <span class=\"gallery_caption\">"[{$>+1}/$nb_examples]"</span>"
8607            fi
8608            html_etime=
8609            if narg($etime) html_etime="<br/><span class=\"gallery_caption\">(generated in "${etime}"s)</span>" fi
8610
8611            html_codesample=
8612            if $is_codesample
8613               html_codesample="\n                  <div id=\"dialog"$nex"\" title=\""samples/$basename_codesample"\">"\
8614                               "\n                    <pre class>\n"
8615               it[] $url_codesample html_codesample=${html_codesample}{t} rm.
8616               html_codesample=${html_codesample}"\n</pre></div>\n"\
8617                               "<br/><script>$( function() { $( \"#dialog"$nex"\" )."\
8618                               "dialog({ autoOpen: false, width:600 }); } );</script>\n
8619                                <a onclick=\"javascript:$( '#dialog"$nex"' ).dialog( 'open' );\"
8620                                style=\"cursor: pointer;\"><span class=\"gallery_caption\">[ Source code ]</p></a>\n"
8621            fi
8622            if ['$_label']==0 _label=$command$counter else ('$_label') replace_str. "~"," " _label={t} rm. fi
8623            if $is_input
8624              html=${html}\
8625                  "          <td><div><a href=\"#\" onclick=\"return hs.htmlExpand(this, { width: "{$width+64}","\
8626                  "height: "{$height+64}" })\">\n"\
8627                  "                <img alt=\""gallery_$command$nex"\" src=\""$filename_thumb"\" "\
8628                  "onmouseover=\"javascript:this.src='"$filename_thumb_original"';\"
8629                  onmouseout=\"javascript:this.src='"$filename_thumb"';\"/><br/><b>"\
8630                  ${_label}${html_codesample}"</b></a>\n"\
8631                  "              <div class=\"highslide-maincontent\">\n"\
8632                  "                <img alt=\""gallery_$command$nex"\" width=\""$width"\" "\
8633                  "src=\""$filename_full"\"
8634                  onclick=\"hs.close()\" onmouseover=\"javascript:this.src='"$filename_full"';\"
8635                  onmouseout=\"javascript:this.src='"$filename_original"';\"/>\n"\
8636                  "              </div>\n"\
8637                  "              <div class=\"highslide-caption\">Command: "\
8638                  "<samp>$ gmic "${replaced_example$nex}"</samp>"\
8639                  ${html_etime}"</div></div></td>\n"
8640            else
8641              html=${html}\
8642                  "          <td><div><a href=\"#\" onclick=\"return hs.htmlExpand(this, { width: "{$width+64}","\
8643                  "height: "{$height+64}" })\">\n"\
8644                  "                <img alt=\""gallery_$command$nex"\" src=\""$filename_thumb"\" />"\
8645                  "<br/><b>"\
8646                  ${_label}${html_codesample}"</b></a>\n"\
8647                  "              <div class=\"highslide-maincontent\">\n"\
8648                  "                <img alt=\""gallery_$command$nex"\" width=\""$width"\" "\
8649                  "src=\""$filename_full"\" onclick=\"hs.close()\" />\n"\
8650                  "              </div>\n"\
8651                  "              <div class=\"highslide-caption\">Command: "\
8652                  "<samp>$ gmic "${replaced_example$nex}"</samp>"\
8653                  ${html_etime}"</div></div></td>\n"
8654            fi
8655            nex+=1
8656            col={($col+1)%$nb_cols}
8657            if !$col html=${html}"        </tr>\n" fi
8658          done
8659        fi
8660        rm
8661      endl
8662    done
8663    if $col html=${html}"          <td colspan=\""{$nb_cols-$col}"\"></td></tr>\n" fi
8664    html=${html}"      </table><br/>\n"\
8665         ${html_menu}\
8666         "    </div><div class=\"section_end\"></div>"\n"    <div id=\"include_footer\"></div>"\n"  </body>"
8667    ('$html') ot. $category.html rm.
8668  done
8669  rm
8670
8671  # Transfer on G'MIC server
8672  x "ln -fs artistic.html index.html"
8673  if $1
8674    e[] "\n  * Transfer gallery on G'MIC server.\n"
8675    x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -eRL . /www/gmic/gallery ; quit\""
8676  fi
8677  e[] "  * All done!\n"
8678
8679# Generate a single image from a list of images, for the gallery.
8680_gallery :
8681  repeat $! l[$>] W$>={w} H$>={h} D$>={d} S$>={s} IS_3D$>=${-_is_3d} endl done
8682  repeat $! l[$>]
8683    if ${IS_3D$>} r3d 1,1,0,-80 r3d 0,1,0,80 snapshot3d 400
8684    else if w>8192 z 0,8191 elif h>8192 rows 0,8191 fi n 0,255
8685    fi
8686  endl done
8687
8688  if !$_is_animated
8689    +__gallery
8690    if w>1024 r={round(1024*100/w,0.1)} r[^-1] $r%,$r%,1,100%,2 fi rm.
8691  fi
8692
8693  repeat $! l[$>]
8694    if s==1 r {w},{h},1,3
8695    elif s==4 drgba
8696    else r {w},{h},1,3,0 fi
8697    if w<=h" && "h<256 r2dy 256,2 elif h<=w" && "w<256 r2dx 256,2 fi
8698    if w<=h" && "h>620 r2dy 620,2 elif h<=w" && "w>620 r2dx 620,2 fi
8699    if h<48 r 100%,48 fi
8700    if w<48 r 48,100% fi
8701    if $_is_animated" && "(w>480" || "h>480) rr2d 480,480,0,2 fi
8702    frame 1,1,0 frame 4,4,255
8703  endl done
8704
8705  if $_is_animated
8706    - 255 r ${-max_wh},1,3,0,0,0.5,0.5 + 255
8707  else
8708    - 255 __gallery + 255
8709    if w<256 - 255 r 256,100%,1,100%,0,0,0.5,0.5 + 255 fi
8710    if h<256 - 255 r 100%,256,1,100%,0,0,0.5,0.5 + 255 fi
8711  fi
8712
8713__gallery :
8714  if $!==2 if w>h a y else a x fi
8715  else montage A # append_tiles 2
8716  fi
8717
8718# Generate data files for the G'MIC Online website.
8719update_gmicol :
8720  e[^-1] "Generate XML file and thumbnails for the G'MIC Online website."
8721  filename=$_path_rc/update$_version.gmic
8722  if !isfile('$filename') up fi
8723  rm it $filename
8724
8725  # Generate XML file.
8726  m "parse_gui_trigger_gmicol : _update_gmicol $*"
8727  v + +parse_gui. gmicol v -
8728  um parse_gui_trigger_gmicol
8729  ot. gmicol.xml
8730  rm.
8731
8732  # Generate thumbnails.
8733  m "parse_gui_trigger_thumbnails : _update_gmicol $*"
8734  v + w[] parse_gui thumbnails v -
8735  um parse_gui_trigger_thumbnails
8736
8737_update_gmicol : # Keep only a subset of existing filters
8738  nmd 3,\
8739  "About/♥ Support Us ! ♥",\
8740  "About/About G'MIC",\
8741  "About/Friends Hall of Fame",\
8742  "Arrays & Tiles/Annular Steiner Chain Round Tile",\
8743  "Arrays & Tiles/Array [Faded]",\
8744  "Arrays & Tiles/Array [Mirrored]",\
8745  "Arrays & Tiles/Array [Random Colors]",\
8746  "Arrays & Tiles/Array [Random]",\
8747  "Arrays & Tiles/Array [Regular]",\
8748  "Arrays & Tiles/Ascii Art",\
8749  "Arrays & Tiles/Chessboard",\
8750  "Arrays & Tiles/Dices",\
8751  "Arrays & Tiles/Grid [Cartesian]",\
8752  "Arrays & Tiles/Grid [Hexagonal]",\
8753  "Arrays & Tiles/Grid [Triangular]",\
8754  "Arrays & Tiles/Ministeck",\
8755  "Arrays & Tiles/Puzzle",\
8756  "Arrays & Tiles/Taquin",\
8757  "Arrays & Tiles/Tiled Isolation",\
8758  "Arrays & Tiles/Tiled Normalization",\
8759  "Arrays & Tiles/Tiled Parameterization",\
8760  "Arrays & Tiles/Tiled Random Shifts",\
8761  "Arrays & Tiles/Tiled Rotation",\
8762  "Artistic/Angoisse Anguish",\
8763  "Artistic/Aurora",\
8764  "Artistic/Blockism",\
8765  "Artistic/Bokeh",\
8766  "Artistic/Cartoon",\
8767  "Artistic/Circle Abstraction",\
8768  "Artistic/Color Abstraction Paint",\
8769  "Artistic/Colored Pencils",\
8770  "Artistic/Comic Book",\
8771  "Artistic/Cubism",\
8772  "Artistic/Cutout",\
8773  "Artistic/Diffusion Tensors",\
8774  "Artistic/Dream Smoothing",\
8775  "Artistic/Ellipsionism",\
8776  "Artistic/Felt Pen",\
8777  "Artistic/Finger Paint",\
8778  "Artistic/Fractalize",\
8779  "Artistic/Ghost",\
8780  "Artistic/Graphic Boost",\
8781  "Artistic/Graphic Novel",\
8782  "Artistic/Hard Sketch",\
8783  "Artistic/Highlight Bloom",\
8784  "Artistic/Hope Poster",\
8785  "Artistic/Hough Sketch",\
8786  "Artistic/Illustration Look",\
8787  "Artistic/Kuwahara",\
8788  "Artistic/Lylejk's Painting",\
8789  "Artistic/Make Squiggly",\
8790  "Artistic/Morphology Painting",\
8791  "Artistic/Paint With Brush",\
8792  "Artistic/Painting",\
8793  "Artistic/Pen Drawing",\
8794  "Artistic/Photoillustration",\
8795  "Artistic/Polygonize [Delaunay]",\
8796  "Artistic/Polygonize [Energy]",\
8797  "Artistic/Poster Edges",\
8798  "Artistic/Posterize",\
8799  "Artistic/Posterized Dithering",\
8800  "Artistic/Quadtree Variations",\
8801  "Artistic/Rodilius",\
8802  "Artistic/Sharp Abstract",\
8803  "Artistic/Simple Noise Canvas",\
8804  "Artistic/Skeletik",\
8805  "Artistic/Sketch",\
8806  "Artistic/Smooth Abstract",\
8807  "Artistic/Stylize",\
8808  "Artistic/Vector Painting",\
8809  "Artistic/Warhol",\
8810  "Artistic/Whirl Drawing",\
8811  "Black & White/B&W Stencil",\
8812  "Black & White/Black & White",\
8813  "Black & White/Charcoal",\
8814  "Black & White/Colorize [with Colormap]",\
8815  "Black & White/Colorize Lineart [Auto-Fill]",\
8816  "Black & White/Colorize Lineart [Smart Coloring]",\
8817  "Black & White/Desaturate Norm",\
8818  "Black & White/Dithering",\
8819  "Black & White/Emboss",\
8820  "Black & White/Engrave",\
8821  "Black & White/Freaky B&W",\
8822  "Black & White/Ink Wash",\
8823  "Black & White/Multi-Layer Etch",\
8824  "Black & White/Pencil",\
8825  "Black & White/Pencil Portrait",\
8826  "Black & White/Stamp",\
8827  "Black & White/Threshold Etch",\
8828  "Colors/Abstraction",\
8829  "Colors/Auto Balance",\
8830  "Colors/Basic Adjustments",\
8831  "Colors/Boost Chromaticity",\
8832  "Colors/Boost-Fade",\
8833  "Colors/Brightness",\
8834  "Colors/Channel Processing",\
8835  "Colors/CMYK Tone",\
8836  "Colors/Color Balance",\
8837  "Colors/Color Blindness",\
8838  "Colors/Color Grading",\
8839  "Colors/Color Presets",\
8840  "Colors/Color Temperature",\
8841  "Colors/Colormap",\
8842  "Colors/Contrast",\
8843  "Colors/Customize CLUT",\
8844  "Colors/Dark Sky",\
8845  "Colors/Equalize HSI-HSL-HSV",\
8846  "Colors/Equalize HSV",\
8847  "Colors/HSL Adjustment",\
8848  "Colors/HSV Select",\
8849  "Colors/Hue Lighten-Darken",\
8850  "Colors/LMS Adjustment",\
8851  "Colors/Local Contrast",\
8852  "Colors/Metallic Look",\
8853  "Colors/Mixer [CMYK]",\
8854  "Colors/Mixer [HSV]",\
8855  "Colors/Mixer [Lab]",\
8856  "Colors/Mixer [PCA]",\
8857  "Colors/Mixer [RGB]",\
8858  "Colors/Mixer [YCbCr]",\
8859  "Colors/Normalize Brightness",\
8860  "Colors/Retinex",\
8861  "Colors/Retro Fade",\
8862  "Colors/RGB Tone",\
8863  "Colors/Saturation EQ",\
8864  "Colors/Select-Replace Color",\
8865  "Colors/Selective Desaturation",\
8866  "Colors/Sepia",\
8867  "Colors/Simulate Film",\
8868  "Colors/Softlight",\
8869  "Colors/Specific Saturation",\
8870  "Colors/Temperature Balance",\
8871  "Colors/Tone Presets",\
8872  "Colors/Tune HSV Colors",\
8873  "Colors/User-Defined",\
8874  "Colors/Vintage Style",\
8875  "Colors/Zone System",\
8876  "Contours/Convolve",\
8877  "Contours/Curvature",\
8878  "Contours/Difference of Gaussians",\
8879  "Contours/Distance Transform",\
8880  "Contours/Edge",\
8881  "Contours/Edges",\
8882  "Contours/Edges Offsets",\
8883  "Contours/Gradient Norm",\
8884  "Contours/Gradient RGB",\
8885  "Contours/Isophotes",\
8886  "Contours/Laplacian",\
8887  "Contours/Local Orientation",\
8888  "Contours/Morphological Filter",\
8889  "Contours/Segmentation",\
8890  "Contours/Skeleton",\
8891  "Contours/Super-Pixels",\
8892  "Contours/Thin Edges",\
8893  "Deformations/Breaks",\
8894  "Deformations/Cartesian Transform",\
8895  "Deformations/Circle Transform",\
8896  "Deformations/Conformal Maps",\
8897  "Deformations/Continuous Droste",\
8898  "Deformations/Crease",\
8899  "Deformations/Distort Lens",\
8900  "Deformations/Drop Water",\
8901  "Deformations/Equirectangular to Nadir-Zenith",\
8902  "Deformations/Euclidean - Polar",\
8903  "Deformations/Fish-Eye",\
8904  "Deformations/Flower",\
8905  "Deformations/Kaleidoscope [Blended]",\
8906  "Deformations/Kaleidoscope [Polar]",\
8907  "Deformations/Kaleidoscope [Reptorian-Polar]",\
8908  "Deformations/Kaleidoscope [Symmetry]",\
8909  "Deformations/Logarithmic Distortion",\
8910  "Deformations/Moon2panorama",\
8911  "Deformations/Perspective",\
8912  "Deformations/Pixel Push",\
8913  "Deformations/Point Warp",\
8914  "Deformations/Polar Transform",\
8915  "Deformations/Quadrangle",\
8916  "Deformations/Raindrops",\
8917  "Deformations/Random",\
8918  "Deformations/Reflection",\
8919  "Deformations/Ripple",\
8920  "Deformations/Seamcarve",\
8921  "Deformations/Sinusoidal Water Distortion",\
8922  "Deformations/Sphere",\
8923  "Deformations/Spherize",\
8924  "Deformations/Square to Circle",\
8925  "Deformations/Stereographic Projection",\
8926  "Deformations/Symmetrize",\
8927  "Deformations/Textured Glass",\
8928  "Deformations/Twirl",\
8929  "Deformations/Water",\
8930  "Deformations/Wave",\
8931  "Deformations/Wind",\
8932  "Deformations/Zoom",\
8933  "Degradations/Add Grain",\
8934  "Degradations/Blur [Angular]",\
8935  "Degradations/Blur [Bloom]",\
8936  "Degradations/Blur [Depth-Of-Field]",\
8937  "Degradations/Blur [Gaussian]",\
8938  "Degradations/Blur [Glow]",\
8939  "Degradations/Blur [Linear]",\
8940  "Degradations/Blur [Multidirectional]",\
8941  "Degradations/Blur [Radial]",\
8942  "Degradations/Blur [Splinter]",\
8943  "Degradations/Chromatic Aberrations",\
8944  "Degradations/CRT Sub-Pixels",\
8945  "Degradations/Dirty",\
8946  "Degradations/Flip & Rotate Blocs",\
8947  "Degradations/Fragment Blur",\
8948  "Degradations/JPEG Artefacts",\
8949  "Degradations/Lomo",\
8950  "Degradations/Mess with Bits",\
8951  "Degradations/Noise [Additive]",\
8952  "Degradations/Noise [Perlin]",\
8953  "Degradations/Noise [Spread]",\
8954  "Degradations/Old-Movie Stripes",\
8955  "Degradations/Oldschool 8bits",\
8956  "Degradations/Pixel Sort",\
8957  "Degradations/Rain & Snow",\
8958  "Degradations/Random Shade Stripes",\
8959  "Degradations/Rebuild From Similar Blocs",\
8960  "Degradations/Scanlines",\
8961  "Degradations/Self Glitching",\
8962  "Degradations/Streak",\
8963  "Degradations/UltraWarp++++",\
8964  "Degradations/Visible Watermark",\
8965  "Degradations/Warp by Intensity",\
8966  "Details/Constrained Sharpen",\
8967  "Details/DCP Dehaze",\
8968  "Details/Details Equalizer",\
8969  "Details/Dynamic Range Increase",\
8970  "Details/Easy Skin Retouch",\
8971  "Details/Emboss-Relief",\
8972  "Details/Equalize Local Histograms",\
8973  "Details/Freaky Details",\
8974  "Details/High Pass",\
8975  "Details/Local Contrast Enhancement",\
8976  "Details/Local Normalization",\
8977  "Details/Local Processing",\
8978  "Details/Local Variance Normalization",\
8979  "Details/Magic Details",\
8980  "Details/Make Up",\
8981  "Details/Mighty Details",\
8982  "Details/Portrait Retouching",\
8983  "Details/Pyramid Processing",\
8984  "Details/Quick Tonemap",\
8985  "Details/Sharpen [Deblur]",\
8986  "Details/Sharpen [Gold-Meinel]",\
8987  "Details/Sharpen [Gradient]",\
8988  "Details/Sharpen [Hessian]",\
8989  "Details/Sharpen [Inverse Diffusion]",\
8990  "Details/Sharpen [Multiscale]",\
8991  "Details/Sharpen [Octave Sharpening]",\
8992  "Details/Sharpen [Richardson-Lucy]",\
8993  "Details/Sharpen [Shock Filters]",\
8994  "Details/Sharpen [Texture]",\
8995  "Details/Sharpen [Tones]",\
8996  "Details/Sharpen [Unsharp Mask]",\
8997  "Details/Sharpen [Whiten]",\
8998  "Details/Simple Local Contrast",\
8999  "Details/Spotify",\
9000  "Details/Texture",\
9001  "Details/Texture Enhance",\
9002  "Details/Tone Enhance",\
9003  "Details/Tone Mapping",\
9004  "Details/Tone Mapping [Fast]",\
9005  "Details/YAG Effect",\
9006  "Frames/Droste",\
9007  "Frames/Frame [Blur]",\
9008  "Frames/Frame [Cube]",\
9009  "Frames/Frame [Fuzzy]",\
9010  "Frames/Frame [Mirror]",\
9011  "Frames/Frame [Painting]",\
9012  "Frames/Frame [Pattern]",\
9013  "Frames/Frame [Regular]",\
9014  "Frames/Frame [Round]",\
9015  "Frames/Frame [Smooth]",\
9016  "Frames/Old Photograph",\
9017  "Frames/Polaroid",\
9018  "Frames/Tunnel",\
9019  "Frames/Vignette",\
9020  "Frequencies/Bandpass",\
9021  "Frequencies/Fourier Analysis",\
9022  "Frequencies/Fourier Transform",\
9023  "Frequencies/Fourier Watermark",\
9024  "Lights & Shadows/Burn",\
9025  "Lights & Shadows/Contrast Swiss Mask",\
9026  "Lights & Shadows/Dodge and Burn",\
9027  "Lights & Shadows/Drop Shadow",\
9028  "Lights & Shadows/Drop Shadow 3D",\
9029  "Lights & Shadows/Equalize Light",\
9030  "Lights & Shadows/Equalize Shadow",\
9031  "Lights & Shadows/Guided Light Rays",\
9032  "Lights & Shadows/Illuminate 2D Shape",\
9033  "Lights & Shadows/Light Glow",\
9034  "Lights & Shadows/Light Leaks",\
9035  "Lights & Shadows/Light Patch",\
9036  "Lights & Shadows/Light Rays",\
9037  "Lights & Shadows/Pop Shadows",\
9038  "Lights & Shadows/Relief Light",\
9039  "Lights & Shadows/Shadow Patch",\
9040  "Patterns/Bayer Filter",\
9041  "Patterns/Box Fitting",\
9042  "Patterns/Camouflage",\
9043  "Patterns/Canvas",\
9044  "Patterns/Canvas Texture",\
9045  "Patterns/Clouds",\
9046  "Patterns/Cracks",\
9047  "Patterns/Crystal",\
9048  "Patterns/Crystal Background",\
9049  "Patterns/Denim Texture",\
9050  "Patterns/Fibers",\
9051  "Patterns/Freqy Pattern",\
9052  "Patterns/Halftone",\
9053  "Patterns/Halftone Shapes",\
9054  "Patterns/Hearts",\
9055  "Patterns/Hedcut (Experimental)",\
9056  "Patterns/Lava",\
9057  "Patterns/Marble",\
9058  "Patterns/Maze",\
9059  "Patterns/Mineral Mosaic",\
9060  "Patterns/Mosaic",\
9061  "Patterns/Op Art",\
9062  "Patterns/Paper Texture",\
9063  "Patterns/Periodic Dots",\
9064  "Patterns/Plaid",\
9065  "Patterns/Polka Dots",\
9066  "Patterns/Random Color Ellipses",\
9067  "Patterns/Random Pattern",\
9068  "Patterns/Rays",\
9069  "Patterns/Reptile",\
9070  "Patterns/Resynthetize Texture [FFT]",\
9071  "Patterns/Resynthetize Texture [Patch-Based]",\
9072  "Patterns/Rorschach",\
9073  "Patterns/Satin",\
9074  "Patterns/Seamless Deco",\
9075  "Patterns/Seamless Turbulence",\
9076  "Patterns/Shock Waves",\
9077  "Patterns/Soft Random Shades",\
9078  "Patterns/Sponge",\
9079  "Patterns/Stained Glass",\
9080  "Patterns/Stars",\
9081  "Patterns/Stencil",\
9082  "Patterns/Strip",\
9083  "Patterns/Tetris",\
9084  "Patterns/Triangular Pattern",\
9085  "Patterns/Truchet",\
9086  "Patterns/Turbulent Halftone",\
9087  "Patterns/Voronoi",\
9088  "Patterns/Weave",\
9089  "Patterns/Whirls",\
9090  "Rendering/3D Blocks",\
9091  "Rendering/3D Colored Object",\
9092  "Rendering/3D Elevation",\
9093  "Rendering/3D Extrusion",\
9094  "Rendering/3D Image Object",\
9095  "Rendering/3D Lathing",\
9096  "Rendering/3D Random Objects",\
9097  "Rendering/Ball",\
9098  "Rendering/Circle Art",\
9099  "Rendering/Construction Material Texture",\
9100  "Rendering/Disco",\
9101  "Rendering/Equation Plot [Parametric]",\
9102  "Rendering/Equation Plot [Y=f(X)]",\
9103  "Rendering/Gradient [Corners]",\
9104  "Rendering/Gradient [Custom Shape]",\
9105  "Rendering/Gradient [from Line]",\
9106  "Rendering/Gradient [Linear]",\
9107  "Rendering/Gradient [Radial]",\
9108  "Rendering/Gradient [Random]",\
9109  "Rendering/Hair Locks",\
9110  "Rendering/Hypotrochoid",\
9111  "Rendering/Kitaoka Spin Illusion",\
9112  "Rendering/Lightning",\
9113  "Rendering/Lissajous",\
9114  "Rendering/Mandelbrot - Julia Sets",\
9115  "Rendering/Nebulous",\
9116  "Rendering/Neon Lightning",\
9117  "Rendering/Newton Fractal",\
9118  "Rendering/Plasma",\
9119  "Rendering/Popcorn Fractal",\
9120  "Rendering/Pseudorandom Noise",\
9121  "Rendering/Pythagoras Tree",\
9122  "Rendering/Quick Copyright",\
9123  "Rendering/Rainbow",\
9124  "Rendering/Shade Bobs",\
9125  "Rendering/Sine Curve",\
9126  "Rendering/Snowflake 2",\
9127  "Rendering/Spiral",\
9128  "Rendering/Spiral RGB",\
9129  "Rendering/Superformula",\
9130  "Rendering/Symmetric 2D Shape",\
9131  "Rendering/Thorn Fractal - Secant Sea",\
9132  "Rendering/Tree",\
9133  "Rendering/Turbulence",\
9134  "Rendering/Twisted Rays",\
9135  "Rendering/Wiremap",\
9136  "Repair/Anti Alias",\
9137  "Repair/Banding Denoise",\
9138  "Repair/Bayer Reconstruction",\
9139  "Repair/Clean Text",\
9140  "Repair/Compression Blur",\
9141  "Repair/Deinterlace",\
9142  "Repair/Deinterlace2x",\
9143  "Repair/Denoise",\
9144  "Repair/Denoise Smooth",\
9145  "Repair/Denoise Smooth Alt",\
9146  "Repair/Descreen",\
9147  "Repair/Despeckle",\
9148  "Repair/Iain Noise Reduction 2019",\
9149  "Repair/Iain's Fast Denoise",\
9150  "Repair/Inpaint [Holes]",\
9151  "Repair/Inpaint [Morphological]",\
9152  "Repair/Inpaint [Multi-Scale]",\
9153  "Repair/Inpaint [Patch-Based]",\
9154  "Repair/Inpaint [Transport-Diffusion]",\
9155  "Repair/JPEG Smooth",\
9156  "Repair/Local Similarity Mask",\
9157  "Repair/Moire Removal",\
9158  "Repair/Pixel Denoise",\
9159  "Repair/Recursive Median",\
9160  "Repair/Red-Eye Attenuation",\
9161  "Repair/Remove Hot Pixels",\
9162  "Repair/Repair Scanned Document",\
9163  "Repair/Smooth [Anisotropic]",\
9164  "Repair/Smooth [Antialias]",\
9165  "Repair/Smooth [Bilateral]",\
9166  "Repair/Smooth [Block PCA]",\
9167  "Repair/Smooth [Diffusion]",\
9168  "Repair/Smooth [Geometric-Median]",\
9169  "Repair/Smooth [Guided]",\
9170  "Repair/Smooth [IUWT]",\
9171  "Repair/Smooth [Mean-Curvature]",\
9172  "Repair/Smooth [Median]",\
9173  "Repair/Smooth [NL-Means]",\
9174  "Repair/Smooth [Patch-Based]",\
9175  "Repair/Smooth [Patch-PCA]",\
9176  "Repair/Smooth [Perona-Malik]",\
9177  "Repair/Smooth [Selective Gaussian]",\
9178  "Repair/Smooth [Skin]",\
9179  "Repair/Smooth [Thin Brush]",\
9180  "Repair/Smooth [Total Variation]",\
9181  "Repair/Smooth [Wavelets]",\
9182  "Repair/Smooth [Wiener]",\
9183  "Repair/Solidify",\
9184  "Repair/Unpurple",\
9185  "Repair/Unquantize [JPEG Smooth]",\
9186  "Repair/Unstrip",\
9187  "Repair/Upscale [DCCI2x]",\
9188  "Repair/Upscale [Diffusion]",\
9189  "Repair/Upscale [Edge]",\
9190  "Repair/Upscale [Scale2x]"
9191  k[${}]
9192
9193# Generate reference documentation pages for the G'MIC website.
9194# $1 = upload to G'MIC server, can be { 0 | 1 }.
9195update_reference_html : check "isbool(${1=0})"
9196  path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/reference/
9197  if ['$path_current']!=['$path_ok']
9198    error[0--3] "Command 'update_reference_html: Command run from wrong path: '"$path_current"', "\
9199                "should be '"$path_ok"'."
9200  fi
9201  x "rm -f *.pdf"
9202
9203  rm
9204  e[^-1] "Generate reference documentation pages for the G'MIC website."
9205  it $HOME/work/src/gmic/src/gmic_stdlib.gmic
9206  a y
9207
9208  # Generate image of examples.
9209  x "mkdir -p img"
9210  _parse_cli_images_path="img/"
9211  +parse_cli images
9212  parse_cli html
9213
9214  # Generate reference pages in html.
9215  rm
9216  x "cp -rf "$HOME/work/src/gmic-community/reference/images" ."
9217  reference html,$HOME/work/src/gmic-community/reference
9218
9219  # Upload to G'MIC server.
9220  if $1
9221    e[] "\n > Transfer reference documentation on G'MIC server.\n"
9222    x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -RL . /www/gmic/reference ; quit\""
9223  fi
9224  e[] "\n > All done.\n"
9225
9226# Generate tutorial pages for the G'MIC website.
9227# $1 = upload to G'MIC server, can be { 0 | 1 }.
9228update_tutorial_html : check "isbool(${1=0})"
9229  path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/tutorial/
9230  if ['$path_current']!=['$path_ok']
9231    error[0--3] "Command 'update_tutorial_html: Command run from wrong path: '"$path_current"', "\
9232                "should be '"$path_ok"'."
9233  fi
9234
9235  rm
9236  e[^-1] "Generate tutorial pages for the G'MIC website."
9237  path_tutorial=$HOME/work/src/gmic-community/tutorial
9238  use_vt100
9239
9240  # Build directory structure.
9241  e[] " > Build directory structure."
9242  x "mkdir -p images scripts"
9243  files 5,$path_tutorial/*
9244  l[]
9245    ({'${}'}:y) s -,{','}
9246    for $!
9247      file={0,t} rm[0]
9248      basename $file basename=${} 0 nm. $basename ext={`lowercase(['{x}'])`} rm.
9249      if ['$basename']!='img'
9250        if isdir(['$file']) files 5,$file/* ({'${}'}:y) s. -,{','} # Folder -> Recursively add files
9251        elif s=['$ext'];s=='png'||s=='jpg'||s=='jpeg'||s=='gif'||s=='mp4'||s=='svg'
9252          x "cp -f \""$file"\" images/" # Image/videos: Copy to sub-folder 'images/'
9253        elif s=['$ext'];s=='gmic'
9254          x "cp -f \""$file"\" scripts/" # Scripts: Copy to sub-folder 'scripts/'
9255        elif s=['$ext'];s=='gmd' # G'MIC Markdown -> Copy to current folder './'
9256          it[] $file
9257          ot. $basename rm.
9258        fi
9259      fi
9260    done
9261  endl
9262
9263  # Generate html pages.
9264  files 0,*.gmd files=${}
9265  repeat narg({/$files})
9266    arg0 $>,$files file=${}
9267    e[] " > Generate '"$_vt100_c$file$_vt100_n"'."
9268    t0=$|
9269    it $file
9270    replace_str. "../listmanip/",""
9271    gmd2html 2
9272    ot {n} rm.
9273    t1=$|
9274    e[] "\r > Generate '"$_vt100_c$file$_vt100_n"' (done in "$_vt100_g{_round($t1-$t0,0.1)}"s"$_vt100_n")."
9275  done
9276  x "rm -f *.gmd"
9277
9278  # Upload to G'MIC server.
9279  if $1
9280    e[] "\n > Transfer tutorial pages on G'MIC server.\n"
9281    x "lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"mirror -RL . /www/gmic/tutorial ; quit\""
9282  fi
9283  e[] "\n > All done.\n"
9284
9285# string2ts : ts_file.ts,strings_en.txt,strings_fr.txt
9286# Regenerate .ts file from new translated strings (separated by newline in files strings_*).
9287# Specified .ts file can be an empty argument ("").
9288strings2ts : skip "${1=}"
9289  e[^-1] "Regenerate translation file by merging file '$1' and source/translated strings '$2/$3'."
9290
9291  # Import data and extract original/translated strings.
9292  if narg($1)
9293    l[] it "$1"
9294      lang={`"
9295        lang = vector256();
9296        p = find(#-1,'language=\"');
9297        p>=0?(
9298          p+=10;
9299          q = find(#-1,'\">',p);
9300          q>=0?copy(lang,i[p],q - p);
9301        ); lang"`}
9302      e[] "  > File '$1', detected language : "$lang.
9303      s -,10
9304      N0=0
9305      repeat $! l[$>]
9306        autocrop {'" "'}
9307        if s=crop();find(s,'<source>')>=0" && "find(s,'</source>')>=0
9308          discard {'<source>'} discard {'</source>'} src$N0={t}
9309        elif s=crop();find(s,'<translation>')>=0" && "find(s,'</translation>')>=0
9310          discard {'<translation>'} discard {'</translation>'} dest$N0={t}
9311          N0+=1
9312        fi
9313      endl done
9314      rm
9315    endl
9316  else N0=0 lang=
9317  fi
9318  if $N0 e[] "  > File '$1' contains "$N0" strings." fi
9319  l[] it "$2" s -,10 N1=$! repeat $! nsrc$>={$>,t} done rm endl
9320  l[] it "$3" replace_str " ;",";" s -,10 N2=$! repeat $! ndest$>={$>,t} done rm endl
9321  if $N1!=$N2 error[0--3] "Command 'strings2ts': Number of lines do not match in files '$2' and '$3'." fi
9322  e[] "  > Files '$2' and '$3' contain "$N1" strings."
9323
9324  # Merge all strings together (set higher priority to strings that were already in the .ts file).
9325  l[] repeat $N1 ({'${ndest$>}'}) nm. ${nsrc$>} done y endl
9326  repeat $N0 rmn ${src$>} done # Discard already-translated strings in the .ts file
9327  if $N0 i[0] 0 l[0] rm repeat $N0 i ({'${dest$>}'}) nm. ${src$>} done y endl fi
9328  repeat $! l[$<] src={n} dest={t}
9329    if lowercase(['$src'])==lowercase(['$dest']) rm fi
9330  endl done
9331  sort_list +,n
9332  e[] "  > "$!" strings remain after cleaning/merging ("{$!-$N0}" new)."
9333
9334  # Generate .ts file.
9335  repeat $! l[$>]
9336    _strings2ts_src {n} src=${}
9337    _strings2ts_dest {t} dest=${}
9338    rm
9339    ({'"    <message>"\n\
9340       "      <source>"$src"</source>"\n\
9341       "      <translation>"$dest"</translation>"\n\
9342       "    </message>"\n\n'})
9343  endl done
9344  i[0] ({'"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\n\
9345     "<!DOCTYPE TS>"\n\
9346     "<TS version=\"2.1\" language=\""$lang"\">"\n\
9347     "  <context>"\n\
9348     "    <name>FilterTextTranslator</name>"\n\n'})
9349  ({'"  </context>"\n\
9350     "</TS>"\n'})
9351  y a y
9352
9353_strings2ts_src :
9354  ({'"$*"'})
9355  replace_str. "&deg;","°"
9356  replace_str. "&agrave;","à"
9357  replace_str. "&","&amp;"
9358  replace_str. "<","&lt;"
9359  replace_str. ">","&gt;"
9360  replace_str. "\"","&quot;"
9361  replace_str. "'","&apos;"
9362  u {t}
9363  rm.
9364
9365_strings2ts_dest :
9366  ({'"$*"'}) html2utf8.
9367  replace_str. "&deg;","°"
9368  replace_str. "&agrave;","à"
9369  replace_str. "&","&amp;"
9370  replace_str. "<","&lt;"
9371  replace_str. ">","&gt;"
9372  replace_str. "\"","&quot;"
9373  replace_str. "'","&apos;"
9374  u {t}
9375  rm.
9376
9377#
9378# Output mode 'zart' (output in stdout).
9379#
9380parse_gui_parseparams_zart : u 1 # Tell parser to parse filter parameters
9381parse_gui_trigger_zart :
9382  repeat $! l[$>] # Keep only 1-level folders
9383    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`}
9384  endl done
9385  sort_list +,n
9386
9387parse_gui_zart :
9388  e[] "  >> Generate output, in 'zart' mode.\n"
9389  +e[] "<?xml version=\"1.0\" ?>"
9390  +e[] "<document name=\"Presets\">\n"
9391
9392  current_group=
9393  N={$_nbfilters-1}
9394  repeat $_nbfilters,f
9395    e[] "\r  >> "$_vt100_c[#$f/$N]$_vt100_n" "{`copy(vector48(_'" "'),['${_f${f}_path}${_f${f}_name}'])`}
9396
9397    # Manage groups.
9398    0 nm. {`s=['${_f${f}_path}'];s[0,size(s)-1]`} path={b} rm.
9399    if ['$current_group']!=['$path']
9400      if ['$current_group']!=0 +e[] "</preset_group>\n" fi
9401      +e[] "<!-- ******** \n\n    "$path"\n\n ******** -->"
9402      +e[] "<preset_group name=\""$path"\">\n"
9403      current_group=$path
9404    fi
9405
9406    # Filter definition.
9407    _parse_gui_zart[] ${_f${f}_name} fname=${}
9408
9409    +e[] "<!-- "$path/$fname" -->"
9410    +e[] "<preset name=\""$fname"\">"
9411    +e[] "  <command>"${_f${f}_commandpreview}" $""*</command>"
9412    repeat ${_f${f}_nbparams},p
9413      _parse_gui_zart ${_f${f}_p{$p}_name} name=${}
9414      type=${_f${f}_p{$p}_type}
9415      nbargs=${_f${f}_p{$p}_nbargs}
9416      arg0= repeat $nbargs ('${_f${f}_p${p}_a$>}') autocrop. {'\"'} _parse_gui_zart {t} arg$>=${} rm. done
9417
9418      if ['$type']=='bool'
9419        if lowercase(['$arg0'])=='false' arg0=0 elif lowercase(['$arg0'])=='true' arg0=1 elif !isnum($arg0) arg0=0 fi
9420        +e[] "  <bool name=\""$name"\" default=\""$arg0"\" />"
9421
9422      elif ['$type']=='choice'
9423        default=0 n=0 args=
9424        l[] if isint($arg0) default=$arg0 n+=1 fi onfail endl
9425        c= repeat $nbargs-$n,a args.=${c}"choice"$>"=\""${arg{$n+$a}}"\"" c=" " done
9426        +e[] "  <choice name=\""$name"\" default=\""$default"\" "$args" />"
9427
9428      elif ['$type']=='color'
9429        if $nbargs==1" && "['$arg0'][0]==_'#' # Convert colors specified as '#RRGGBB[AA]'
9430          l[] ('$arg0') autocrop. {'#'} s x,-2 nbargs=$! repeat $!,a hex2dec {$>,t} arg$a=${} done rm endl
9431        fi
9432        args= c= repeat $nbargs,a args.=$c${arg$a} c="," done
9433        +e[] "  <color name=\""$name"\" default=\""$args"\" />"
9434
9435      elif s=['$type'];s=='int'" || "s=='float'
9436        +e[] "  <"$type" name=\""$name"\" default=\""$arg0"\" min=\""$arg1"\" max=\""$arg2"\" />"
9437
9438      elif ['$type']=='file'" || "['$type']=='filein'" || "['$type']=='fileout'
9439        +e[] "  <file name=\""$name"\" default=\""$arg0"\" />"
9440
9441      elif ['$type']=='folder'
9442        +e[] "  <folder name=\""$name"\" default=\""$arg0"\" />"
9443
9444      elif ['$type']=='link'
9445        align=-1 name= url= n=0
9446        l[] if isnum($arg0) align=$arg0 n+=1 fi onfail endl
9447        if $nbargs-$n>1 name=${arg$n} url=${arg{$n+1}}
9448        else url,name=${arg$n}
9449        fi
9450        if $align==0 align=left elif $align==1 align=right else align=center fi
9451        +e[] "  <link name=\""$name"\" url=\""$url"\" align=\""$align"\" />"
9452
9453      elif ['$type']=='note'
9454        text={/$arg0}
9455        +e[] "  <note text=\""$text"\" />"
9456
9457      elif ['$type']=='point'
9458        +e[] "  <point name=\""$name"\" position=\""$arg0,$arg1"\" />"
9459
9460      elif ['$type']=='separator'
9461        +e[] "  <separator />"
9462
9463      elif ['$type']=='text'
9464        +e[] "  <text name=\""$name"\" default=\""$arg0"\" />"
9465
9466      elif ['$type']=='value'
9467        +e[] "  <value value=\""$arg0"\" />"
9468
9469      else # Unsupported parameter
9470        +e[] "  <value value=\""$arg0"\" />"
9471      fi
9472
9473    done
9474    +e[] "</preset>\n"
9475  done
9476  if narg($current_group) +e[] "</preset_group>" fi
9477  +e[] "</document>"
9478  e[] "\r  >> "${_vt100_g}{`copy(vector64(_'" "'),'"Output done!"')`}$_vt100_n
9479
9480_parse_gui_zart :
9481  l[]
9482    ('"$*"')
9483    replace_str "&amp;","#amp;"
9484    replace_str. "&","&amp;"
9485    replace_str. "<","&lt;"
9486    replace_str. ">","&gt;"
9487    replace_str. "\"","&quot;"
9488    replace_str "#amp;","&amp;"
9489    u {t} rm.
9490  onfail u "" endl
9491
9492# Convert last ascii buffer with html codes (e.g. &#37197;), as an UTF-8 encoded buffer.
9493html2utf8 :
9494  if h
9495    eval "
9496      ref(crop(),source);
9497      ref(vector8(),svalue);
9498      p = 0;
9499      while (1,
9500        p = p0 = find(source,'&#',p);
9501        p<0?break();
9502        p+=2;
9503        p>=size(source)?break();
9504        q = find(source,';',p);
9505        q>p && q<=p+8 && !isnan(val=stov(source,p))?(
9506          copy(svalue,source[p],q-p); svalue[q-p] = 0;
9507          val = stov(svalue,0,1);
9508          !isnan(val) && isint(val) && val>0?(
9509
9510            # Encode in UTF-8.
9511            val<=0x007f?( # 7 bits or less -> 1 byte
9512              i[p0++] = val;
9513            ):val<=0x07ff?( # 11 bits or less -> 2 bytes
9514              i[p0++] = (val>>6)|0xc0;
9515              i[p0++] = (val&0x3f)|0x80;
9516            ):val<=0xffff?( # 16 bits or less -> 3 bytes
9517              i[p0++] = (val>>12)|0xe0;
9518              i[p0++] = ((val>>6)&0x3f)|0x80;
9519              i[p0++] = (val&0x3f)|0x80;
9520            ):( # 21 bits or less -> 4 bytes
9521              i[p0++] = (val>>18)|0xf0;
9522              i[p0++] = ((val>>12)&0x3f)|0x80;
9523              i[p0++] = ((val>>6)&0x3f)|0x80;
9524              i[p0++] = (val&0x3f)|0x80;
9525            );
9526            copy(i[p0],-1,q-p0+1,1,0);
9527            p = q + 1;
9528          );
9529        );
9530      )"
9531    discard. -1
9532  fi
9533
9534# Convert last UTF-8 encoded buffer to ascii buffer with html codes.
9535utf82html :
9536  if h
9537    eval "
9538      write_seq() = ( copy(res[q],'&#'); q+=2; s = vtos(val); l = find(s,0); copy(res[q],s,l); q+=l; res[q++]=_';' );
9539      ref(vector(#4*wh),res);
9540      q = 0;
9541      repeat (wh,p,
9542        i = i[p];
9543        !(i&0x80)?( # 1-byte
9544          res[q++] = i;
9545        ):(i&0xe0)==0xc0?( # 2-bytes
9546          val = (i&0x1f)<<6; i = i[p + 1]; val|= (i&0x3f);
9547          write_seq();
9548        ):(i&0xf0)==0xe0?( # 3-bytes
9549          val = (i&0xf)<<12; i = i[p + 1]; val|= (i&0x3f)<<6; i = i[p + 2]; val|= (i&0x3f);
9550          write_seq();
9551        ):(i&0xf8)==0xf0?( # 4-bytes
9552          val = (i&0x7)<<18; i = i[p + 1]; val|= (i&0x3f)<<12; i = i[p + 2]; val|= (i&0x3f)<<6;
9553          i = i[p + 3]; val|= (i&0x3f);
9554          write_seq();
9555        );
9556      );
9557      store(res,'res',1,q)"
9558    rm. $res
9559  fi
9560
9561# Force certain filters to move from one path to another.
9562_upload_filters_move :
9563  m "move_filter : skip \"${""2=}\" nmd 3,\"$""1\" ind=${}
9564     if $ind basename \"$""1\" nm[$ind] \"$""2\"/${}
9565     else v 0 e[0--4] \"Cannot move unknown filter '\"${_vt100_r}\"$""1\"${_vt100_n}\"'
9566     to '\"${_vt100_g}\"$""2\"${_vt100_n}\"'\" fi"
9567
9568  sort_list +,n
9569  move_filter "Testing/Garagecoder/Anti Alias","Repair"
9570  move_filter "Testing/Garagecoder/Auto Balance","Colors"
9571  move_filter "Testing/Garagecoder/LMS Adjustment","Colors"
9572  move_filter "Testing/Garagecoder/Compression Blur","Repair"
9573  move_filter "Testing/Garagecoder/Emboss","Black & White"
9574  move_filter "Testing/Garagecoder/JPEG Smooth","Repair"
9575  move_filter "Testing/Garagecoder/Quick Tonemap","Details"
9576  move_filter "Testing/Garagecoder/Normalize Brightness","Colors"
9577  move_filter "Testing/Garagecoder/Sharpen [Gradient]","Details"
9578  move_filter "Testing/Garagecoder/Sharpen [Tones]","Details"
9579  move_filter "Testing/Garagecoder/Temperature Balance","Colors"
9580  move_filter "Testing/Garagecoder/Unquantize [JPEG Smooth]","Repair"
9581  move_filter "Testing/Garagecoder/Upscale [Edge]","Repair"
9582  move_filter "Testing/Garagecoder/Wiremap","Rendering"
9583  move_filter "Testing/Garagecoder/Smooth [Geometric-Median]","Repair"
9584  move_filter "Testing/Gmic Tutorials/Hedcut (Experimental)","Patterns"
9585  move_filter "Testing/Iain Fergusson/Easy Skin Retouch","Details"
9586  move_filter "Testing/Iain Fergusson/Moire Removal","Repair"
9587  move_filter "Testing/Iain Fergusson/Halftone Shapes","Patterns"
9588  move_filter "Testing/Iain Fergusson/Simple Local Contrast","Details"
9589  move_filter "Testing/Iain Fergusson/Turbulent Halftone","Patterns"
9590  move_filter "Testing/Joan Rake/Deformations/Ultrawarp++++","Degradations"
9591  move_filter "Testing/Naggobot/Blockism","Artistic"
9592  move_filter "Testing/Reptorian/Blur [Splinter]","Degradations"
9593  move_filter "Testing/Reptorian/Construction Material Texture","Rendering"
9594  move_filter "Testing/Reptorian/Emboss-Relief","Details"
9595  move_filter "Testing/Reptorian/Fragment Blur","Degradations"
9596  move_filter "Testing/Reptorian/Kaleidoscope [Reptorian-Polar]","Deformations"
9597  move_filter "Testing/Reptorian/Logarithmic Distortion","Deformations"
9598  move_filter "Testing/Reptorian/Nebulous","Rendering"
9599  move_filter "Testing/Reptorian/Pixel Push","Deformations"
9600  move_filter "Testing/Reptorian/Point Warp","Deformations"
9601  move_filter "Testing/Reptorian/Popcorn Fractal","Rendering"
9602  move_filter "Testing/Reptorian/Pseudorandom Noise","Rendering"
9603  move_filter "Testing/Reptorian/Sinusoidal Water Distortion","Deformations"
9604  move_filter "Testing/Samj/Arrays & Tiles/Annular Steiner Chain Round Tile","Arrays & Tiles"
9605  move_filter "Testing/Samj/Arrays & Tiles/Reptile","Patterns"
9606  move_filter "Testing/Samj/Artistic/Angoisse Anguish","Artistic"
9607  move_filter "Testing/Samj/Artistic/Chalk It Up [Fr]","Artistic"
9608  move_filter "Testing/Samj/Artistic/Barbouillage Paint Daub","Artistic"
9609  move_filter "Testing/Samj/Artistic/Skeletik","Artistic"
9610  move_filter "Testing/Samj/Patterns/Denim Texture","Patterns"
9611  move_filter "Testing/Samj/Patterns/Soft Random Shades","Patterns"
9612  move_filter "Testing/Samj/Rendering/Pythagoras Tree","Rendering"
9613  move_filter "Testing/Samj/Rendering/Snowflake 2","Rendering"
9614  move_filter "Testing/Samj/Rendering/Twisted Rays","Rendering"
9615  move_filter "Testing/Souphead/Disco","Rendering"
9616  move_filter "Testing/Souphead/Moon2panorama","Deformations"
9617  move_filter "Testing/Souphead/Spiral RGB","Rendering"
9618  move_filter "Testing/Souphead/Kitaoka Spin Illusion","Rendering"
9619  move_filter "Testing/Zonderr/Spiral","Rendering"
9620  um move_filter
9621
9622upload_filters :
9623  e[^-1] "Upload filter definition file on G'MIC server.\n"
9624  rm
9625
9626  # Be sure to get the latest version of filters.
9627  x "cd "$HOME"/work/src/gmic && git pull"
9628  x "cd "$HOME"/work/src/gmic-community && git pull"
9629
9630  # Define the list of compatible versions.
9631  (300,$_version) store. compat
9632
9633  # Import filters from stdlib and community.
9634  files $HOME/work/src/gmic-community/include/*.gmic
9635  files=${},$HOME/work/src/gmic/src/gmic_stdlib.gmic
9636  repeat narg($files) l[]
9637    file=${"arg 1+$>,"$files}
9638    it[] $file
9639    basename $file basename=${}
9640    if ['$basename']=='sylvie_alexandre.gmic'
9641      s +,{'"#@gui <b>"'} i[1--2:2] ('"#@gui ________<b>Testing<b>\n#@gui <i>Samj</i>\n"') y a y
9642    elif s=['$basename'];s=='template.gmic'
9643      rm 0
9644    fi
9645  endl done
9646  i[1--2] ('"#@gui _________________\n"') y a y
9647
9648  # Create update file.
9649  +l.
9650    e[] "** Generate filter update file."
9651    m "parse_gui_trigger_update : _upload_filters_move"
9652    v + parse_gui. update v -
9653    um parse_gui_trigger_update
9654
9655    # Upload filter files on G'MIC server.
9656    e[] "** Upload filter update."
9657    ot ${_path_rc}update$_version.gmic
9658    if "d = date(3); h = date(4); h>=7 && d>=1 && d<=5" url=http://bit.ly/2CmhX65 # url=http://bit.ly/2uaBRMB
9659    else url=https://bit.ly/33lRzX4 # https://bit.ly/2WeKVPv url=http://bit.ly/2uaBRMB #
9660    fi
9661    replace_str "<i>David Tschumperlé</i>","<i><a href=\""$url"\">David Tschumperlé</a></i>"
9662    o cimgz:/tmp/update$_version.gmic,uchar
9663
9664    $compat
9665    repeat w
9666      v={i[$>]}
9667      _upload[] ${_path_rc}update$_version.gmic,plain_update$v.gmic,1
9668      _upload[] /tmp/update$_version.gmic,update$v.gmic,1
9669    done
9670    rm
9671  endl
9672
9673  # Create JSON file.
9674  +l.
9675    e[] "** Generate JSON filters file."
9676    v + parse_gui. json v -
9677
9678    # Upload filter files on G'MIC server.
9679    e[] "** Upload JSON filters."
9680    ot ${_path_rc}/update$_version.json
9681
9682    $compat
9683    repeat w
9684      v={i[$>]}
9685      _upload[] ${_path_rc}/update$_version.json,update$v.json,1
9686    done
9687    rm
9688  endl
9689
9690  # Create filter listing.
9691  +l.
9692    e[] "** Generate filter listing."
9693    v + parse_gui. list v -
9694    ot /tmp/gui_filters.txt
9695    rm
9696  endl
9697  rm
9698
9699_upload : skip "${2=""}","${3=0}"
9700  if narg("$2") out="$2" else basename "$1" out=${} fi
9701  if !narg($GMIC_LOGIN)
9702    GMIC_LOGIN=${"gmic_ftp 0"}
9703    GMIC_PASSWD=${"gmic_ftp 1"}
9704    GMIC_FTP=${"gmic_ftp 2"}
9705  fi
9706  if narg($GMIC_LOGIN)
9707    x $3,"lftp ftp://"$GMIC_LOGIN":"$GMIC_PASSWD"@"$GMIC_FTP" -e \"put -O /www/gmic/ \\\"$1\\\" -o \\\""$out"\\\";
9708          quit\" >/dev/null"
9709  fi
9710
9711# Upload or list released binaries on the G'MIC web server.
9712# $1=version number (e.g "1.6.7_pre")
9713# $2={ 0=print list of URLs | 1=upload files }
9714upload_binaries : check "isbool(${2=1})"
9715  is_pre=${"strcontains $1,_pre"}
9716  N=0
9717  file$N=gmic_$1_debian9-2_stretch_amd64.deb            N+=1
9718  file$N=gmic_$1_debian10_buster_amd64.deb              N+=1
9719  file$N=gmic_$1_ubuntu18-10_cosmic_amd64.deb           N+=1
9720  file$N=gmic_$1_ubuntu19-04_disco_amd64.deb            N+=1
9721  file$N=gmic_$1_ubuntu19-10_eoan_amd64.deb             N+=1
9722  file$N=gmic_$1_ubuntu20-04_focal_amd64.deb            N+=1
9723  file$N=gmic_$1_ubuntu20-10_groovy_amd64.deb           N+=1
9724  file$N=gmic_$1_ubuntu21-04_hirsute_amd64.deb          N+=1
9725  file$N=gmic_$1_ubuntu21-10_impish_amd64.deb           N+=1
9726
9727  file$N=gmic_$1_gimp2.8_debian9-2_stretch_amd64.zip    N+=1
9728  file$N=gmic_$1_gimp2.10_debian10_buster_amd64.zip     N+=1
9729  file$N=gmic_$1_gimp2.10_ubuntu18-10_cosmic_amd64.zip  N+=1
9730  file$N=gmic_$1_gimp2.10_ubuntu19-04_disco_amd64.zip   N+=1
9731  file$N=gmic_$1_gimp2.10_ubuntu19-10_eoan_amd64.zip    N+=1
9732  file$N=gmic_$1_gimp2.10_ubuntu20-04_focal_amd64.zip   N+=1
9733  file$N=gmic_$1_gimp2.10_ubuntu20-10_groovy_amd64.zip  N+=1
9734  file$N=gmic_$1_gimp2.10_ubuntu21-04_hirsute_amd64.zip N+=1
9735  file$N=gmic_$1_gimp2.10_ubuntu21-10_impish_amd64.zip  N+=1
9736
9737  file$N=gmic_$1_krita_debian9-2_stretch_amd64.zip      N+=1
9738  file$N=gmic_$1_krita_debian10_buster_amd64.zip        N+=1
9739  file$N=gmic_$1_krita_ubuntu18-10_cosmic_amd64.zip     N+=1
9740  file$N=gmic_$1_krita_ubuntu19-04_disco_amd64.zip      N+=1
9741  file$N=gmic_$1_krita_ubuntu19-10_eoan_amd64.zip       N+=1
9742  file$N=gmic_$1_krita_ubuntu20-04_focal_amd64.zip      N+=1
9743  file$N=gmic_$1_krita_ubuntu20-10_groovy_amd64.zip     N+=1
9744  file$N=gmic_$1_krita_ubuntu21-04_hirsute_amd64.zip    N+=1
9745  file$N=gmic_$1_krita_ubuntu21-10_impish_amd64.zip     N+=1
9746
9747  file$N=gmic_$1_cli_win64.zip                          N+=1
9748  file$N=gmic_$1_lib_win64.zip                          N+=1
9749  file$N=gmic_$1_qt_win64.zip                           N+=1
9750  file$N=gmic_$1_gimp2.10_win64.zip                     N+=1
9751  file$N=gmic_$1_gimp2.10_win64.exe                     N+=1
9752  file$N=gmic_$1_krita_win64.zip                        N+=1
9753
9754  if !$2 # Only get list of URLs
9755    e[0--3] "List URLs of released binaries ($1) from the G'MIC web server.\n"
9756    repeat $N
9757      file=${file$>}
9758      is_win=${strcontains[]" "$file,win}
9759      if $is_win folder="windows" else folder="linux" fi
9760      e[] "http://gmic.eu/files/"$folder/$file
9761    done
9762    e[] ""
9763
9764  else # Upload files
9765    e[0--3] "Upload released binaries ($1) on the G'MIC web server."
9766
9767    t0=$| n=0 t=0
9768    e[] "- Waiting for binary files to be build."
9769    do
9770      repeat $N
9771        file=${file$>}
9772        if isfile(['{/$file}'])
9773          strreplace $file,_$1_,_
9774          file_short=${}
9775          is_win=${strcontains[]" "$file,win}
9776          if $is_win folder="windows" else folder="linux" fi
9777          e[] "- Upload file '"$file"' to 'https://gmic.eu/files/prerelease/"$file_short"'."
9778          _upload[] $file,"files/prerelease/"$file_short
9779          if !$is_pre
9780            e[] "- Upload file '"$file"' to 'https://gmic.eu/files/"$folder/$file"'."
9781            _upload[] $file,"files/"$folder/$file
9782          fi
9783
9784          file$>= n+=1
9785        fi
9786      done
9787      if $n<$N
9788        if !($t%4)
9789          remaining= sep=
9790          repeat $N if narg(${file$>}) remaining.=${sep}${file$>} sep=", " fi done
9791          e[] "- Waiting for files: "$remaining"."
9792        fi
9793        wait 30000 t+=1
9794      fi
9795    while $n<$N" && "$|<$t0+60*60*6
9796    if $n<$N e[] "- Partial uploads done (timeout reached)."
9797    else e[] "- All uploads done !"
9798    fi
9799  fi
9800
9801# Update version number in html header file.
9802# (works both for CImg and G'MIC !)
9803# $1 = filename
9804# $2 = version number
9805# $3 = is_prerelease = { 0 | 1 }
9806_update_header_html : check "narg(${1=}) && ${2=0}>0 && isbool(${3=0})"
9807  filename="$1"
9808  l[]
9809    it[] $filename
9810    s +,{'\n'}
9811    repeat $! if {$>,h>=64} l[$>]
9812      +autocrop {'" "'} autocrop. {'\t'}
9813      if "find(#0,['Latest stable version: '])>=0"
9814        is_gmic={"find(#-1,'gmic.eu')>=0"}
9815        is_cimg={"find(#-1,'cimg.eu')>=0"}
9816        if !$is_gmic" && "!$is_cimg error "Cannot determine CImg or G'MIC header file." fi
9817
9818        # Retrieve latest stable version specified in the header file.
9819        +l. s -,{'>'}
9820          if {2,"i[0]>=_'0' && i[0]<=_'9' && i[1]==_'.' && i[2]>=_'0' && i[2]<=_'9' &&
9821                 i[3]==_'.' && i[4]>=_'0' && i[4]<=_'9'"}
9822            sta={2,`crop(0,0,1,5)`}
9823          fi
9824          if $3 pre=${strver\ $2} else sta=${strver\ $2} fi
9825        rm endl
9826        rm[0]
9827        if $is_gmic
9828          i[0] ('"        Latest stable version: <b>"\
9829                 "<a href=\"https://gmic.eu/download.html\">"$sta"</a></b>"')
9830          if $3
9831            i[1] ('" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "\
9832                   "Current pre-release: <b><a href=\"https://gmic.eu/files/prerelease\">"$pre"</a></b>"')
9833          fi
9834        else
9835          i[0] ('"        Latest stable version: "\
9836                 "<b><a href=\"http://cimg.eu/files/CImg_"$sta".zip\">"$sta"</a></b>"')
9837          if $3
9838            i[1] ('" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "\
9839                   "Current pre-release: <b><a href=\"http://cimg.eu/files/CImg_latest.zip\">"$pre"</a></b>"')
9840          fi
9841        fi
9842        y
9843      fi
9844      rm.
9845    endl fi done
9846    a y
9847    ot $filename
9848    rm
9849  endl
9850
9851#@cli v : eq. to 'verbose'. : (+)
9852
9853#@cli verbose : level : { + | - } : (+)
9854#@cli : Set or increment/decrement the verbosity level. Default level is 0.
9855#@cli : (eq. to 'v').\n
9856#@cli : When 'level'>0, G'MIC log messages are displayed on the standard error (stderr).
9857#@cli : Default value: 'level=1'.
9858
9859#@cli wait : delay : (no arg) : (+)
9860#@cli : Wait for a given delay (in ms), optionally since the last call to 'wait'.
9861#@cli : or wait for a user event occurring on the selected instant display windows.
9862#@cli : 'delay' can be { <0=delay+flush events | 0=event | >0=delay }.
9863#@cli : Command selection (if any) stands for instant display window indices instead of image indices.
9864#@cli : If no window indices are specified and if 'delay' is positive, the command results
9865#@cli : in a 'hard' sleep during specified delay.
9866#@cli : Default value: 'delay=0'.
9867
9868#@cli warn : _force_visible={ 0 | 1 },_message : (+)
9869#@cli : Print specified warning message, on the standard error (stderr).
9870#@cli : Command selection (if any) stands for displayed call stack subset instead of image indices.
9871
9872#@cli w : eq. to 'window'. : (+)
9873
9874#@cli window : _width[%]>=-1,_height[%]>=-1,_normalization,_fullscreen,_pos_x[%],_pos_y[%],_title : (+)
9875#@cli : Display selected images into an instant display window with specified size, normalization type,
9876#@cli : fullscreen mode and title.
9877#@cli : (eq. to 'w').\n
9878#@cli : If 'width' or 'height' is set to -1, the corresponding dimension is adjusted to the window
9879#@cli : or image size.
9880#@cli : Specify 'pos_x' and 'pos_y' arguments only if the window has to be moved to the specified
9881#@cli : coordinates. Otherwise, they can be avoided.
9882#@cli : 'width'=0 or 'height'=0 closes the instant display window.
9883#@cli : 'normalization' can be { -1=keep same | 0=none | 1=always | 2=1st-time | 3=auto }.
9884#@cli : 'fullscreen' can be { -1=keep same | 0=no | 1=yes }.
9885#@cli : You can manage up to 10 different instant display windows by using the numbered variants
9886#@cli : 'w0' (default, eq. to 'w'),'w1',...,'w9' of the command 'w'.
9887#@cli : Invoke 'window' with no selection to make the window visible, if it has been closed by the user.
9888#@cli : Default values: 'width=height=normalization=fullscreen=-1' and 'title=(undefined)'.
9889
9890#---------------------------------
9891#
9892#@cli :: List Manipulation
9893#
9894#---------------------------------
9895
9896#@cli k : eq. to 'keep'. : (+)
9897
9898#@cli keep : (+)
9899#@cli : Keep only selected images.
9900#@cli : (eq. to 'k').
9901#@cli : $ image.jpg split x keep[0-50%:2] append x
9902#@cli : $ image.jpg split x keep[^30%-70%] append x
9903
9904#@cli mv : eq. to 'move'. : (+)
9905
9906#@cli move : position[%] : (+)
9907#@cli : Move selected images at specified position.
9908#@cli : (eq. to 'mv').
9909#@cli : $ image.jpg split x,3 move[1] 0
9910#@cli : $ image.jpg split x move[50%--1:2] 0 append x
9911
9912#@cli nm : eq. to 'name'. : (+)
9913
9914#@cli name : "name1","name2",... : (+)
9915#@cli : Set names of selected images.
9916#@cli : - If the selection contains a single image, then it is assumed the command has a single name
9917#@cli :  argument (possibly containing multiple comas).
9918#@cli : - If the selection contains more than one image, each command argument defines a single image
9919#@cli :  name for each image of the selection.
9920#@cli : (eq. to 'nm').
9921#@cli : $ image.jpg name image blur[image] 2
9922#@cli : $$
9923
9924#@cli rm : eq. to 'remove'. : (+)
9925
9926#@cli remove : (+)
9927#@cli : Remove selected images.
9928#@cli : (eq. to 'rm').
9929#@cli : $ image.jpg split x remove[30%-70%] append x
9930#@cli : $ image.jpg split x remove[0-50%:2] append x
9931
9932#@cli remove_duplicates
9933#@cli : Remove duplicates images in the selected images list.
9934#@cli : $ (1,2,3,4,2,4,3,1,3,4,2,1) split x remove_duplicates append x
9935remove_duplicates :
9936  e[^-1] "Remove duplicates images in selected list of image$?."
9937  repeat $!,base
9938    off=0
9939    repeat $!-$>-1
9940      comp={$base+1+$>-$off}
9941      if $comp>=$! break fi
9942      +-[$base,$comp] abs. is_duplicate={!is} rm.
9943      if $is_duplicate rm[$comp] off+=1 fi
9944    done
9945  done
9946
9947#@cli remove_empty
9948#@cli : Remove empty images in the selected image list.
9949remove_empty :
9950  e[^-1] "Remove empty images in selected list of image$?."
9951  $!,1,1,1,"!w#x?x:-1" discard. -1 rm[{^},-1]
9952
9953#@cli rmn : eq. to 'remove_named'.
9954rmn :
9955  e[^-1] "Remove images named '$*'."
9956  nmd $"*" rm[${}]
9957
9958#@cli remove_named : "name1","name2",...
9959#@cli : Remove all images with specified names from the list of images.
9960#@cli : Does nothing if no images with those names exist.
9961#@cli : (eq. to 'rmn').
9962remove_named :
9963  e[^-1] "Remove images named '$*'."
9964  nmd $"*" rm[${}]
9965
9966#@cli rv : eq. to 'reverse'. : (+)
9967
9968#@cli reverse : (+)
9969#@cli : Reverse positions of selected images.
9970#@cli : (eq. to 'rv').
9971#@cli : $ image.jpg split x,3 reverse[-2,-1]
9972#@cli : $ image.jpg split x,-16 reverse[50%-100%] append x
9973
9974#@cli sort_list : _ordering={ + | - },_criterion
9975#@cli : Sort list of selected images according to the specified image criterion.
9976#@cli : Default values: 'ordering=+', 'criterion=i'.
9977#@cli : $ (1;4;7;3;9;2;4;7;6;3;9;1;0;3;3;2) split y sort_list +,i append y
9978sort_list : skip ${1=+},${2=i}
9979  s0="descending" s1="ascending"
9980  e[^-1] "Sort list of image$? in "${s{_'+'=='$1'}}" order, according to the image criterion '$2'."
9981  if $!
9982    if '$2'=='n'||'$2'=='N' # Special case : lexicographic order from image names (n: ignore case, N: consider case)
9983      op={`;'$1'=='-'?_'>':_'<'`}
9984      if '$2'=='n' fn=lowercase else fn= fi
9985      $!,1,1,1,"n = name(#x,1024); find(n,0)%1025" slen={iM} rm. # Largest name length.
9986      eval "
9987        const lm1 = l - 1;
9988        const slen = "$slen";
9989        strcmp(n0,n1) = (for (k = 0, k<slen && !(diff = "$fn"(n0[k]) - "$fn"(n1[k])), ++k); diff);
9990        quicksort(lo0,hi0) = (
9991          stack = vector(#2*l);
9992          stacksize = 0;
9993          push(elt0,elt1) = (stack[stacksize++] = elt0; stack[stacksize++] = elt1);
9994          pop() = (_s1 = stack[--stacksize]; _s0 = stack[--stacksize]; [_s0,_s1]);
9995          push(lo0,hi0);
9996          while (stacksize>0,
9997            range = pop();
9998            lo = range[0];
9999            hi = range[1];
10000            pivot = int((lo + hi)/2);
10001            ref(name(#pivot,slen),npivot);
10002            while (lo<hi,
10003              while ((ref(name(#lo,slen),nlo);strcmp(nlo,npivot)<0), ++lo);
10004              while ((ref(name(#hi,slen),nhi);strcmp(npivot,nhi)<0), --hi);
10005              lo<=hi?(lo!=hi?run('rv[',lo,',',hi,']'); ++lo; --hi);
10006            );
10007            if (range[0]<hi,push(range[0],hi));
10008            if (lo<range[1],push(lo,range[1]));
10009          )
10010        );
10011        quicksort(0,lm1)"
10012
10013    else # Generic case
10014      i=$! repeat $! ({$>,$2}) done a[$i--1] y +f. 'y' a[-2,-1] x sort. $1,y z. 1,1
10015      repeat h nm$>={$>,n} nm[$>] sortlist$> done
10016      repeat h mv[sortlist{i(0,$>)}] -1 done
10017      repeat h nm[$>] ${nm{i(0,$>)}} done
10018      rm.
10019    fi
10020  fi
10021
10022#---------------------------------
10023#
10024#@cli :: Mathematical Operators
10025#
10026#---------------------------------
10027
10028#@cli abs : (+)
10029#@cli : Compute the pointwise absolute values of selected images.
10030#@cli : $ image.jpg +sub {ia} abs[-1]
10031#@cli : $ 300,1,1,1,'cos(20*x/w)' +abs display_graph 400,300
10032
10033#@cli acos : (+)
10034#@cli : Compute the pointwise arccosine of selected images.
10035#@cli : $ image.jpg +normalize -1,1 acos[-1]
10036#@cli : $ 300,1,1,1,'cut(x/w+0.1*u,0,1)' +acos display_graph 400,300
10037#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
10038
10039#@cli acosh : (+)
10040#@cli : Compute the pointwise hyperbolic arccosine of selected images.
10041
10042#@cli + : eq. to 'add'. : (+)
10043
10044#@cli add : value[%] : [image] : 'formula' : (no arg) : (+)
10045#@cli : Add specified value, image or mathematical expression to selected images, \
10046# or compute the pointwise sum of selected images.
10047#@cli : (eq. to '+').
10048#@cli : $ image.jpg +add 30% cut 0,255
10049#@cli : $ image.jpg +blur 5 normalize 0,255 add[1] [0]
10050#@cli : $ image.jpg add '80*cos(80*(x/w-0.5)*(y/w-0.5)+c)' cut 0,255
10051#@cli : $ image.jpg repeat 9 +rotate[0] {$>*36},1,0,50%,50% done add div 10
10052
10053#@cli & : eq. to 'and'. : (+)
10054
10055#@cli and : value[%] : [image] : 'formula' : (no arg) : (+)
10056#@cli : Compute the bitwise AND of selected images with specified value, image or mathematical \
10057# expression, or compute the pointwise sequential bitwise AND of selected images.
10058#@cli : (eq. to '&').
10059#@cli : $ image.jpg and {128+64}
10060#@cli : $ image.jpg +mirror x and
10061
10062#@cli argmax
10063#@cli : Compute the argmax of selected images. Returns a single image
10064#@cli : with each pixel value being the index of the input image with maximal value.
10065#@cli : $ image.jpg sample lena,lion,square +argmax
10066argmax :
10067  e[^-1] "Compute argmax of image$?."
10068  if !$! return fi
10069  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
10070  ${-max_whds},"argmax("$str")" k. nm [argmax]
10071
10072#@cli argmaxabs
10073#@cli : Compute the argmaxabs of selected images. Returns a single image
10074#@cli : with each pixel value being the index of the input image with maxabs value.
10075argmaxabs :
10076  e[^-1] "Compute argmaxabs of image$?."
10077  if !$! return fi
10078  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
10079  ${-max_whds},"argmaxabs("$str")" k. nm [argmaxabs]
10080
10081#@cli argmin
10082#@cli : Compute the argmin of selected images. Returns a single image
10083#@cli : with each pixel value being the index of the input image with minimal value.
10084#@cli : $ image.jpg sample lena,lion,square +argmin
10085argmin :
10086  e[^-1] "Compute argmin of image$?."
10087  if !$! return fi
10088  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
10089  ${-max_whds},"argmin("$str")" k. nm [argmin]
10090
10091#@cli argminabs
10092#@cli : Compute the argminabs of selected images. Returns a single image
10093#@cli : with each pixel value being the index of the input image with minabs value.
10094argminabs :
10095  e[^-1] "Compute argminabs of image$?."
10096  if !$! return fi
10097  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
10098  ${-max_whds},"argminabs("$str")" k. nm [argminabs]
10099
10100#@cli asin : (+)
10101#@cli : Compute the pointwise arcsine of selected images.
10102#@cli : $ image.jpg +normalize -1,1 asin[-1]
10103#@cli : $ 300,1,1,1,'cut(x/w+0.1*u,0,1)' +asin display_graph 400,300
10104#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
10105
10106#@cli asinh : (+)
10107#@cli : Compute the pointwise hyperbolic arcsine of selected images.
10108
10109#@cli atan : (+)
10110#@cli : Compute the pointwise arctangent of selected images.
10111#@cli : $ image.jpg +normalize 0,8 atan[-1]
10112#@cli : $ 300,1,1,1,'4*x/w+u' +atan display_graph 400,300
10113#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
10114
10115#@cli atan2 : [x_argument] : (+)
10116#@cli : Compute the pointwise oriented arctangent of selected images.
10117#@cli : Each selected image is regarded as the y-argument of the arctangent function, while the
10118#@cli : specified image gives the corresponding x-argument.
10119#@cli : $ (-1,1) (-1;1) resize 400,400,1,1,3 atan2[1] [0] keep[1] mod {pi/8}
10120#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
10121
10122#@cli atanh : (+)
10123#@cli : Compute the pointwise hyperbolic arctangent of selected images.
10124
10125#@cli << : eq. to 'bsl'. : (+)
10126
10127#@cli bsl : value[%] : [image] : 'formula' : (no arg) : (+)
10128#@cli : Compute the bitwise left shift of selected images with specified value, image or \
10129# mathematical expression, or compute the pointwise sequential bitwise left shift of \
10130# selected images.
10131#@cli : (eq. to '<<').
10132#@cli : $ image.jpg bsl 'round(3*x/w,0)' cut 0,255
10133
10134#@cli >> : eq. to 'bsr'. : (+)
10135
10136#@cli bsr : value[%] : [image] : 'formula' : (no arg) : (+)
10137#@cli : Compute the bitwise right shift of selected images with specified value, image or \
10138# mathematical expression, or compute the pointwise sequential bitwise right shift of \
10139# selected images.
10140#@cli : (eq. to '>>').
10141#@cli : $ image.jpg bsr 'round(3*x/w,0)' cut 0,255
10142
10143#@cli cos : (+)
10144#@cli : Compute the pointwise cosine of selected images.
10145#@cli : $ image.jpg +normalize 0,{2*pi} cos[-1]
10146#@cli : $ 300,1,1,1,'20*x/w+u' +cos display_graph 400,300
10147#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
10148
10149#@cli cosh : (+)
10150#@cli : Compute the pointwise hyperbolic cosine of selected images.
10151#@cli : $ image.jpg +normalize -3,3 cosh[-1]
10152#@cli : $ 300,1,1,1,'4*x/w+u' +cosh display_graph 400,300
10153
10154#@cli deg2rad
10155#@cli : Convert pointwise angle values of selected images, from degrees to radians (apply 'i*pi/180').
10156deg2rad :
10157  e[^-1] "Convert pointwise angle values of image$?, from degrees to radians."
10158  * 0.017453292519943295
10159
10160#@cli / : eq. to 'div'. : (+)
10161
10162#@cli div : value[%] : [image] : 'formula' : (no arg) : (+)
10163#@cli : Divide selected images by specified value, image or mathematical expression, \
10164# or compute the pointwise quotient of selected images.
10165#@cli : (eq. to '/').
10166#@cli : $ image.jpg div '1+abs(cos(x/10)*sin(y/10))'
10167#@cli : $ image.jpg +norm add[-1] 1 +div
10168
10169#@cli div_complex : [divider_real,divider_imag],_epsilon>=0
10170#@cli : Perform division of the selected complex pairs (real1,imag1,...,realN,imagN) of images by
10171#@cli : specified complex pair of images (divider_real,divider_imag).
10172#@cli : In complex pairs, the real image must be always located before the imaginary image in the image list.
10173#@cli : Default value: 'epsilon=1e-8'.
10174div_complex : check ${3=1e-8}>=0
10175  e[^-1] "Divide complex pair$? by complex pair "${"pass$1,$2 -1"}" (with epsilon $3)."
10176  repeat int($!/2) pass${1,2} 0 l[$>,{$>+1},-2,-1]
10177    +*[1,2] +*[0,3] -[-2,-1] # bc-ad
10178    *[0] [2] *[1] [3] +[0,1] # ac+bd
10179    sqr[1,2] +[1,2] +[1] $3  # c^2+d^2
10180    /[2] [1] /[0,1]
10181  endl done
10182
10183#@cli == : eq. to 'eq'. : (+)
10184
10185#@cli eq : value[%] : [image] : 'formula' : (no arg) : (+)
10186#@cli : Compute the boolean equality of selected images with specified value, image or \
10187# mathematical expression, or compute the boolean equality of selected images.
10188#@cli : (eq. to '==').
10189#@cli : $ image.jpg round 40 eq {round(ia,40)}
10190#@cli : $ image.jpg +mirror x eq
10191
10192#@cli erf : (+)
10193#@cli : Compute the pointwise error function of selected images.
10194#@cli : $ image.jpg +normalize 0,2 erf[-1]
10195#@cli : $ 300,1,1,1,'7*x/w-3.5+u' +erf display_graph 400,300
10196
10197#@cli exp : (+)
10198#@cli : Compute the pointwise exponential of selected images.
10199#@cli : $ image.jpg +normalize 0,2 exp[-1]
10200#@cli : $ 300,1,1,1,'7*x/w+u' +exp display_graph 400,300
10201
10202#@cli >= : eq. to 'ge'. : (+)
10203
10204#@cli ge : value[%] : [image] : 'formula' : (no arg) : (+)
10205#@cli : Compute the boolean 'greater or equal than' of selected images with specified value, image
10206#@cli : or mathematical expression, or compute the boolean 'greater or equal than' of selected images.
10207#@cli : (eq. to '>=').
10208#@cli : $ image.jpg ge {ia}
10209#@cli : $ image.jpg +mirror x ge
10210
10211#@cli > : eq. to 'gt'. : (+)
10212
10213#@cli gt : value[%] : [image] : 'formula' : (no arg) : (+)
10214#@cli : Compute the boolean 'greater than' of selected images with specified value, image or \
10215# mathematical expression, or compute the boolean 'greater than' of selected images.
10216#@cli : (eq. to '>').
10217#@cli : $ image.jpg gt {ia}
10218#@cli : $ image.jpg +mirror x gt
10219
10220#@cli <= : eq. to 'le'. : (+)
10221
10222#@cli le : value[%] : [image] : 'formula' : (no arg) : (+)
10223#@cli : Compute the boolean 'less or equal than' of selected images with specified value, image or \
10224# mathematical expression, or compute the boolean 'less or equal than' of selected images.
10225#@cli : (eq. to '<=').
10226#@cli : $ image.jpg le {ia}
10227#@cli : $ image.jpg +mirror x le
10228
10229#@cli < : eq. to 'lt'. : (+)
10230
10231#@cli lt : value[%] : [image] : 'formula' : (no arg) : (+)
10232#@cli : Compute the boolean 'less than' of selected images with specified value, image or \
10233# mathematical expression, or compute the boolean 'less than' of selected images.
10234#@cli : (eq. to '<').
10235#@cli : $ image.jpg lt {ia}
10236#@cli : $ image.jpg +mirror x lt
10237
10238#@cli log : (+)
10239#@cli : Compute the pointwise base-e logarithm of selected images.
10240#@cli : $ image.jpg +add 1 log[-1]
10241#@cli : $ 300,1,1,1,'7*x/w+u' +log display_graph 400,300
10242
10243#@cli log10 : (+)
10244#@cli : Compute the pointwise base-10 logarithm of selected images.
10245#@cli : $ image.jpg +add 1 log10[-1]
10246#@cli : $ 300,1,1,1,'7*x/w+u' +log10 display_graph 400,300
10247
10248#@cli log2 : (+)
10249#@cli : Compute the pointwise base-2 logarithm of selected images
10250#@cli : $ image.jpg +add 1 log2[-1]
10251#@cli : $ 300,1,1,1,'7*x/w+u' +log2 display_graph 400,300
10252
10253#@cli max : value[%] : [image] : 'formula' : (no arg) : (+)
10254#@cli : Compute the maximum between selected images and specified value, image or \
10255# mathematical expression, or compute the pointwise maxima between selected images.
10256#@cli : $ image.jpg +mirror x max
10257#@cli : $ image.jpg max 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R'
10258
10259#@cli maxabs : value[%] : [image] : 'formula' : (no arg) : (+)
10260#@cli : Compute the maxabs between selected images and specified value, image or \
10261# mathematical expression, or compute the pointwise maxabs between selected images.
10262
10263#@cli m/ : eq. to 'mdiv'. : (+)
10264
10265#@cli mdiv : value[%] : [image] : 'formula' : (no arg) : (+)
10266#@cli : Compute the matrix division of selected matrices/vectors by specified value, image or \
10267# mathematical expression, or compute the matrix division of selected images.
10268#@cli : (eq. to 'm/').
10269
10270#@cli med
10271#@cli : Compute the median of selected images.
10272#@cli : $ image.jpg sample lena,lion,square +med
10273med :
10274  e[^-1] "Compute median of image$?."
10275  if !$! return fi
10276  13,$! eval. "!x?copy(i(),[[',i#'],vtos(y,10,10)])" =. 0 discard. 0 str={t} rm.
10277  ${-max_whds},"med("$str")" k. nm [med]
10278
10279#@cli min : value[%] : [image] : 'formula' : (no arg) : (+)
10280#@cli : Compute the minimum between selected images and specified value, image or \
10281# mathematical expression, or compute the pointwise minima between selected images.
10282#@cli : $ image.jpg +mirror x min
10283#@cli : $ image.jpg min 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R'
10284
10285#@cli minabs : value[%] : [image] : 'formula' : (no arg) : (+)
10286#@cli : Compute the minabs between selected images and specified value, image or \
10287# mathematical expression, or compute the pointwise minabs between selected images.
10288
10289#@cli % : eq. to 'mod'. : (+)
10290
10291#@cli mod : value[%] : [image] : 'formula' : (no arg) : (+)
10292#@cli : Compute the modulo of selected images with specified value, image or mathematical \
10293# expression, or compute the pointwise sequential modulo of selected images.
10294#@cli : (eq. to '%').
10295#@cli : $ image.jpg +mirror x mod
10296#@cli : $ image.jpg mod 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R'
10297
10298#@cli m* : eq. to 'mmul'. : (+)
10299
10300#@cli mmul : value[%] : [image] : 'formula' : (no arg) : (+)
10301#@cli : Compute the matrix right multiplication of selected matrices/vectors by specified value, image or \
10302# mathematical expression, or compute the matrix right multiplication of selected images.
10303#@cli : (eq. to 'm*').
10304#@cli : $ (0,1,0;0,0,1;1,0,0) (1;2;3) +mmul
10305
10306#@cli * : eq. to 'mul'. : (+)
10307
10308#@cli mul : value[%] : [image] : 'formula' : (no arg) : (+)
10309#@cli : Multiply selected images by specified value, image or mathematical expression, \
10310# or compute the pointwise product of selected images.
10311#@cli : (eq. to '*').
10312#@cli : See also: ''add'', ''sub'', ''div''.
10313#@cli : $ image.jpg +mul 2 cut 0,255
10314#@cli : $ image.jpg (1,2,3,4,5,6,7,8) ri[-1] [0] mul[0] [-1]
10315#@cli : $ image.jpg mul '1-3*abs(x/w-0.5)' cut 0,255
10316#@cli : $ image.jpg +luminance negate[-1] +mul
10317
10318#@cli mul_channels : value1,_value2,...,_valueN
10319#@cli : Multiply channels of selected images by specified sequence of values.
10320#@cli : $ image.jpg +mul_channels 1,0.5,0.8
10321mul_channels :
10322  e[^-1] "Multiply channels of image$? by value sequence ($*)."
10323  $=arg repeat $#,i
10324    fact=${arg{1+($>%$#)}}
10325    repeat $! if {$>,$i<s} sh[$>] $i *. $fact rm. fi done
10326  done
10327
10328#@cli mul_complex : [multiplier_real,multiplier_imag]
10329#@cli : Perform multiplication of the selected complex pairs (real1,imag1,...,realN,imagN) of images by
10330#@cli : specified complex pair of images (multiplier_real,multiplier_imag).
10331#@cli : In complex pairs, the real image must be always located before the imaginary image in the image list.
10332mul_complex :
10333  e[^-1] "Multiply complex pair$? by complex pair "${"pass$1,$2 -1"}"."
10334  repeat int($!/2) pass${1,2} 0 l[$>,{$>+1},-2,-1]
10335    +*[0,3] +*[1,2] +[-2,-1] # ad+bc
10336    *[0,2] *[1,2] -[0,1] # ac-bd
10337  endl done
10338
10339#@cli != : eq. to 'neq'. : (+)
10340
10341#@cli neq : value[%] : [image] : 'formula' : (no arg) : (+)
10342#@cli : Compute the boolean inequality of selected images with specified value, image or \
10343# mathematical expression, or compute the boolean inequality of selected images.
10344#@cli : (eq. to '!=').
10345#@cli : $ image.jpg round 40 neq {round(ia,40)}
10346
10347#@cli | : eq. to 'or'. : (+)
10348
10349#@cli or : value[%] : [image] : 'formula' : (no arg) : (+)
10350#@cli : Compute the bitwise OR of selected images with specified value, image or mathematical \
10351# expression, or compute the pointwise sequential bitwise OR of selected images.
10352#@cli : (eq. to '|').
10353#@cli : $ image.jpg or 128
10354#@cli : $ image.jpg +mirror x or
10355
10356#@cli ^ : eq. to 'pow'. : (+)
10357
10358#@cli pow : value[%] : [image] : 'formula' : (no arg) : (+)
10359#@cli : Raise selected images to the power of specified value, image or mathematical \
10360# expression, or compute the pointwise sequential powers of selected images.
10361#@cli : (eq. to '^').
10362#@cli : $ image.jpg div 255 +pow 0.5 mul 255
10363#@cli : $ image.jpg gradient pow 2 add pow 0.2
10364
10365#@cli rad2deg
10366#@cli : Convert pointwise angle values of selected images, from radians to degrees (apply 'i*180/pi').
10367rad2deg :
10368  e[^-1] "Convert pointwise angle values of image$?, from radians to degrees."
10369  * 57.295779513082323
10370
10371#@cli rol : value[%] : [image] : 'formula' : (no arg) : (+)
10372#@cli : Compute the bitwise left rotation of selected images with specified value, image or \
10373# mathematical expression, or compute the pointwise sequential bitwise left rotation of \
10374# selected images.
10375#@cli : $ image.jpg rol 'round(3*x/w,0)' cut 0,255
10376
10377#@cli ror : value[%] : [image] : 'formula' : (no arg) : (+)
10378#@cli : Compute the bitwise right rotation of selected images with specified value, image or \
10379# mathematical expression, or compute the pointwise sequential bitwise right rotation of \
10380# selected images.
10381#@cli : $ image.jpg ror 'round(3*x/w,0)' cut 0,255
10382
10383#@cli sign : (+)
10384#@cli : Compute the pointwise sign of selected images.
10385#@cli : $ image.jpg +sub {ia} sign[-1]
10386#@cli : $ 300,1,1,1,'cos(20*x/w+u)' +sign display_graph 400,300
10387
10388#@cli sin : (+)
10389#@cli : Compute the pointwise sine of selected images.
10390#@cli : $ image.jpg +normalize 0,{2*pi} sin[-1]
10391#@cli : $ 300,1,1,1,'20*x/w+u' +sin display_graph 400,300
10392#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
10393
10394#@cli sinc : (+)
10395#@cli : Compute the pointwise sinc function of selected images.
10396#@cli : $ image.jpg +normalize {-2*pi},{2*pi} sinc[-1]
10397#@cli : $ 300,1,1,1,'20*x/w+u' +sinc display_graph 400,300
10398
10399#@cli sinh : (+)
10400#@cli : Compute the pointwise hyperbolic sine of selected images.
10401#@cli : $ image.jpg +normalize -3,3 sinh[-1]
10402#@cli : $ 300,1,1,1,'4*x/w+u' +sinh display_graph 400,300
10403
10404#@cli sqr : (+)
10405#@cli : Compute the pointwise square function of selected images.
10406#@cli : $ image.jpg +sqr
10407#@cli : $ 300,1,1,1,'40*x/w+u' +sqr display_graph 400,300
10408
10409#@cli sqrt : (+)
10410#@cli : Compute the pointwise square root of selected images.
10411#@cli : $ image.jpg +sqrt
10412#@cli : $ 300,1,1,1,'40*x/w+u' +sqrt display_graph 400,300
10413
10414#@cli - : eq. to 'sub'. : (+)
10415
10416#@cli sub : value[%] : [image] : 'formula' : (no arg) : (+)
10417#@cli : Subtract specified value, image or mathematical expression to selected images, \
10418# or compute the pointwise difference of selected images.
10419#@cli : (eq. to '-').
10420#@cli : $ image.jpg +sub 30% cut 0,255
10421#@cli : $ image.jpg +mirror x sub[-1] [0]
10422#@cli : $ image.jpg sub 'i(w/2+0.9*(x-w/2),y)'
10423#@cli : $ image.jpg +mirror x sub
10424
10425#@cli tan : (+)
10426#@cli : Compute the pointwise tangent of selected images.
10427#@cli : $ image.jpg +normalize {-0.47*pi},{0.47*pi} tan[-1]
10428#@cli : $ 300,1,1,1,'20*x/w+u' +tan display_graph 400,300
10429#@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands
10430
10431#@cli tanh : (+)
10432#@cli : Compute the pointwise hyperbolic tangent of selected images.
10433#@cli : $ image.jpg +normalize -3,3 tanh[-1]
10434#@cli : $ 300,1,1,1,'4*x/w+u' +tanh display_graph 400,300
10435
10436#@cli xor : value[%] : [image] : 'formula' : (no arg) : (+)
10437#@cli : Compute the bitwise XOR of selected images with specified value, image or mathematical \
10438# expression, or compute the pointwise sequential bitwise XOR of selected images.
10439#@cli : $ image.jpg xor 128
10440#@cli : $ image.jpg +mirror x xor
10441
10442#---------------------------------
10443#
10444#@cli :: Values Manipulation
10445#
10446#---------------------------------
10447
10448#@cli apply_curve : 0<=smoothness<=1,x0,y0,x1,y1,x2,y2,...,xN,yN
10449#@cli : Apply curve transformation to image values.
10450#@cli : Default values: 'smoothness=1', 'x0=0', 'y0=100'.
10451#@cli : $ image.jpg +apply_curve 1,0,0,128,255,255,0
10452apply_curve : check "${1=1}>=0 && $1<=1" skip ${2=0},${3=100}
10453  e[^-1] "Apply intensity curve with smoothness $1 and keypoints (${2--1}) on image$?."
10454
10455  # Determine value mapping.
10456  (${^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]
10457
10458  # Apply value mapping.
10459  -[^-1] $vm *[^-1] {8191/($vM-$vm)} map[^-1] .,1 rm.
10460
10461#@cli apply_gamma : gamma>=0
10462#@cli : Apply gamma correction to selected images.
10463#@cli : $ image.jpg +apply_gamma 2
10464apply_gamma : check $1>=0
10465  e[^-1] "Apply Gamma-correction to image$?, with gamma $1."
10466  if $1==1 return fi
10467  repeat $! l[$>] mM={[im,iM]} n 0,1 ^ {1/$1} n $mM endl done
10468
10469#@cli balance_gamma : _ref_color1,...
10470#@cli : Compute gamma-corrected color balance of selected image, with respect to specified reference color.
10471#@cli : Default value: 'ref_color1=128'.
10472#@cli : $ image.jpg +balance_gamma 128,64,64
10473balance_gamma : check "isnum(${1=128})"
10474  e[^-1] "Apply gamma-corrected color balance of image$?, with reference color ("${^0}")."
10475  repeat $! l[$>]
10476    (${^0}) r. {-2,s},1,1,1,0,1 s.. c /. 255
10477    repeat $!-1 /[$>] 255 ^[$>] {log({@$>})/log({$>,ia})} *[$>] 255 done
10478    rm. a c c 0,255
10479  endl done
10480
10481#@cli cast : datatype_source,datatype_target
10482#@cli : Cast datatype of image buffer from specified source type to specified target type.
10483#@cli : 'datatype_source' and 'datatype_target' can be \
10484# { uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }.
10485cast :
10486  e[^-1] "Cast datatype of image buffer$? from '$1' to '$2'."
10487  stype="$1"
10488  if s='$stype';s[0]==_'u'" && "s[1]!=_'n' stype="unsigned_"{`s='$stype';s[1,size(s)-1]`}
10489  elif s='$stype';s[0]==_'u'" && "s[0,9]=='"unsigned "' stype="unsigned_"{`s='$stype';s[9,size(s)-9]`}
10490  fi
10491  dtype="$2"
10492  if s='$dtype';s[0]==_'u'" && "s[1]!=_'n' dtype="unsigned_"{`s='$dtype';s[1,size(s)-1]`}
10493  elif s='$dtype';s[0]==_'u'" && "s[0,9]=='"unsigned "' dtype="unsigned_"{`s='$dtype';s[9,size(s)-9]`}
10494  fi
10495  ssize={s='$stype';s=='"unsigned_char"'||s=='char'?1:s=='"unsigned_short"'||s=='short'?2:s=='"unsigned_int"'||\
10496         s=='int'||s=='float'?4:8}
10497  dsize={s='$dtype';s=='"unsigned_char"'||s=='char'?1:s=='"unsigned_short"'||s=='short'?2:s=='"unsigned_int"'||\
10498         s=='int'||s=='float'?4:8}
10499  repeat $! l[$>]
10500    w,h,d,s={[w,h,d,s]}
10501    serialize $1,0,0
10502    s -,{'\n$w\ $h\ $d\ $s\n'}
10503    i[1] ('\n1\ {int($w*$h*$d*$s*$ssize/$dsize)}\ 1\ 1\n') y[1]
10504    replace_str[0] $stype,$dtype
10505    a y unserialize
10506  endl done
10507
10508#@cli complex2polar
10509#@cli : Compute complex to polar transforms of selected images.
10510#@cli : $ image.jpg +fft complex2polar[-2,-1] log[-2] shift[-2] 50%,50%,0,0,2 remove[-1]
10511complex2polar :
10512  e[^-1] "Compute complex to polar transforms of image$?."
10513  repeat int($!/2) l[{2*$>},{2*$>+1}]
10514    ri[1] [0],3 +atan2[1] [0] nm. {1,n} sqr[-3,-2] +[-3,-2] sqrt..
10515  endl done
10516
10517#@cli compress_clut : _max_error>0,_avg_error>0,_max_nbpoints>=8 | 0 (unlimited),\
10518# _error_metric={ 0=L2-norm | 1=deltaE_1976 | 2=deltaE_2000 },_reconstruction_colorspace={ 0=srgb | 1=rgb | 2=lab },\
10519# _try_rbf_first={ 0 | 1 }
10520#@cli : Compress selected color LUTs as sequences of colored keypoints.
10521#@cli : Default values: 'max_error=1.5', 'avg_error=0.75', 'max_nb_points=2048', 'error_metric=2', \
10522# 'reconstruction_colorspace=0' and 'try_rbf_first=1'.
10523compress_clut : check "${1=1.5}>0 && ${2=0.75}>0 && isint(${3=2048}) && (!$3 || $3>=8) && isin(${4=2},0,1,2) && "\
10524                      "isin(${5=0},0,1,2) && isbool(${6=1})"
10525  e[^-1] "Compress color LUT$? as a set of colored keypoints, with maximum error $1, average error $2, "\
10526         "$3 maximum keypoints, "${"s0,s1,s2=L2-RGB,DeltaE_1976,DeltaE_2000 u $s$4"}" metric and "\
10527         ${"s0,s1,s2=srgb,rgb,lab u $s$5"}" colorspace for reconstruction."
10528  v 0
10529  max_error,avg_error,max_keypoints,metric,colorspace,try_rbf=${1-6}
10530  if $try_rbf method=rbf else method=pde fi
10531  repeat $! l[$>] nm={b}
10532    if iM>1024 / 257 fi # When input is 16bits
10533    if d==1 S={round(cbrt(wh))} r $S,$S,$S,100%,-1 fi # When input is a 2D haldclut image
10534    e[] "\n* Process CLUT '"$nm"' ("{w}"x"{h}"x"{d}")."
10535
10536    # Detect B&W cluts.
10537    if "s==3 &&
10538        crop(0,0,0,0,w,h,d,1)==crop(0,0,0,1,w,h,d,1) &&
10539        crop(0,0,0,0,w,h,d,1)==crop(0,0,0,2,w,h,d,1)"
10540      channels 0
10541    fi
10542
10543    _metric={s==3?$metric:0}
10544    if $_metric +srgb2lab fi
10545
10546    # Initialize keypoints (corners of the RGB cube).
10547    1,8,1,{s+3}
10548    eval "
10549      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 ];
10550      repeat (size(coords)/3,k,
10551        P = coords[3*k,3];
10552        I[k] = [ P, I(#0,round(P*([w#0,h#0,d#0]-1)/255)) ]
10553      )"
10554
10555    # Iteratively add keypoints.
10556    sep="\n"
10557    do
10558      +decompress_clut_$method. {0,[w,h,d]},$colorspace
10559      if !$_metric -. [0] norm. else srgb2lab. deltaE. [1],{$_metric-1}," " fi
10560      emax,eavg={[iM,ia]}
10561      e[] "\r"$sep"  > Add [#"{-2,h}"] Max_Err = "{_$emax}", Avg_Err = "{_$eavg}"         " sep=""
10562      if $emax<=$max_error" && "$eavg<=$avg_error rm. break fi
10563      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..
10564      a[-2,-1] y
10565      if $max_keypoints" && "h>=320" && "'$method'=='rbf' method=pde rows. 0,7 fi # RBF failed, switch to PDE method.
10566    while h<($max_keypoints?$max_keypoints:inf)
10567
10568    # Iteratively remove keypoints.
10569    if h>8
10570      if $_metric max_rounding,avg_rounding=0.1,0.025
10571      else max_rounding,avg_rounding=1,0.25
10572      fi
10573      if $emax>$max_error" || "$eavg>$avg_error
10574        max_error=round($max_error,$max_rounding,1)
10575        avg_error=round($avg_error,$avg_rounding,1)
10576      fi
10577      index=8 sep="\n"
10578      do
10579        +l. s y rm[$index] a y endl # Remove kth keypoint
10580        +decompress_clut_$method. {0,[w,h,d]},$colorspace
10581        if $_metric==0 -. [0] norm. else srgb2lab. deltaE. [1],{$_metric-1}," " fi
10582        emax,eavg={[iM,ia]} rm.
10583
10584        if $emax<=$max_error" && "$eavg<=$avg_error rv[-2,-1] else index+=1 fi
10585        e[] "\r"$sep"  > Rem [#"{min($index,h-1)}"/"{h}"] Max_Err = "{_$emax}", Avg_Err = "{_$eavg}"       "
10586        sep=""
10587        rm.
10588      while $index<h
10589    fi
10590    k.
10591
10592    # Sort keypoints in lexicographic order (increasing order for RBF, decreasing for PDE).
10593    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%
10594
10595    to_clutname $nm nm ${}
10596  endl done
10597
10598# compress_cluts : {"file_pattern" | "file_list.txt"},_max_error>0,_avg_error>0,_max_nb_points>=8
10599# Batch compress CLUT files.
10600compress_cluts : check "${2=1.5}>0 && ${3=0.75}>0 && isint(${4=2048}) && $4>8"
10601  rm
10602  if isfile(['{/"$1"}']) it[] "$1" s -,{'\n'}
10603  else files "$1" ('${}') s -,{','}
10604  fi
10605  rv
10606  repeat $! l[$<]
10607    filename={t} 0 nm. $filename ext={x} rm
10608    basename $filename
10609    l[] ('${}') replace_str .$ext,"" basename={t} rm endl
10610    need_compression=1
10611    cclut=cclut_$basename.gmz
10612
10613    if isfile(['{/$cclut}'])
10614      i $cclut
10615      if "dat = (date(5) + 60*(date(4) + date(2)*24));
10616          fdat = (date(5,'"{/$cclut}"') + 60*(date(4,'"{/$cclut}"') + date(2,'"{/$cclut}"')*24));
10617          !h && dat-fdat<30"
10618        e[] "* Skip file '"$filename"' (CLUT already being compressed)."
10619        need_compression=0
10620      elif h>0" && "h!=2048
10621        e[] "* Skip file '"$filename"' (CLUT already compressed)."
10622        need_compression=0
10623      fi
10624      rm.
10625    fi
10626
10627    if $need_compression
10628      0 o. $cclut rm. # Lock current file
10629      if lowercase(['$ext'])=='cube' input_cube $filename c. 0,255
10630      elif lowercase(['$ext'])=='png' i $filename S={round(cbrt(wh))} r $S,$S,$S,100%,-1
10631      else e[] "* Skip file '"$filename"' (unknown CLUT format)." continue
10632      fi
10633
10634      e[] "* Compress file '"$filename"'."
10635      if w>33 r3dx 33 fi
10636      to_rgb
10637      tic compress_clut $2,$3,$4 toc
10638      o $cclut
10639    fi
10640    rm
10641  endl done
10642
10643# cluts2libclut : "compressed_CLUT_collection.gmz"
10644# Convert specified compressed CLUT collection for C++ 'libclut'
10645# (https://framagit.org/dtschump/libclut)
10646cluts2libclut : skip ${1=$HOME/work/src/gmic/resources/gmic_cluts.gmz}
10647  e[] "Convert compressed CLUT collection '$1' to .png/.ppm/.txt for C++ libclut."
10648  l[]
10649    i $1
10650    0 nm. "$1" basename={b} rm.
10651    list=
10652    repeat $! l[$>] if s<6 r 100%,100%,1,6,0,1 fi transpose list.={n}"\n" endl done s c,2 a y
10653    o. $basename.png
10654    o. $basename.ppm
10655    ('$list') ot. $basename.txt
10656    rm
10657  endl
10658
10659#@cli compress_rle : _is_binary_data={ 0 | 1 },_maximum_sequence_length>=0
10660#@cli : Compress selected images as 2xN data matrices, using RLE algorithm.
10661#@cli : Set 'maximum_sequence_length=0' to disable maximum length constraint.
10662#@cli : Default values: 'is_binary_data=0' and 'maximum_sequence_length=0'.
10663#@cli : $ image.jpg resize2dy 100 quantize 4 round +compress_rle , +decompress_rle[-1]
10664compress_rle : check "isbool(${1=0}) && isint(${2=0}) && $2>=0"
10665  s0=" for binary data" s1=""
10666  if $2 s=", with maximal sequence length "$2 else s="" fi
10667  e[^-1] "Compress image$? using RLE algorithm"${s{!$1}}$s"."
10668  repeat $! l[$>] nm={0,n} im={im} header={w};{h};{d};{s};$im;{$1!=0}
10669    - $im y x ({{0,@-1}+1}) a x r 100%,3
10670    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)'
10671    if $2 # Constrain maximum sequence length.
10672      transpose mirror x
10673      f. '>x==2?i:!x?(j(0,-1)==$2?1:i!=1?j(0,-1)+1:1):(i==-1&&j(-1)==$2?y:i)'
10674      mirror x transpose
10675    fi
10676    z 0,{w-2} s y,3 y[1] discard[1] -1 warp[0,2] [1],0,0 rm[1]
10677    if $1 # Encode for binary data.
10678      !=[0] 0 *[0] 2 -[0] 1 *
10679    else # Encode for arbitrary data.
10680      *. -1 rv a x y discard -1 f '>i(0,y-1)<0&&i==0&&i(0,y+1)<0?-1:i' discard -1
10681    fi
10682    i[0] ($header) a y nm $nm
10683  endl done
10684
10685#@cli cumulate : { x | y | z | c }...{ x | y | z | c } : (no arg) : (+)
10686#@cli : Compute the cumulative function of specified image data, optionally along the specified axes.
10687#@cli : $ image.jpg +histogram 256 +cumulate[-1] display_graph[-2,-1] 400,300,3
10688
10689#@cli c : eq. to 'cut'. : (+)
10690
10691#@cli cut : { value0[%] | [image0] },{ value1[%] | [image1] } : [image] : (+)
10692#@cli : Cut values of selected images in specified range.
10693#@cli : (eq. to 'c').\n
10694#@cli : $ image.jpg +add 30% cut[-1] 0,255
10695#@cli : $ image.jpg +cut 25%,75%
10696
10697#@cli decompress_clut : _width>0,_height>0,_depth>0,_reconstruction_colorspace={ 0=srgb | 1=rgb | 2=lab }
10698#@cli : Decompress selected colored keypoints into 3D CLUTs, using a mixed RBF/PDE approach.
10699#@cli : Default values: 'width=height=depth=33' and 'reconstruction_colorspace=0'.
10700decompress_clut : check "isint(${1=33}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=$1}) && $3>0 && "\
10701                        "isin(${4=0},0,1,2)"
10702  e[^-1] "Decompress colored keypoint$? into $1x$2x$3 CLUTs, using "\
10703         ${"s0,s1=srgb,rgb u $s$4"}" colorspace for reconstruction."
10704  repeat $! l[$>]
10705    if "h>=320 || (P0 = I[0]; P1 = I[h - 1]; val(P) = (P[0]*65536 + P[1]*256 + P[2]); val(P0)>val(P1))"
10706      decompress_clut_pde. ${1-4}
10707    else
10708      decompress_clut_rbf. ${1-4}
10709    fi
10710  endl done
10711
10712#@cli decompress_clut_rbf : _width>0,_height>0,_depth>0,_reconstruction_colorspace={ 0=srgb | 1=rgb | 2=lab }
10713#@cli : Decompress selected colored keypoints into 3D CLUTs, using RBF thin plate spline interpolation.
10714#@cli : Default value: 'width=height=depth=33' and 'reconstruction_colorspace=0'.
10715decompress_clut_rbf : check "isint(${1=33}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=$1}) && $3>0 && "\
10716                            "isin(${4=0},0,1,2)"
10717  e[^-1] "Decompress colored keypoint$? into $1x$2x$3 CLUTs (RBF approach), using "\
10718         ${"s0,s1,s2=srgb,rgb,lab u $s$4"}" colorspace for reconstruction."
10719  repeat $! l[$>]
10720    if $4 s c,-3 srgb2${"arg $4,rgb,lab"}. a c fi
10721    rbf $1,$2,$3,0,0,0,255,255,255
10722    if $4 ${"arg $4,rgb,lab"}2srgb fi
10723  endl done
10724
10725#@cli decompress_clut_pde : _width>0,_height>0,_depth>0,_reconstruction_colorspace={ 0=srgb | 1=rgb | 2=lab }
10726#@cli : Decompress selected colored keypoints into 3D CLUTs, using multiscale diffusion PDE's.
10727#@cli : Default values: 'width=height=depth=33' and 'reconstruction_colorspace=0'.
10728decompress_clut_pde : check "isint(${1=33}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=$1}) && $3>0 && "\
10729                            "isin(${4=0},0,1,2)"
10730  e[^-1] "Decompress colored keypoint$? into $1x$2x$3 CLUTs (PDE approach), using "\
10731         ${"s0,s1,s2=srgb,rgb,lab u $s$4"}" colorspace for reconstruction."
10732  repeat $! l[$>] nm={n}
10733    if $4 s c,-3 srgb2${"arg $4,rgb,lab"}. a c fi
10734    2,2,2,{s-3}
10735    do
10736      +f. 0 .,.,.,1
10737      eval[0] "begin(fact = ([w#1,h#1,d#1] - 1)/255); PC = I; P = PC[0,3]; X = round(P*fact);
10738               I(#2,X)+=PC[3,s-3]; ++i(#3,X); I"
10739      f. "*i?(I(#2)/=i;1):0"
10740      if im rm[-3,-1] # No missing data so far
10741      else # Reconstruct missing data by anisotropic diffusion scheme
10742        +distance. 1 .,.,.,3
10743        eval.. "* # Specific gradient discretization for distance function
10744          const boundary = 1;
10745          maxabs(a,b) = (abs(a)>abs(b)?a:b);
10746          I(#-1) = [ maxabs(j(1) - i,i - j(-1)),
10747                     maxabs(j(0,1) - i,i - j(0,-1)),
10748                     maxabs(j(0,0,1) - i,i - j(0,0,-1)) ]"
10749        orientation. rm..
10750        repeat 20
10751          j[-4] ...,0,0,0,0,1,..
10752          +warp[-4] .,1,2,1 *.. -1 warp[-5] ..,1,2,1 +[-5,-1] /[-4] 2
10753        done
10754        j[-4] ...,0,0,0,0,1,.. k[0,1]
10755      fi
10756      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
10757    while 1
10758    k.
10759    if $4 ${"arg $4,rgb,lab"}2srgb fi
10760    nm $nm
10761  endl done
10762  um srgb2srgb
10763
10764#@cli decompress_rle
10765#@cli : Decompress selected data vectors, using RLE algorithm.
10766decompress_rle :
10767  e[^-1] "Decompress data vector$?, using RLE algorithm."
10768  repeat $! l[$>]
10769
10770    # Retrieve original data dimension and min value.
10771    y whds={0,@0-3} im={0,@4} is_binary_data={0,@5} rows 6,100%
10772
10773    # Transform RLE data to list of pairs {nb_occurrences,value}.
10774    if $is_binary_data  # Decode for binary data.
10775      +>= 0 abs[0] a x
10776    else # Decode for arbitrary data
10777      +<. 0
10778      (0;1;1)
10779      erode.. .,0 rm. -. 1 a x discard -1 # Get back compressed '0' (minimum) values.
10780      +< 0 (0;1;1) dilate.. . rm. *. -2 +. 1 # Get back singletons.
10781      rv abs. a x discard -1
10782      r 2,{h/2},1,1,-1
10783    fi
10784
10785    # Decompress, using 3D objects.
10786    s y,-256
10787    repeat $! l[$>]
10788      i[0] ('CImg3d') +[0] 0.5
10789      i[1] ({2*h};{h})
10790      s. x,2 cumulate.. siz={-2,@-1}
10791      +shift.. 0,1 -... 1 rv[-3,-1] z[-3,-1] 0,2 a[-3,-1] x
10792      i[3] (2,0,1;2,{2*h-2},{2*h-1}) r[3] 3,{h},1,1,3 round[3]
10793      r[4] 3 1,100%,1,1,1 y a y
10794      $siz j3d. ..,0,0,0,1,2,0,0 rm..
10795    endl done
10796    a x r $whds,-1 + $im
10797  endl done
10798
10799#@cli discard : _value1,_value2,... : { x | y | z | c}...{ x | y | z | c},_value1,_value2,... : (no arg) : (+)
10800#@cli : Discard specified values in selected images or discard neighboring duplicate values,
10801#@cli : optionally only for the values along the first of a specified axis.
10802#@cli : If no arguments are specified, neighboring duplicate values are discarded.
10803#@cli : If all pixels of a selected image are discarded, an empty image is returned.
10804#@cli : $ (1;2;3;4;3;2;1) +discard 2
10805#@cli : $ (1,2,2,3,3,3,4,4,4,4) +discard x
10806
10807#@cli eigen2tensor
10808#@cli : Recompose selected pairs of eigenvalues/eigenvectors as 2x2 or 3x3 tensor fields.
10809#@cli : $$
10810eigen2tensor :
10811  e[^-1] "Recompose pairs in eigen field$? as 2x2 or 3x3 tensor fields."
10812  repeat $!/2 l[$>,{$>+1}] nm={0,n}
10813    if s==2 # 2D tensors.
10814      s. c
10815      +sqr. *.. ... sqr... # u^2 uv v^2
10816      sh. +*... -1
10817      sh[-5]     # v^2 -uv u^2
10818      a[-3--1] c a[-4--2] c
10819      sh... 0 *[-3,-1]          # l1*(u^2;uv;v^2)
10820      sh... 1 *[-2,-1]          # l2*(v^2;-uv;u^2)
10821      rm... +[-2,-1]
10822    elif s==6 # 3D tensors.
10823      s. c
10824      l[-6--4] +sqr.. +*[-2,-3] +sqr... *[-5] [-6] *[-4] [-6] sqr[-6] a c endl
10825      l[-3--1] +sqr.. +*[-2,-3] +sqr... *[-5] [-6] *[-4] [-6] sqr[-6] a c endl
10826      s... c
10827      -[-5] ... -[-4] ... *.. [-5] *. [-4]
10828      (1^0^0^1^0^1) ri. ... *. [-4] rm[-6--4] +[-3--1]
10829    else error[0--3] "Command '$0': Invalid image ["{$!-$>-1}"] : Dimensions "{w}","{h}","{d}","{s}" does
10830                          not represent a field of 2D or 3D eigenvectors."
10831    fi
10832  nm $nm endl done
10833
10834#@cli endian : _datatype : (+)
10835#@cli : Reverse data endianness of selected images, eventually considering the pixel being of the specified datatype.
10836#@cli : 'datatype' can be { bool | uchar | char | ushort | short | uint | int | uint64 | int64 | float | double }.
10837#@cli : This command does nothing for 'bool', 'uchar' and 'char' datatypes.
10838
10839#@cli equalize : _nb_levels>0[%],_value_min[%],_value_max[%] : (+)
10840#@cli : Equalize histograms of selected images.
10841#@cli : If value range is specified, the equalization is done only for pixels in the specified
10842#@cli : value range.
10843#@cli : Default values: 'nb_levels=256', 'value_min=0%' and 'value_max=100%'.
10844#@cli : $ image.jpg +equalize
10845#@cli : $ image.jpg +equalize 4,0,128
10846
10847#@cli f : eq. to 'fill'. : (+)
10848
10849#@cli fill : value1,_value2,... : [image] : 'formula' : (+)
10850#@cli : Fill selected images with values read from the specified value list, existing image
10851#@cli : or mathematical expression. Single quotes may be omitted in 'formula'.
10852#@cli : (eq. to 'f').
10853#@cli : $ 4,4 fill 1,2,3,4,5,6,7
10854#@cli : $ 4,4 (1,2,3,4,5,6,7) fill[-2] [-1]
10855#@cli : $ 400,400,1,3 fill "X=x-w/2; Y=y-h/2; R=sqrt(X^2+Y^2); a=atan2(Y,X); \
10856# if(R<=180,255*abs(cos(c+200*(x/w-0.5)*(y/h-0.5))),850*(a%(0.1*(c+1))))"
10857#@cli : $$
10858
10859#@cli index : { [palette] | palette_name },0<=_dithering<=1,_map_palette={ 0 | 1 } : (+)
10860#@cli : Index selected vector-valued images by specified vector-valued palette.
10861#@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | algae | amp |\
10862# balance | curl | deep | delta | dense | diff | haline | ice | matter | oxy | phase | rain | solar | speed | tarn |\
10863# tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | uzebox }
10864#@cli : Default values: 'dithering=0' and 'map_palette=0'.
10865#@cli : $ image.jpg +index 1,1,1
10866#@cli : $ image.jpg (0;255;255^0;128;255^0;0;255) +index[-2] [-1],1,1
10867#@cli : $$ https://gmic.eu/tutorial/gindex
10868index : check "${2=0}>=0 && $2<=1 && isbool(${3=0})"
10869  names=${-_palette_names} N={narg($names)}
10870  l[] if isint("$1") name=${"arg 1+($1%"$N"),"$names} else name="$1" fi onfail name="$1" endl
10871  e[^-1] "Index values in image$? by color LUT '"$name"', with dithering level $2."
10872  palette $1 index[^-1] .,$2,$3 rm.
10873
10874#@cli ir : eq. to 'inrange'.
10875ir : check "isbool(${3=1}) && isbool(${4=$3})"
10876  _gmic_s="$?" v + _inrange $*
10877
10878#@cli inrange : min[%],max[%],_include_min_boundary={ 0=no | 1=yes },_include_max_boundary={ 0=no | 1=yes }
10879#@cli : Detect pixels whose values are in specified range `[min,max]`, in selected images.
10880#@cli : (eq. to 'ir').
10881#@cli : Default value: 'include_min_boundary=include_max_boundary=1'.
10882#@cli : $ image.jpg +inrange 25%,75%
10883inrange : check "isbool(${3=1}) && isbool(${4=$3})"
10884  _gmic_s="$?" v + _$0 $*
10885
10886_inrange : skip "${3=1},${4=$3}"
10887  v={$3+2*$4}
10888  s=${"arg 1+"$v",out,\" min\",\" max\",\" min and max\""}
10889  if $v%3 b="y" else b="ies" fi
10890  e[0--3] "Extract pixel values in range [$1,$2] in image"$_gmic_s", with"$s" boundar"$b" included."
10891  repeat $! l[$>]
10892    m=$1 M=$2
10893    if ${is_percent\ $1} m={im+(iM-im)*$1} fi
10894    if ${is_percent\ $2} M={im+(iM-im)*$2} fi
10895    f. inrange(i,$m,$M,$3)
10896  endl done
10897
10898#@cli map : [palette],_boundary_conditions : palette_name,_boundary_conditions : (+)
10899#@cli : Map specified vector-valued palette to selected indexed scalar images.
10900#@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | algae | amp | \
10901# balance | curl | deep | delta | dense | diff | gray | haline | ice | matter | oxy | phase | rain | solar | speed | \
10902# tarn | tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | uzebox }
10903#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
10904#@cli : Default value: 'boundary_conditions=0'.
10905#@cli : $ image.jpg +luminance map[-1] 3
10906#@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 \
10907# ycbcr2rgb[-1]
10908#@cli : $$
10909map : check "isint(${2=0}) && $2>=0 && $2<=3"
10910  s0,s1,s2,s3=dirichlet,neumann,periodic,mirror boundary=${s$2}
10911  names=${-_palette_names} N={narg($names)}
10912  l[] if isint("$1") name=${"arg 1+($1%"$N"),"$names} else name="$1" fi onfail name="$1" endl
10913  e[^-1] "Map color LUT '"$name"' on image$?, with "$boundary" boundary conditions."
10914  palette $1 map[^-1] .,$2 rm.
10915
10916#@cli mix_channels : (a00,...,aMN) : [matrix]
10917#@cli : Apply specified matrix to channels of selected images.
10918#@cli : $ image.jpg +mix_channels (0,1,0;1,0,0;0,0,1)
10919mix_channels :
10920  e[^-1] "Apply matrix $1 to channels of image$?."
10921  if ${"is_image_arg $1"} pass$1 1 else i ${^0} fi
10922  repeat $!-1 l[$>] nm={n}
10923    whd={[w,h,d]} r. {[whd,s]},1,1,-1
10924    pass. 0 mv. 0 m* r $whd,{h},-1
10925  nm $nm endl done rm.
10926
10927#@cli negate : base_value : (no arg)
10928#@cli : Negate image values.
10929#@cli : Default value: 'base_value=(undefined)'.
10930#@cli : $ image.jpg +negate
10931negate : skip "${1=,}"
10932  if isnum("$*")
10933    e[0--3] "Negate values of image$?, according to base value $*."
10934    - {"$*"} * -1
10935  else
10936    e[0--3] "Negate values of image$?."
10937    repeat $! -[$>] {$>,iM} done * -1
10938    if ['"$1"']!=',' noarg fi
10939  fi
10940
10941#@cli noise : std_deviation>=0[%],_noise_type : (+)
10942#@cli : Add random noise to selected images.
10943#@cli : 'noise_type' can be { 0=gaussian | 1=uniform | 2=salt&pepper | 3=poisson | 4=rice }.
10944#@cli : Default value: 'noise_type=0'.
10945#@cli : $ image.jpg +noise[0] 50,0 +noise[0] 50,1 +noise[0] 10,2 cut 0,255
10946#@cli : $ 300,300,1,3 [0] noise[0] 20,0 noise[1] 20,1 +histogram 100 display_graph[-2,-1] 400,300,3
10947
10948#@cli noise_perlin : _scale_x[%]>0,_scale_y[%]>0,_scale_z[%]>0,_seed_x,_seed_y,_seed_z
10949#@cli : Render 2D or 3D Perlin noise on selected images, from specified coordinates.
10950#@cli : The Perlin noise is a specific type of smooth noise,
10951#@cli : described here : <https://en.wikipedia.org/wiki/Perlin_noise>.
10952#@cli : Default values: 'scale_x=scale_y=scale_z=16' and 'seed_x=seed_y=seed_z=0'.
10953#@cli : $ 500,500,1,3 noise_perlin ,
10954noise_perlin : check "${1=16}>0 && ${2=$1}>0 && ${3=$1}>0 && isnum(${4=0}) && isnum(${5=0}) && isnum(${6=0})"
10955  e[^-1] "Render Perlin noise on image$?, with scales (${1-3}) and seeds (${4-6})."
10956
10957  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,
10958                        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,
10959                        237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,
10960                        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,
10961                        216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,
10962                        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,
10963                        16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,
10964                        43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,
10965                        34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,
10966                        181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,
10967                        24,72,243,141,128,195,78,66,215,61,156,180 ];
10968        p = [ permutation,permutation ];
10969        fade(t) = (t*t*t*(t*(t*6 - 15) + 10));
10970        _lerp(t,a,b) = lerp(a,b,t);
10971        pcmod255 = vectors(); repeat (s,k,pcmod255[k] = p[k%255]);
10972        sx = ['$1']; is_px = sx[size(sx) - 1]==_'%';
10973        sy = ['$2']; is_py = sy[size(sy) - 1]==_'%';
10974        sz = ['$3']; is_pz = sz[size(sz) - 1]==_'%';
10975        x0 = $4;
10976        y0 = $5;
10977        z0 = $6;
10978        x1 = x0 + (is_px?1/$1:w/$1);
10979        y1 = y0 + (is_py?1/$2:h/$2);
10980        z1 = z0 + (is_pz?1/$3:d/$3);
10981        fw = (x1 - x0)/max(w - 1,1);
10982        fh = (y1 - y0)/max(h - 1,1);
10983        fd = (z1 - z0)/max(d - 1,1);"
10984
10985  repeat $! if {$>,d>1} # 3D version
10986    f[$>] "*begin("$init"
10987         grad(hash,x,y,z) = ( gh = hash&15; gu = gh<8?x:y; gv = gh<4?y:gh==12 || gh==14?x:z;
10988                             (!(gh&1)?gu:-gu) + (!(gh&2)?gv:-gv))
10989       );
10990       x = x0 + x*fw + pcmod255[c]; y = y0 + y*fh + pcmod255[c]; z = z0 + z*fd + pcmod255[c];
10991       ix = floor(x); iy = floor(y); iz = floor(z);
10992       X = ix&255; Y = iy&255; Z = iz&255;
10993       fx = x - ix; fy = y - iy; fz = z - iz;
10994       u = fade(fx); v = fade(fy); w = fade(fz);
10995       A = p[X] + Y; AA = p[A] + Z; AB = p[A + 1] + Z;
10996       B = p[X + 1] + Y; BA = p[B] + Z; BB = p[B + 1] + Z;
10997       fx1 = fx - 1; fy1 = fy - 1; fz1 = fz - 1;
10998       lerp(lerp(lerp(grad(p[AA],fx,fy,fz),
10999                      grad(p[BA],fx1,fy,fz),u),
11000                 lerp(grad(p[AB],fx,fy1,fz),
11001                      grad(p[BB],fx1,fy1,fz),u),v),
11002            lerp(lerp(grad(p[AA + 1],fx,fy,fz1),
11003                      grad(p[BA + 1],fx1,fy,fz1,u),
11004                 lerp(grad(p[AB + 1],fx,fy1,fz1),
11005                      grad(p[BB+1],fx1,fy1,fz1),u),v),w)"
11006  else # 2D version
11007    f[$>] "*begin("$init"
11008         grad(hash,x,y) = ( gh = hash&15; gu = gh<8?x:y; gv = gh<4?y:gh==12 || gh==14?x:0;
11009                            (!(gh&1)?gu:-gu) + (!(gh&2)?gv:-gv))
11010       );
11011       x = x0 + x*fw + pcmod255[c]; y = y0 + y*fh + pcmod255[c];
11012       ix = floor(x); iy = floor(y);
11013       X = ix&255; Y = iy&255;
11014       fx = x - ix; fy = y - iy;
11015       u = fade(fx); v = fade(fy);
11016       A = p[X] + Y; B = p[X + 1] + Y;
11017       fx1 = fx - 1; fy1 = fy - 1;
11018       lerp(lerp(grad(p[A],fx,fy),
11019                 grad(p[B],fx1,fy),u),
11020            lerp(grad(p[A + 1],fx,fy1),
11021                 grad(p[B + 1],fx1,fy1),u),v)"
11022  fi done
11023
11024#@cli noise_poissondisk : _radius[%]>0,_max_sample_attempts>0
11025#@cli : Add poisson disk sampling noise to selected images.
11026#@cli : Implements the algorithm from the article "Fast Poisson Disk Sampling in Arbitrary Dimensions",
11027#@cli : by Robert Bridson (SIGGRAPH'2007).
11028#@cli : Default values: 'radius=8' and 'max_sample_attempts=30'.
11029#@cli : $ 300,300 noise_poissondisk 8
11030##### : Original G'MIC code by Garagecoder (Andy Kelday)
11031noise_poissondisk : check "${1=8}>0 && ${2=30}>0"
11032  e[^-1] "Add poisson disk sampling points to image$?, with radius $1 and max sample attempts $2."
11033  repeat $! l[$>]
11034    R={${"is_percent $1"}?max(w,h,d)*$1:$1} # [0] input image to draw samples on
11035    dim={d>1?3:h>1?2:1} cw={0.999*$R/sqrt($dim)} # dimensions, grid cell width
11036    ({[w,h,d,1]}) y. c  # [1] image dimensions vector
11037    {[ceil(I/$cw)]}     # [2] "accelerator" grid/cells
11038    r[1] 1,1,1,$dim,-1  # keep only used dimensions in [1]
11039    1,1,1,$dim 1,1,1,1  # [3] samples list, [4] active list
11040    {vector$dim(2*ceil(sqrt($dim))+1)} r. 100%,100%,100%,2 # [5] cell proximity kernel
11041    f. "P=[x,y,z]-int([w/2,h/2,d/2]);[sum(sqr(P)),dot(P,[1,w#2,w#2*h#2])]"
11042    r. {[whd,s,1,1,-1]} sort. +,x z. 0,1,100%,100% y. c # sort kernel by distance
11043    nm[1] dims nm[2] grid nm[3] samples nm[4] active nm[5] prox
11044    eval "
11045      begin(
11046        dotoff = resize([ 1,w#2,w#2*h#2 ],d#2>1?3:h#2>1?2:1,0);
11047      );
11048      const N = "$dim";
11049      const radius = "$R";
11050      const grid_cw = "$cw";
11051      const max_sample_attempts = $2;
11052      const value = iM#0 + (im#0==iM#0);
11053      mag2(vec) = sum(sqr(vec));
11054      prox = I#5;
11055      lim = I#1;
11056
11057      da_push(#3,I#1);                    # Dummy sample to simplify bounds checks
11058      da_push(#3,u(I#1));                 # Add initial sample to list
11059      da_push(#4,1);                      # Add its index to active list
11060      I(#2,int(I[#3,1]/grid_cw)) = 1;     # Add its index to grid cell
11061      I(#0,I[#3,1]) = value;              # Draw the point
11062
11063      while (da_size(#4)>0,
11064        R = int(u(da_size(#4) - 1e-4));   # Choose a random active list index
11065        P = i[#4,R];                      # Get the index of that sample
11066        T = I[#3,P];                      # Position vector of that sample
11067
11068        repeat (max_sample_attempts,attempts,
11069          do (S = 4*(u(vectorN(1)) - 0.5); M = mag2(S), M <= 1 || M > 4);
11070
11071          X = T + radius * S;             # Potential sample from annulus around T
11072          if (min(X)<0 || min(lim-X)<0, continue()); # Check within bounds
11073
11074          # check proximity of surrounding points
11075          G = int(X/grid_cw);             # Grid cell position vector
11076          GI = dot(G,dotoff);             # Grid cell direct buffer index
11077
11078          for (K = 0; rejected = 0, K<size(prox), ++K,
11079            V = i[#2,GI+prox[K]];         # Sample index from grid to check
11080            if (V>0 && mag2(I[#3,V]-X)<sqr(radius), rejected = 1; break())
11081          );
11082
11083          if (!rejected,
11084            Q = da_size(#3);              # Sample found, get new index
11085            da_push(#3,X);                # Insert into samples list
11086            da_push(#4,Q);                # Insert its index into active
11087            I(#2,G) = Q;                  # Insert its index into grid
11088            I(#0,X) = value;              # Draw the point
11089            break();
11090          );
11091        );
11092        if (attempts==max_sample_attempts, da_remove(#4,R));
11093      )"
11094    k[0]
11095  endl done
11096
11097#@cli normp : p>=0
11098#@cli : Compute the pointwise Lp-norm norm of vector-valued pixels in selected images.
11099#@cli : Default value: 'p=2'.
11100#@cli : $ image.jpg +normp[0] 0 +normp[0] 1 +normp[0] 2 +normp[0] inf
11101normp : check "isnum(${1==2}) && $1>=0"
11102  e[^-1] "Compute pointwise L"$1"-norm of vectors, in image$?."
11103  if $1==0 != 0 compose_channels +
11104  elif $1==1 abs compose_channels +
11105  elif $1==2 norm
11106  elif $1==inf abs compose_channels max
11107  else ^ $1 compose_channels + ^ {1/$1}
11108  fi
11109
11110#@cli norm
11111#@cli : Compute the pointwise euclidean norm of vector-valued pixels in selected images.
11112#@cli : $ image.jpg +norm
11113#@cli : $$
11114norm :
11115  e[^-1] "Compute pointwise euclidean norm of vectors, in image$?."
11116  sqr compose_channels + sqrt
11117
11118#@cli n : eq. to 'normalize'. : (+)
11119
11120#@cli normalize : { value0[%] | [image0] },{ value1[%] | [image1] },_constant_case_ratio : [image] : (+)
11121#@cli : Linearly normalize values of selected images in specified range.
11122#@cli : (eq. to 'n').
11123#@cli : $ image.jpg split x,2 normalize[-1] 64,196 append x
11124#@cli : $$
11125
11126#@cli normalize_l2
11127#@cli : Normalize selected images such that they have a unit L2 norm.
11128normalize_l2 :
11129  e[^-1] "Normalize image$?, s.t they have a unit L2 norm."
11130  repeat $! /[$>] {norm={$>,in};if(norm!=0,norm,1)} done
11131
11132#@cli normalize_sum
11133#@cli : Normalize selected images such that they have a unit sum.
11134#@cli : $ image.jpg +histogram 256 normalize_sum[-1] display_graph[-1] 400,300
11135normalize_sum :
11136  e[^-1] "Normalize image$?, s.t they have a unit sum."
11137  repeat $! /[$>] {sum={$>,is};if(sum!=0,sum,1)} done
11138
11139#@cli not
11140#@cli : Apply boolean not operation on selected images.
11141#@cli : $ image.jpg +ge 50% +not[-1]
11142not :
11143  e[^-1] "Apply boolean not operation on image$?."
11144  == 0
11145
11146#@cli orientation
11147#@cli : Compute the pointwise orientation of vector-valued pixels in selected images.
11148#@cli : $ image.jpg +orientation +norm[-2] negate[-1] mul[-2] [-1] reverse[-2,-1]
11149#@cli : $$
11150orientation :
11151  e[^-1] "Compute pointwise orientation vectors, in image$?."
11152  repeat $! +norm[$>] replace. 0,1 /[$>,-1] done
11153
11154#@cli oneminus
11155#@cli : For each selected image, compute one minus image.
11156#@cli : $ image.jpg normalize 0,1 +oneminus
11157oneminus :
11158  e[^-1] "Compute one minus selected images$?."
11159  * -1 + 1
11160
11161#@cli otsu : _nb_levels>0
11162#@cli : Hard-threshold selected images using Otsu's method.
11163#@cli : The computed thresholds are returned as a list of values in the status.
11164#@cli : Default value: 'nb_levels=256'.
11165#@cli : $ image.jpg luminance +otsu ,
11166otsu : check "isint(${1=256}) && $1>0"
11167  e[^-1] "Hard-threshold image$? using Otsu\47s method, with $1 histogram levels."
11168  repeat $! l[$>]
11169    imM={[im,iM]} +histogram $1,$imM
11170    otsu={"
11171      sum = sumB = wB = best_variance = best_t = 0;
11172      repeat (w,t,sum+=t*i[t]);
11173      repeat (w,t,
11174        wB+=i[t];
11175        if (!wB, continue());
11176        wF = whds#-2 - wB;
11177        if (!wF, break());
11178        sumB+=t*i[t];
11179        mB = sumB/wB;
11180        mF = (sum - sumB)/wF;
11181        variance = wB*wF*(mB - mF)^2;
11182        if (variance>best_variance, best_variance = variance; best_t = t);
11183      );
11184      imM = ["$imM"];
11185      imM[0] + best_t*(imM[1] - imM[0])/(w - 1)"}
11186    rm. >=. $otsu
11187    if $> u ${},$otsu else u $otsu fi
11188  endl done
11189
11190#@cli polar2complex
11191#@cli : Compute polar to complex transforms of selected images.
11192polar2complex :
11193  e[^-1] "Compute polar to complex transforms of image$?."
11194  repeat int($!/2) l[{2*$>},{2*$>+1}]
11195    ri[1] [0],3 +sin. cos.. *. ... *[-3,-2]
11196  endl done
11197
11198#@cli quantize : nb_levels>=1,_keep_values={ 0 | 1 },_quantization_type={ -1=median-cut | 0=k-means | 1=uniform }
11199#@cli : Quantize selected images.
11200#@cli : Default value: 'keep_values=1' and 'quantization_type=0'.
11201#@cli : $ image.jpg luminance +quantize 3
11202#@cli : $ 200,200,1,1,'cos(x/10)*sin(y/10)' +quantize[0] 6 +quantize[0] 4 +quantize[0] 3 +quantize[0] 2
11203quantize : check "isint($1) && $1>=1 && isbool(${2=1}) && isint(${3=0}) && $3>=-1 && $3<=1"
11204  e[^-1] "Quantize image$? using $1 levels, "${arg\ 1+!$2,with,without}" keeping value range."
11205  repeat $! l[$>]
11206    if $3==1 # Uniform quantization.
11207      if s==1 # Greyscale image.
11208        if $2 mM={[im,iM]} n 0,$1 round 1,-1 min {$1-1} n $mM
11209        else n 0,$1 round 1,-1 min {$1-1} fi
11210      else mM={[im,iM]} uniform_distribution $1,{s} n. $mM index.. .,0,$2 rm.
11211      fi
11212    else +colormap $1,{!$3},1 index.. .,0,$2 rm. # Non-uniform quantization.
11213    fi
11214  endl done
11215
11216#@cli quantize_area : _min_area>0
11217#@cli : Quantize selected images such that each flat region has an area greater or equal to 'min_area'.
11218#@cli : Default value: 'min_area=10'.
11219#@cli : $ image.jpg quantize 3 +blur 1 round[-1] +quantize_area[-1] 2
11220quantize_area : check "${1=10}>0"
11221  e[^-1] "Quantize image$? by regions of areas greater than $1."
11222  if $1==1 return fi
11223  repeat $! l[$>]
11224    if s>1 +f. "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm. round. 0.01 else [0] fi
11225    area. 0,0 <. $1
11226    do
11227      [0]
11228      f.. "*
11229        begin(
11230          const boundary = 1;
11231          offx = [ -1,1,0,0,0,0 ];
11232          offy = [ 0,0,-1,1,0,0 ];
11233          offz = [ 0,0,0,0,-1,1 ];
11234          nb_offs = d>1?6:h>1?4:2;
11235        );
11236        is_neighbor = j(-1) && j(-1);
11237        if (h>1, is_neighbor&=j(1,0) && j(0,1));
11238        if (d>1, is_neighbor&=j(0,0,-1) && j(0,0,1));
11239        is_neighbor = !is_neighbor;
11240        i && is_neighbor?(
11241          col0 = I(#0);
11242          kmin = -1;
11243          distmin = inf;
11244          repeat (nb_offs,k,
11245            p = offx[k];
11246            q = offy[k];
11247            r = offz[k];
11248            if (!j(p,q,r),
11249              col = J(#0,p,q,r);
11250              dist = norm(col-=col0);
11251              if (dist<distmin, distmin = dist; kmin = k);
11252            );
11253          );
11254          if (kmin>=0,
11255            I(#-1) = J(#0,offx[kmin],offy[kmin],offz[kmin]);
11256            0,
11257            1
11258          );
11259        ):i"
11260      rv[0,-1] rm.
11261    while iM
11262    rm.
11263  endl done
11264
11265#@cli rand : { value0[%] | [image0] },_{ value1[%] | [image1] } : [image] : (+)
11266#@cli : Fill selected images with random values uniformly distributed in the specified range.
11267#@cli : $ 400,400,1,3 rand -10,10 +blur 10 sign[-1]
11268
11269#@cli replace : source,target
11270#@cli : Replace pixel values in selected images.
11271#@cli : $ (1;2;3;4) +replace 2,3
11272replace :
11273  e[^-1] "Replace pixel values $1 with $2 in image$?."
11274  f "i==$1?$2:i"
11275
11276#@cli replace_inf : _expression
11277#@cli : Replace all infinite values in selected images by specified expression.
11278#@cli : $ (0;1;2) log +replace_inf 2
11279replace_inf :
11280  e[^-1] "Replace all infinite values in image$? by expression '$1'."
11281  f "isinf(i)?$1:i"
11282
11283#@cli replace_nan : _expression
11284#@cli : Replace all NaN values in selected images by specified expression.
11285#@cli : $ (-1;0;2) sqrt +replace_nan 2
11286replace_nan :
11287  e[^-1] "Replace all NaN values in images$? by expression '$1'."
11288  f "isnan(i)?$1:i"
11289
11290#@cli replace_naninf : _expression
11291#@cli : Replace all NaN and infinite values in selected images by specified expression.
11292replace_naninf :
11293  e[^-1] "Replace all NaN and infinite values in images$? by expression '$1'."
11294  f "isnan(i) || isinf(i)?$1:i"
11295
11296#@cli replace_seq : "search_seq","replace_seq"
11297#@cli : Search and replace a sequence of values in selected images.
11298#@cli : $ (1;2;3;4;5) +replace_seq "2,3,4","7,8"
11299replace_seq : skip "${2=''}"
11300  e[^-1] "Replace value sequence '$1' by value sequence '${2--1}' in image$?."
11301  y repeat $! l[$>] nm={n}
11302    1,100%
11303    eval "ref([ $1 ],str1);
11304          ref([ ${2--1} ],str2);
11305          copy_bloc(pd,src,len) = (
11306            l = len;
11307            pd + l>=h#1?resize(#1,1,h(#1) + 3*l,1,1,0);
11308            copy(i[pd],src,l);
11309            pd+=l;
11310          );
11311          for (ps = pd = 0, ps<h#0 && (qs = find(#0,str1,ps))>=0,
11312            qs>ps?copy_bloc(pd,i[#0,ps],qs - ps);
11313            copy_bloc(pd,str2,size(str2));
11314            ps = qs + size(str1);
11315          );
11316          ps<h#0?copy_bloc(pd,i[#0,ps],h#0 - ps);
11317          resize(#1,1,pd,1,1,0)"
11318    k. nm $nm
11319  endl done
11320
11321#@cli replace_str : "search_str","replace_str"
11322#@cli : Search and replace a string in selected images (viewed as strings, i.e. sequences of character codes).
11323#@cli : $ ('"Hello there, how are you ?"') +replace_str "Hello there","Hi David"
11324replace_str : skip "${2=}"
11325  e[^-1] "Replace string '$1' by string '${2--1}' in image$?."
11326  replace_seq {``{'"$1"'}},{'"${2--1}"'}
11327
11328#@cli round : rounding_value>=0,_rounding_type : (no arg) : (+)
11329#@cli : Round values of selected images.
11330#@cli : 'rounding_type' can be { -1=backward | 0=nearest | 1=forward }.
11331#@cli : Default value: 'rounding_type=0'.
11332#@cli : $ image.jpg +round 100
11333#@cli : $ image.jpg mul {pi/180} sin +round
11334
11335#@cli roundify : gamma>=0
11336#@cli : Apply roundify transformation on float-valued data, with specified gamma.
11337#@cli : Default value: 'gamma=0'.
11338#@cli : $ 1000 fill '4*x/w' repeat 5 +roundify[0] {$>*0.2} done append c display_graph 400,300
11339roundify : check $1>=0
11340  e[^-1] "Roundify image$?, with gamma $1."
11341  if $1==1 return fi
11342  repeat $! l[$>]
11343    +round 1 -.. . +*.. 2 abs. ^. $1 sign... *[-3,-1] *.. 0.5 +
11344  endl done
11345
11346#@cli = : eq. to 'set'. : (+)
11347
11348#@cli set : value,_x[%],_y[%],_z[%],_c[%] : (+)
11349#@cli : Set pixel value in selected images, at specified coordinates.
11350#@cli : (eq. to '=').\n
11351#@cli : If specified coordinates are outside the image bounds, no action is performed.
11352#@cli : Default values: 'x=y=z=c=0'.
11353#@cli : $ 2,2 set 1,0,0 set 2,1,0 set 3,0,1 set 4,1,1
11354#@cli : $ image.jpg repeat 10000 set 255,{u(100)}%,{u(100)}%,0,{u(100)}% done
11355
11356#@cli threshold : value[%],_is_soft={ 0 | 1 } :
11357#@cli : Threshold values of selected images.
11358#@cli : 'soft' can be { 0=hard-thresholding | 1=soft-thresholding }.
11359#@cli : Default value: 'is_soft=0'.
11360#@cli : $ image.jpg +threshold[0] 50% +threshold[0] 50%,1
11361#@cli : $$
11362threshold : check "isexpr($1) && isbool(${2=0})"
11363  e[^-1] ${"arg 1+!$2,Soft,Hard"}"-threshold image$? by $1."
11364  if $2  # Soft thresholding
11365    f "begin(
11366         str = ['$1'];
11367         value = str[size(str)-1]==_'%'?im + (iM-im)*$1:$1
11368       );
11369       i>=value?i - value:
11370       i<=-value?i + value:0"
11371  else ge $1
11372  fi
11373
11374#@cli vector2tensor
11375#@cli : Convert selected vector fields to corresponding tensor fields.
11376vector2tensor :
11377  e[^-1] "Convert vector field$? to tensor field$?."
11378  repeat $! l[$>]
11379    s c
11380    if $!==2 +sqr. *.. ... sqr...
11381    elif $!==3 +sqr.. +*... .. +sqr... *[-5,-4] [-6] sqr[-6]
11382    else error[0--4] "Command '$0': Invalid image ["{$!-$>-1}"] : Dimensions "{w}","{h}","{d}","{s}" does not
11383                      represent a field of 2D or 3D vectors."
11384    fi
11385    a c
11386  endl done
11387
11388#---------------------------------
11389#
11390#@cli :: Colors
11391#
11392#---------------------------------
11393
11394#@cli adjust_colors : -100<=_brightness<=100,-100<=_contrast<=100,-100<=_gamma<=100,-100<=_hue_shift<=100,\
11395# -100<=_saturation<=100,_value_min,_value_max
11396#@cli : Perform a global adjustment of colors on selected images.
11397#@cli : Range of correct image values are considered to be in [value_min,value_max] (e.g. [0,255]).
11398#@cli : If 'value_min==value_max==0', value range is estimated from min/max values of selected images.
11399#@cli : Processed images have pixel values constrained in [value_min,value_max].
11400#@cli : Default values: 'brightness=0', 'contrast=0', 'gamma=0', 'hue_shift=0', 'saturation=0', 'value_min=value_max=0'.
11401#@cli : $ image.jpg +adjust_colors 0,30,0,0,30
11402adjust_colors : check "${1=0}>=-100 && $1<=100 && ${2=0}>=-100 && $2<=100 && ${3=0}>=-100 && $3<=100 &&
11403                       ${4=0}>=-100 && $4<=100 && ${5=0}>=-100 && $5<=100" skip ${6=0},${7=0}
11404  e[^-1] "Adjust colors of image$?, with brightness $1, contrast $2, gamma $3, hue shift $4, saturation $5 and
11405          value range [$6,$7]."
11406  repeat $! l[$>] split_opacity l[0]
11407    range={"$6==$7 && $6==0?[im,iM]:[min($6,$7),max($6,$7)]"}
11408    m={arg(1,$range)} M={arg(2,$range)} fact={255/max(1e-5,$M-$m)}
11409    - $m * $fact
11410    if $4" || "$5 # Adjust Hue/Saturation
11411      to_rgb[0] rgb2hsv[0]
11412      sh[0] 0 +. {$4*1.8} rm.
11413      sh[0] 1 +. {($5%)^(1+($5>0))} c. 0,1 rm.
11414      hsv2rgb[0]
11415    fi
11416    if $3 # Adjust Gamma
11417      /[0] 255 ^[0] {10^-($3%)} *[0] 255
11418    fi
11419    if $2 # Adjust Contrast
11420      -[0] 128 *[0] {exp($2/64)} +[0] 128
11421    fi
11422    +[0] {$1*2} # Adjust Brightness
11423    /[0] $fact +[0] $m c[0] $range # Renormalize values.
11424    a c
11425  endl a c endl done
11426
11427#@cli ac : eq. to 'apply_channels'.
11428ac :
11429  _gmic_s="$?" v + _apply_channels $"*"
11430
11431#@cli apply_channels : "command",color_channels,_value_action={ 0=none | 1=cut | 2=normalize }
11432#@cli : Apply specified command on the chosen color channel(s) of each selected images.
11433#@cli : (eq. to 'ac').\n
11434#@cli : Argument 'color_channels' refers to a colorspace, and can be basically one of
11435#@cli : { all | rgba | [s]rgb | ryb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq }.
11436#@cli : You can also make the processing focus on a few particular channels of this colorspace,
11437#@cli : by setting 'color_channels' as 'colorspace_channel' (e.g. 'hsv_h' for the hue).
11438#@cli : All channel values are considered to be provided in the [0,255] range.
11439#@cli : Default value: 'value_action=0'.
11440#@cli : $ image.jpg +apply_channels "equalize blur 2",ycbcr_cbcr
11441apply_channels :
11442  _gmic_s="$?" v + _$0 $"*"
11443
11444_apply_channels : check "isint(${3=0}) && $3>=0 && $3<=2"
11445  channels=${"_ac_list \"$2\""}
11446  e[^-1] "Apply command '$1' on channels '"$channels"' of image"$_gmic_s"."
11447  ('$/') id={h=0;for(k=0,k<w,((h*=31)+=i[k++])%=1048576)} rm.
11448  _ac_$channels m _ac_precond$id:$_p m _ac_forward$id:$_f m _ac_backward$id:$_b
11449  repeat $! l[$>]
11450    _ac_precond$id is_alpha={s==2" || "s==4}
11451    if $is_alpha s c,{1-s} fi
11452    _ac_forward$id[0] a c
11453    sh $_s _ac. "$1"
11454    if $3==1 c. 0,255 elif $3==2 n. 0,255 fi
11455    rm.
11456    if $is_alpha s c,{1-s} fi
11457    _ac_backward$id[0] a c
11458  endl done
11459  um _ac_precond$id,_ac_forward$id,_ac_backward$id
11460
11461_ac_list :
11462  if isnum("$1")
11463    arg 1+round($1),all,rgba,rgb,rgb_r,rgb_g,rgb_b,rgba_a,\
11464                    lrgb,lrgb_r,lrgb_g,lrgb_b,\
11465                    ycbcr_y,ycbcr_cbcr,ycbcr_cb,ycbcr_cr,ycbcr_cg,\
11466                    lab_l,lab_ab,lab_a,lab_b,\
11467                    lch_ch,lch_c,lch_h,\
11468                    hsv_h,hsv_s,hsv_v,hsi_i,hsl_l,\
11469                    cmyk_c,cmyk_m,cmyk_y,cmyk_k,\
11470                    yiq_y,yiq_iq,ryb,ryb_r,ryb_y,ryb_b
11471  else u "$1" fi
11472
11473_ac :
11474  whds={w},{h},{d},{s} ${1--1} k[0] r $whds,0
11475
11476_ac_all : _p="" _f="" _b="" _s=0,100%
11477
11478_ac_rgba : _p="to_rgba" _f="" _b="" _s=0,3
11479_ac_rgba_r : _p="to_color" _f="" _b="" _s=0
11480_ac_rgba_g : _p="to_color" _f="" _b="" _s=1
11481_ac_rgba_b : _p="to_color" _f="" _b="" _s=2
11482_ac_rgba_a : _p="to_rgba" _f="" _b="" _s=3
11483
11484_ac_rgb : _p="to_color" _f="" _b="" _s=0,2
11485_ac_rgb_r : _ac_rgba_r
11486_ac_rgb_g : _ac_rgba_g
11487_ac_rgb_b : _ac_rgba_b
11488
11489_ac_srgb : _ac_rgb
11490_ac_srgb_r : _ac_rgb_r
11491_ac_srgb_g : _ac_rgb_g
11492_ac_srgb_b : _ac_rgb_b
11493
11494_ac_lrgb : _p="to_color" _f="srgb2rgb" _b="rgb2srgb" _s=0,2
11495_ac_lrgb_r : _p="to_color" _f="srgb2rgb" _b="rgb2srgb" _s=0
11496_ac_lrgb_g : _p="to_color" _f="srgb2rgb" _b="rgb2srgb" _s=1
11497_ac_lrgb_b : _p="to_color" _f="srgb2rgb" _b="rgb2srgb" _s=2
11498
11499_ac_ryb : _p="to_color" _f="rgb2ryb" _b="ryb2rgb" _s=0,2
11500_ac_ryb_r : _p="to_color" _f="rgb2ryb" _b="ryb2rgb" _s=0
11501_ac_ryb_y : _p="to_color" _f="rgb2ryb" _b="ryb2rgb" _s=1
11502_ac_ryb_b : _p="to_color" _f="rgb2ryb" _b="ryb2rgb" _s=2
11503
11504_ac_ycbcr : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=0,2
11505_ac_ycbcr_y : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=0
11506_ac_ycbcr_cbcr : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=1,2
11507_ac_ycbcr_cb : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=1
11508_ac_ycbcr_cr : _p="to_color" _f="rgb2ycbcr" _b="ycbcr2rgb" _s=2
11509_ac_ycbcr_cg : _p="to_color" _f="sh 0,1 mirror. c rm. rgb2ycbcr" _b="ycbcr2rgb sh 0,1 mirror. c rm." _s=2
11510
11511_ac_lab : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=0,2
11512_ac_lab_l : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=0
11513_ac_lab_ab : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=1,2
11514_ac_lab_a : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=1
11515_ac_lab_b : _p="to_color" _f="srgb2lab8" _b="lab82srgb" _s=2
11516
11517_ac_lch : _p="to_color" _f="srgb2rgb rgb2lch8" _b="lch82rgb rgb2srgb" _s=0,2
11518_ac_lch_l : _ac_lab_l
11519_ac_lch_ch : _p="to_color" _f="srgb2rgb rgb2lch8" _b="lch82rgb rgb2srgb" _s=1,2
11520_ac_lch_c : _p="to_color" _f="srgb2rgb rgb2lch8" _b="lch82rgb rgb2srgb" _s=1
11521_ac_lch_h : _p="to_color" _f="srgb2rgb rgb2lch8" _b="lch82rgb rgb2srgb" _s=2
11522
11523_ac_hsv : _p="to_color" _f="rgb2hsv8" _b="hsv82rgb" _s=0,2
11524_ac_hsv_h : _p="to_color" _f="rgb2hsv8" _b="hsv82rgb" _s=0
11525_ac_hsv_s : _p="to_color" _f="rgb2hsv8" _b="hsv82rgb" _s=1
11526_ac_hsv_v : _p="to_color" _f="rgb2hsv8" _b="hsv82rgb" _s=2
11527
11528_ac_hsi : _p="to_color" _f="rgb2hsi8" _b="hsi82rgb" _s=0,2
11529_ac_hsi_h : _p="to_color" _f="rgb2hsi8" _b="hsi82rgb" _s=0
11530_ac_hsi_s : _p="to_color" _f="rgb2hsi8" _b="hsi82rgb" _s=1
11531_ac_hsi_i : _p="to_color" _f="rgb2hsi8" _b="hsi82rgb" _s=2
11532
11533_ac_hsl : _p="to_color" _f="rgb2hsl8" _b="hsl82rgb" _s=0,2
11534_ac_hsl_h : _p="to_color" _f="rgb2hsl8" _b="hsl82rgb" _s=0
11535_ac_hsl_s : _p="to_color" _f="rgb2hsl8" _b="hsl82rgb" _s=1
11536_ac_hsl_l : _p="to_color" _f="rgb2hsl8" _b="hsl82rgb" _s=2
11537
11538_ac_cmy : _p="to_color" _f="rgb2cmy" _b="cmy2rgb" _s=0,2
11539_ac_cmy_c : _p="to_color" _f="rgb2cmy" _b="cmy2rgb" _s=0
11540_ac_cmy_m : _p="to_color" _f="rgb2cmy" _b="cmy2rgb" _s=1
11541_ac_cmy_y : _p="to_color" _f="rgb2cmy" _b="cmy2rgb" _s=2
11542
11543_ac_cmyk : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=0,3
11544_ac_cmyk_c : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=0
11545_ac_cmyk_m : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=1
11546_ac_cmyk_y : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=2
11547_ac_cmyk_k : _p="to_color" _f="rgb2cmyk" _b="cmyk2rgb" _s=3
11548
11549_ac_yiq : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=0,2
11550_ac_yiq_y : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=0
11551_ac_yiq_iq : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=1,2
11552_ac_yiq_i : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=1
11553_ac_yiq_q : _p="to_color" _f="rgb2yiq8" _b="yiq82rgb" _s=2
11554
11555#@cli autoindex : nb_colors>0,0<=_dithering<=1,_method={ 0=median-cut | 1=k-means }
11556#@cli : Index selected vector-valued images by adapted colormaps.
11557#@cli : Default values: 'dithering=0' and 'method=1'.
11558#@cli : $ image.jpg +autoindex[0] 4 +autoindex[0] 8 +autoindex[0] 16
11559autoindex : check "isint($1) && $1>0 && ${2=0}>=0" skip ${3=1}
11560  e[^-1] "Index colors in images$? by adapted colormap with $1 entries, dithering level $2 and "\
11561          ${arg\ 1+!$3,k-means,median-cut}" method."
11562  repeat $! l[$>]
11563    if w>h if w>256 +r2dx 256 else [0] fi
11564    else if h>256 +r2dy 256 else [0] fi
11565    fi
11566    colormap[1] $1,$3,0
11567    index[0] [1],$2,1 rm[1]
11568  endl done
11569
11570#@cli bayer2rgb : _GM_smoothness,_RB_smoothness1,_RB_smoothness2
11571#@cli : Transform selected RGB-Bayer sampled images to color images.
11572#@cli : Default values: 'GM_smoothness=RB_smoothness=1' and 'RB_smoothness2=0.5'.
11573#@cli : $ image.jpg rgb2bayer 0 +bayer2rgb 1,1,0.5
11574bayer2rgb : skip ${1=1},${2=1},${3=0.5}
11575  e[^-1] "Transform RGB-Bayer image$? to color images, with smoothness ($1,$2,$3)."
11576  channels 0 repeat $! l[$>]
11577
11578    # Expand image size to avoid problems with borders.
11579    expand_x {"2 + 4*$1"},0 expand_y {"2 + 4*$1"},0
11580
11581    # Compute green-magenta chromaticity.
11582    (-1,1;1,-1) r. ..,..,1,1,0,2
11583    +*.. .
11584
11585    (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm.
11586    b. $1
11587
11588    *.. .
11589    -[-3,-2]
11590
11591    # Compute red-blue chromaticity.
11592    (1,-1) r. ..,..,1,1,0,2  # Horizontal estimate
11593    *. ...
11594    (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm.
11595    blur_y. $2 blur_x. $3
11596
11597    (1;-1) r. ..,..,1,1,0,2  # Vertical estimate
11598    *. [-4]
11599    (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm.
11600    blur_x. $2 blur_y. $3
11601
11602    +[-2,-1] /. 2
11603
11604    # Luminance reconstruction.
11605    (2,0;0,-2) r. ..,..,1,1,0,2
11606    *. ..
11607    -[-4,-1]
11608
11609    # RGB reconstruction.
11610    a[-3--1] c
11611    mix_rgb. 1,-1,2,1,1,0,1,-1,-2
11612
11613    # Shrink to original image size.
11614    shrink_x {"2 + 4*$1"},0 shrink_y {"2 + 4*$1"},0
11615    c 0,255
11616
11617  endl done
11618
11619#@cli deltaE : [image],_metric={ 0=deltaE_1976 | 1=deltaE_2000 },"_to_Lab_command"
11620#@cli : Compute the CIE DeltaE color difference between selected images and specified [image].
11621#@cli : Argument 'to_Lab_command' is a command able to convert colors of [image] into a Lab representation.
11622#@cli : Default values: 'metric=1' and 'to_Lab_command="srgb2lab"'.
11623#@cli : $ image.jpg +blur 2 +deltaE[0] [1],1,srgb2lab
11624deltaE : check ${"is_image_arg $1"}" && isbool(${2=1})" skip "${3=srgb2lab}"
11625  e[^-1] "Compute the CIE DeltaE_"${"s0,s1=1976,2000 u $s$2"}" color difference between image$? and image$1, "\
11626         "with to_Lab command '$3'."
11627  pass$1 1
11628  needs_to_lab={"s = ['$3']; s!=0 && s!=' '"}
11629  if $needs_to_lab m "_deltaE_to_lab : $3" +_deltaE_to_lab. rm.. fi
11630  repeat $!-1 l[$>,-1] nm={0,n}
11631    if $needs_to_lab _deltaE_to_lab[0] fi
11632    if !$2 -.. . norm..                                                # DeltaE_1976
11633    else 100%,100%,100%,1,${-math_lib}"deltaE00(I#0,I#1)" rv[0,-1] rm. # DeltaE_2000
11634    fi
11635  nm[0] $nm endl done
11636  rm. um _deltaE_to_lab
11637
11638#@cli cmy2rgb
11639#@cli : Convert color representation of selected images from CMY to RGB.
11640cmy2rgb :
11641  e[^-1] "Convert color representation of image$? from CMY to RGB."
11642  rgb2cmy
11643
11644#@cli cmyk2rgb
11645#@cli : Convert color representation of selected images from CMYK to RGB.
11646cmyk2rgb :
11647  e[^-1] "Convert color representation of image$? from CMYK to RGB."
11648  repeat $! l[$>]
11649    s c +/. -255 +. 1 *[0-2] . rm. +[0-2] . rm.
11650    a c cmy2rgb
11651  endl done
11652
11653#@cli colorblind : type={ 0=protanopia | 1=protanomaly | 2=deuteranopia | 3=deuteranomaly | \
11654# 4=tritanopia | 5=tritanomaly | 6=achromatopsia | 7=achromatomaly }
11655#@cli : Simulate color blindness vision.
11656#@cli : Simulation method of Vienot, Brettel & Mollon 1999, \
11657# "Digital video colourmaps for checking the legibility of displays by dichromats".
11658#@cli : The dichromacy matrices of the paper were adapted to sRGB (RGB->XYZ).
11659#@cli : Anomalous trichromacy simulated via linear interpolation with the identity and a factor of 0.6.
11660#@cli : $ image.jpg +colorblind 0
11661colorblind : check "isint($1) && $1>=0 && $1<=7"
11662  s0="protanopia" s1="protanomaly" s2="deuteranopia" s3="deuteranomaly" s4="tritanopia"
11663  s5="tritanomaly" s6="achromatopsia" s7="achromatomaly"
11664  e[^-1] "Simulate color blindness of type '"${s$1}"' on image$?."
11665  type0=(0.10889,0.89111,0;0.10889,0.89111,0;0.00447,-0.00447,1.0)
11666  type1=(0.46533,0.53467,0;0.06533,0.93467,0;0.00268,-0.00268,1)
11667  type2=(0.29031,0.70969,0;0.29031,0.70969,0;-0.02197,0.02197,1)
11668  type3=(0.57418,0.42582,0;0.17418,0.82582,0;-0.01318,0.01318,1)
11669  type4=(1,0.15236,-0.15236;0,0.86717,0.13283;0,0.86717,0.13283)
11670  type5=(1,0.09142,-0.09142;0,0.92030,0.07970;0,0.52030,0.47970)
11671  type6=(0.299,0.587,0.114;0.299,0.587,0.114;0.299,0.587,0.114)
11672  type7=(0.618,0.320,0.062;0.163,0.775,0.062;0.163,0.320,0.516)
11673  repeat $! l[$>] split_opacity l[0] to_rgb srgb2rgb mix_channels ${type$1} rgb2srgb endl a c endl done
11674
11675#@cli colormap : nb_levels>=0,_method={ 0=median-cut | 1=k-means },_sort_vectors
11676#@cli : Estimate best-fitting colormap with 'nb_colors' entries, to index selected images.
11677#@cli : Set 'nb_levels==0' to extract all existing colors of an image.
11678#@cli : 'sort_vectors' can be { 0=unsorted | 1=by increasing norm | 2=by decreasing occurrence }.
11679#@cli : Default value: 'method=1' and 'sort_vectors=1'.
11680#@cli : $ image.jpg +colormap[0] 4 +colormap[0] 8 +colormap[0] 16
11681#@cli : $$ https://gmic.eu/oldtutorial/_colormap
11682colormap : check "isint($1) && $1>=0" skip ${2=1},${3=1}
11683  if $1 e[0--3] "Estimate colormap with $1 entries for image$?, by "${arg\ 1+!$2,k-means,median-cut}" method."
11684  else e[0--3] "Estimate full colormap for image$?."
11685  fi
11686  repeat $! l[$>] nm={b} is_half=${-is_half}
11687
11688    if $1 # Estimate quantized colormap.
11689      r {whd},1,1,100%,-1
11690      if !$2 +_colormap $1 # Just run the median-cut algorithm
11691      else
11692        +_colormap $1
11693        max_diff={(iM-im+1)/8192}
11694        do
11695          +index.. . # Find nearest cluster for each color
11696          if $is_half
11697            ..,1,1,{1,s}
11698            eval "
11699              ref(vector(#w*s#0),csum);
11700              ref(vector(#w,0),cocc);
11701              repeat (w#2,k,
11702                val = i[#2,k];
11703                repeat (s#0,c,csum[val + c*w]+=i(#0,k,0,0,c));
11704                ++cocc[val];
11705              );
11706              off = 0;
11707              repeat (s#0,c,
11708                repeat (w,k,occ = cocc[k]; occ?(csum[off++]/=occ));
11709              );
11710              draw(#3,csum,0,0,0,0)"
11711            rm..
11712          else
11713            ..,1,1,{1,s+1}
11714            f.. ">I[#3,i]+=[ I[#0,x],1 ]" rm..
11715            f. "s = i(x,0,0,s-1); s?I/s:[ I[#1,x], 0 ]"
11716          fi
11717          +-.. . abs. diff={iM/w} rm. # Compute colormap difference
11718          j.. . rm.
11719        while $diff>$max_diff
11720      fi
11721      if $3 index.. .,0,0 histogram.. {[w,0,w-1]} a y sort -,x rows 1 # Sort by decreasing occurrence
11722      else rm..
11723      fi
11724
11725    else # Extract full colormap.
11726
11727      # Compute map of hashcodes.
11728      +n. 0,255 f. "ret = vectors(); H = 0; repeat (s,p,(H*=31)+=j(0,0,0,p)); ret[0] = H%2048; ret"
11729      channels. 0 equalize. 2048 n. 0,2047 round.
11730
11731      # Find single occurrence of each color/vector.
11732      1,1,1,.. .x2047
11733      eval[0] ">
11734        begin( ret = vectors(); col_r = vectors() );
11735        col_s = I;
11736        H = 2 + i#1;
11737        sH = da_size(#H);
11738        is_found = 0;
11739        repeat (sH,p,
11740          copy(col_r,i[#H,p],s#0,1,h(#H));
11741          col_r==col_s?(is_found = 1; break());
11742        );
11743        !is_found?da_push(#H,col_s);
11744        ret;
11745        end(repeat (l - 2,p, resize(#2 + p,1,da_size(#2 + p),1,s#0,0)))"
11746      a[2--1] y transpose. k.
11747    fi
11748
11749    if $3==1 +norm rv a c sort +,x channels 1,100% fi # Sort colors by increasing norm.
11750    nm "[colormap of "$nm"]"
11751  endl done
11752
11753_colormap : # Implementation of the median-cut algorithm.
11754  m "__colormap : repeat s sh[$""1] $> =.. {iv},$""1,0,0,$> rm. done"
11755  1,1,1,{s} __colormap 0 # Initialize image of box variances
11756  repeat $1-1
11757    b,a={[xM,cM]} # b = box with highest variance, a = axis with highest variance
11758    shift[$b] 0,0,0,{-$a},2 sort[$b] +,x shift[$b] 0,0,0,$a,2
11759    if {$b,w>1} s[$b] x,2 else 1 fi # Split selected box along its median axis
11760    mv[$b] -1 r. {w+1},1,1,100%,0
11761    __colormap $b __colormap {$!-2} # Update box variances
11762  done
11763  rm. r 1,1,1,100%,2 a x # Average value in each box and append as final colormap
11764  um __colormap
11765
11766#@cli compose_channels
11767#@cli : Compose all channels of each selected image, using specified arithmetic operator (+,-,or,min,...).
11768#@cli : Default value: '1=+'.
11769#@cli : $ image.jpg +compose_channels and
11770#@cli : $$
11771compose_channels : skip ${1="+"}
11772  e[^-1] "Compose all channels of image$?, with operator '$1'."
11773  repeat $! l[$>]
11774    sh 0
11775    repeat s#-2-1 sh.. {$>+1} l[-2,-1] $1 endl done
11776    rm. r 100%,100%,100%,1,-1
11777  endl done
11778
11779#@cli direction2rgb
11780#@cli : Compute RGB representation of selected 2D direction fields.
11781#@cli : $ image.jpg luminance gradient append c blur 2 orientation +direction2rgb
11782direction2rgb :
11783  e[^-1] "Compute RGB representation of 2D direction field$?."
11784  channels 0,1 repeat $! l[$>] nm={0,n}
11785    s c complex2polar round.. 0.001
11786    *. {180/pi} %. 360 100%,100%,1,1,1 mv... $!
11787    if im!=iM n. 0,1 else f. 1 fi
11788    a c hsv2rgb
11789  nm $nm endl done
11790
11791#@cli ditheredbw
11792#@cli : Create dithered B&W version of selected images.
11793#@cli : $ image.jpg +equalize ditheredbw[-1]
11794ditheredbw :
11795  e[^-1] "Create dithered B&W version of image$?."
11796  repeat $! l[$>] split_opacity
11797    luminance[0] n[0] 0,255 (0,255) index[0] .,1,1 rm.
11798  a c endl done
11799
11800#@cli fc : eq. to 'fill_color'.
11801fc :
11802  _gmic_s="$?" v + _fill_color $*
11803
11804#@cli fill_color : col1,...,colN
11805#@cli : Fill selected images with specified color.
11806#@cli : (eq. to 'fc').
11807#@cli : $ image.jpg +fill_color 255,0,255
11808#@cli : $$ https://gmic.eu/oldtutorial/_fill_color
11809fill_color :
11810  _gmic_s="$?" v + _$0 $*
11811
11812_fill_color :
11813  e[0--3] "Fill image"$_gmic_s" with color (${^0})."
11814  repeat $! l[$>]
11815    repeat s sh[0] $> f. {arg(1+$>,${^0})} done k[0]
11816  nm {n} endl done
11817
11818#@cli gradient2rgb : _is_orientation={ 0 | 1 }
11819#@cli : Compute RGB representation of 2D gradient of selected images.
11820#@cli : Default value: 'is_orientation=0'.
11821#@cli : $ image.jpg +gradient2rgb 0 equalize[-1]
11822gradient2rgb : check "isbool(${1=0})"
11823  arg 1+!$1,"orientation ",""
11824  e[^-1] "Compute RGB representation of 2D gradient "${}"of image$?."
11825  norm repeat $! l[$>]
11826    if $1 gradient_orientation 2 else g xy fi
11827    a c direction2rgb
11828  endl done
11829
11830#@cli hcy2rgb
11831#@cli : Convert color representation of selected images from HCY to RGB.
11832hcy2rgb :
11833  e[^-1] "Convert color representation of image$? from HCY to RGB."
11834  to_color f "
11835    H = (R/60)%6;
11836    X = G*(1 - abs(H%2 - 1));
11837    RGB = arg(1 + int(H),[G,X,0],[X,G,0],[0,G,X],[0,X,G],[X,0,G],[G,0,X]);
11838    m = B - 0.3*RGB[0] - 0.59*RGB[1] - 0.11*RGB[2];
11839    cut((RGB+=m)*=255,0,255)"
11840
11841#@cli hsi2rgb
11842#@cli : Convert color representation of selected images from HSI to RGB.
11843hsi2rgb :
11844  e[^-1] "Convert color representation of image$? from HSI to RGB."
11845  to_color
11846  f "
11847    H = (R/60)%6;
11848    S = G;
11849    I = B;
11850    Z = 1 - abs((H%2) - 1);
11851    C = I*S/(1 + Z);
11852    X = C*Z;
11853    m = I*(1 - S)/3;
11854    RGB = arg(1 + int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]);
11855    (RGB+=m)*=3*255"
11856
11857#@cli hsi82rgb
11858#@cli : Convert color representation of selected images from HSI8 to RGB.
11859hsi82rgb :
11860  e[^-1] "Convert color representation of image$? from HSI8 to RGB."
11861  _hsx82rgb hsi2rgb
11862
11863#@cli hsl2rgb
11864#@cli : Convert color representation of selected images from HSL to RGB.
11865hsl2rgb :
11866  e[^-1] "Convert color representation of image$? from HSL to RGB."
11867  to_color
11868  f "
11869    H = (R/60)%6;
11870    S = G;
11871    L = B;
11872    C = (1 - abs(2*L - 1))*S;
11873    X = C*(1 - abs(H%2 - 1));
11874    m = L - C/2;
11875    RGB = arg(1 + int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]);
11876    (RGB+=m)*=255"
11877
11878#@cli hsl82rgb
11879#@cli : Convert color representation of selected images from HSL8 to RGB.
11880hsl82rgb :
11881  e[^-1] "Convert color representation of image$? from HSL8 to RGB."
11882  _hsx82rgb hsl2rgb
11883
11884#@cli hsv2rgb
11885#@cli : Convert color representation of selected images from HSV to RGB.
11886#@cli : $ (0,360;0,360^0,0;1,1^1,1;1,1) resize 400,400,1,3,3 hsv2rgb
11887hsv2rgb :
11888  e[^-1] "Convert color representation of image$? from HSV to RGB."
11889  to_color
11890  f "
11891    H = (R/60)%6;
11892    S = G;
11893    V = B;
11894    C = V*S;
11895    X = C*(1 - abs(H%2 - 1));
11896    m = V - C;
11897    RGB = arg(1 + int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]);
11898    (RGB+=m)*=255"
11899
11900#@cli hsv82rgb
11901#@cli : Convert color representation of selected images from HSV8 to RGB.
11902hsv82rgb :
11903  e[^-1] "Convert color representation of image$? from HSV8 to RGB."
11904  _hsx82rgb hsv2rgb
11905
11906_hsx82rgb :
11907 repeat $!
11908   sh[$>] 0 /. 0.708333 rm.
11909   sh[$>] 1,2 /. 255 rm.
11910 done
11911
11912#@cli int2rgb
11913#@cli : Convert color representation of selected images from INT24 to RGB.
11914int2rgb :
11915  e[^-1] "Convert color representation of image$? from INT24 scalars to RGB."
11916  round repeat $! l[$>]
11917    +>> 8 &[1] 255 +&[0] 255 >>[0] 16 a c
11918  endl done
11919
11920#@cli jzazbz2rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11921#@cli : Convert color representation of selected images from RGB to Jzazbz.
11922#@cli : Default value: 'illuminant=2'.
11923jzazbz2rgb : skip "${1=,}"
11924  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
11925  e[^-1] "Convert color representation of image$? from Jzazbz to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11926  jzazbz2xyz xyz2rgb $illu
11927
11928#@cli jzazbz2xyz
11929#@cli : Convert color representation of selected images from RGB to XYZ.
11930jzazbz2xyz :
11931  e[^-1] "Convert color representation of image$? from Jzazbz to XYZ."
11932  repeat $! l[$>] split_opacity
11933    f[0] ${-_jzazbz_const}"
11934      tmp = i0 + Jzazbz_d0;
11935      Iz = tmp/(1 + Jzazbz_d - Jzazbz_d*tmp);
11936      azz = i1;
11937      bzz = i2;
11938      Lp = Iz + 0.138605043271539*azz + 0.0580473161561189*bzz;
11939      Mp = Iz - 0.138605043271539*azz - 0.0580473161561189*bzz;
11940      Sp = Iz - 0.0960192420263189*azz - 0.811891896056039*bzz;
11941      tmp = Lp^(1/Jzazbz_p);
11942      L = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n);
11943      tmp = Mp^(1/Jzazbz_p);
11944      M = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n);
11945      tmp = Sp^(1/Jzazbz_p);
11946      S = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n);
11947      Xp = 1.92422643578761*L - 1.00479231259537*M + 0.037651404030618*S;
11948      Yp = 0.350316762094999*L + 0.726481193931655*M - 0.065384422948085*S;
11949      Zp = -0.0909828109828476*L - 0.312728290523074*M + 1.52276656130526*S;
11950      X = (Xp + (Jzazbz_b - 1)*Zp)/Jzazbz_b;
11951      Y = (Yp + (Jzazbz_g - 1)*X)/Jzazbz_g;
11952      Z = Zp;
11953      [ X,Y,Z ]/255"
11954  a c endl done
11955
11956# The XYZ<->Jzazbz conversion code has been written by Alan Gibson,
11957# and published on this page: <http://im.snibgo.com/jzazbz.htm>
11958_jzazbz_const :
11959  u "const Jzazbz_b = 1.15;
11960     const Jzazbz_g = 0.66;
11961     const Jzazbz_c1 = 3424/4096;
11962     const Jzazbz_c2 = 2413/128;
11963     const Jzazbz_c3 = 2392/128;
11964     const Jzazbz_n = 2610/16384;
11965     const Jzazbz_p = 1.7*2523/32;
11966     const Jzazbz_d = -0.56;
11967     const Jzazbz_d0 = 1.6295499532821566e-11;
11968     const peakLum = 10000;"
11969
11970#@cli lab2lch
11971#@cli : Convert color representation of selected images from Lab to Lch.
11972lab2lch :
11973  e[^-1] "Convert color representation of image$? from Lab to Lch."
11974  repeat $! l[$>]
11975    to_color s c complex2polar[1,2] a c
11976  endl done
11977
11978#@cli lab2rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11979#@cli : Convert color representation of selected images from Lab to RGB.
11980#@cli : Default value: 'illuminant=2'.
11981#@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb
11982lab2rgb : skip "${1=,}"
11983  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
11984  e[^-1] "Convert color representation of image$? from Lab to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11985  lab2xyz $illu xyz2rgb $illu
11986
11987#@cli lab2srgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11988#@cli : Convert color representation of selected images from Lab to sRGB.
11989#@cli : Default value: 'illuminant=2'.
11990#@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb
11991lab2srgb : skip "${1=,}"
11992  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
11993  e[^-1] "Convert color representation of image$? from Lab to sRGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
11994  lab2rgb $illu rgb2srgb
11995
11996#@cli lab82srgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
11997#@cli : Convert color representation of selected images from Lab8 to sRGB.
11998#@cli : Default value: 'illuminant=2'.
11999#@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb
12000lab82srgb : skip "${1=,}"
12001  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
12002  e[^-1] "Convert color representation of image$? from Lab8 to sRGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12003  lab82rgb $illu rgb2srgb
12004
12005#@cli lab2xyz : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12006#@cli : Convert color representation of selected images from Lab to XYZ.
12007#@cli : Default value: 'illuminant=2'.
12008lab2xyz : skip "${1=,}"
12009  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
12010  e[^-1] "Convert color representation of image$? from Lab to XYZ, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12011  to_color
12012  f "
12013    begin(
12014      const epsilon = 216/24389;
12015      const kappa = 24389/27;
12016      D65 = [ 0.4124564, 0.3575761, 0.1804375,
12017              0.2126729, 0.7151522, 0.0721750,
12018              0.0193339, 0.1191920, 0.9503041 ];
12019      D50 = [ 0.43603516, 0.38511658, 0.14305115,
12020              0.22248840, 0.71690369, 0.06060791,
12021              0.01391602, 0.09706116, 0.71392822 ];
12022      E = [ 0.488718,0.3106803,0.2006017,
12023            0.1762044,0.8129847,0.0108109,
12024            0,0.0102048,0.9897952 ];
12025      white = ("$illu"==2?E:"$illu"==1?D65:D50)*[ 1,1,1 ];
12026    );
12027
12028    fy = (i0 + 16)/116;
12029    fz = fy - i2/200;
12030    fx = i1/500 + fy;
12031    fx3 = fx^3;
12032    fz3 = fz^3;
12033    XYZ = [ fx3>epsilon?fx3:(116*fx - 16)/kappa,
12034            i0>kappa*epsilon?((i0+16)/116)^3:i0/kappa,
12035            fz3>epsilon?fz3:(116*fz - 16)/kappa ];
12036    XYZ*=white"
12037
12038#@cli lab82rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12039#@cli : Convert color representation of selected images from Lab8 to RGB.
12040#@cli : Default value: 'illuminant=2'.
12041lab82rgb : skip "${1=,}"
12042  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
12043  e[^-1] "Convert color representation of image$? from Lab8 to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12044  repeat $!
12045    sh[$>] 0 /. 2.55 rm.
12046    sh[$>] 1 /. 0.85 -. 127 rm.
12047    sh[$>] 2 /. 0.836 -. 149 rm.
12048  done lab2rgb $illu c 0,255
12049
12050#@cli lch2lab
12051#@cli : Convert color representation of selected images from Lch to Lab.
12052lch2lab :
12053  e[^-1] "Convert color representation of image$? from Lch to Lab."
12054  repeat $! l[$>]
12055    to_color s c polar2complex[1,2] a c
12056  endl done
12057
12058#@cli lch2rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12059#@cli : Convert color representation of selected images from Lch to RGB.
12060#@cli : Default value: 'illuminant=2'.
12061lch2rgb : skip "${1=,}"
12062  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
12063  e[^-1] "Convert color representation of image$? from Lch to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12064  lch2lab lab2rgb $illu
12065
12066#@cli lch82rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12067#@cli : Convert color representation of selected images from Lch8 to RGB.
12068#@cli : Default value: 'illuminant=2'.
12069lch82rgb : skip "${1=,}"
12070  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
12071  e[^-1] "Convert color representation of image$? from Lch8 to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12072  pi,facth={[pi,255/(2*pi)]}
12073  repeat $!
12074    sh[$>] 0 /. 2.55 rm.
12075    sh[$>] 1 /. 1.1086 rm.
12076    sh[$>] 2 /. $facth -. $pi rm.
12077  done lch2rgb $illu c 0,255
12078
12079#@cli luminance
12080#@cli : Compute luminance of selected sRGB images.
12081#@cli : $ image.jpg +luminance
12082#@cli : $$
12083luminance :
12084  e[^-1] "Compute luminance of image$?."
12085  remove_opacity srgb2rgb
12086  repeat $! l[$>]
12087  if s==3 sh 0 sh[0] 1 sh[0] 2 *[1] 0.22248840 *[2] 0.71690369 *[3] 0.06060791 +[1-3] rm[1]
12088  elif s!=1 norm n 0,255
12089  fi endl done
12090  channels 0 rgb2srgb
12091
12092#@cli lightness
12093#@cli : Compute lightness of selected sRGB images.
12094#@cli : $ image.jpg +lightness
12095lightness :
12096  e[^-1] "Compute lightness of image$?."
12097  remove_opacity srgb2rgb
12098  if s==3 srgb2lab channels 0 * {255/100} elif s!=1 norm n 0,255 rgb2srgb fi
12099
12100#@cli lut_contrast : _nb_colors>1,_min_rgb_value
12101#@cli : Generate a RGB colormap where consecutive colors have high contrast.
12102#@cli : This function performs a specific score maximization to generate the result, so
12103#@cli : it may take some time when 'nb_colors' is high.
12104#@cli : Default values: 'nb_colors=256' and 'min_rgb_value=64'.
12105lut_contrast : check "isint(${1=256}) && $1>=1 && isnum(${2=48})"
12106  e[^-1] "Generate high-contrast RGB colormap with $1 colors and min RGB value $2."
12107  l[]
12108
12109    # Initialization by farthest point sampling of the RGB cube.
12110    64,64,64,1 eval "repeat (8,k, x = !!(k&1); y = !!(k&2); z = !!(k&4); i([x,y,z]*(w-1)) = 1)"
12111    N={is}
12112    e[] ""
12113    do
12114      +neq. 0 distance. 1 xyzM={[xM,yM,zM]} rm.
12115      col={round([$xyzM]*255/(w-1))}
12116      if max($col)>=$2 =. 1,$xyzM N+=1 else =. -1,$xyzM fi
12117      e[] "\r  [ Init ] > Colors \#"$N
12118    while $N<$1
12119    >. 0 {is},1,1,3
12120    eval.. ">begin(k = 0); i>0?(I[#-1,k++] = round([ x,y,z ]*255/63))"
12121    k.
12122
12123    N0=5 # 5 first colors to be preserved.
12124    s x repeat $!
12125      if {$>,I==[0,0,0]} rv[$>,0] fi
12126      if {$>,I==[255,255,255]} rv[$>,1] fi
12127      if {$>,I==[255,0,0]} rv[$>,2] fi
12128      if {$>,I==[0,255,0]} rv[$>,3] fi
12129      if {$>,I==[0,0,255]} rv[$>,4] fi
12130    done a x
12131
12132    # Functional optimization.
12133    e[] ""
12134    +srgb2lab a c
12135    energy_max=${-_lut_contrast.}
12136    nb_attempts=1000
12137    do
12138      e[] "\r  [ Optim ] > Score = "{_$energy_max}", Attempts = "$nb_attempts"      "
12139      . eval "
12140        do(
12141          k0 = round(u("$N0",w-1));
12142          k1 = round(u("$N0",w-1)),
12143        k0==k1);
12144        tmp = I[k0]; I[k0] = I[k1]; I[k1] = tmp"
12145      energy=${-_lut_contrast.}
12146      if $energy>$energy_max energy_max=$energy k. nb_attempts=1000 else rm. nb_attempts-=1 fi
12147    while $nb_attempts>0
12148
12149    channels 0,2
12150  endl
12151
12152_lut_contrast :
12153  100%,1,1,1,">
12154    const N = 10;
12155    dist = 0; sumw = 0;
12156    RGB0 = (I[#0,x])[3,3];
12157    kmin = max(x-N,0);
12158    kmax = min(x+N,w-1);
12159    for (k = kmin, k<=kmax, ++k,
12160      RGB = (I[#0,k])[3,3];
12161      w = (1 + N - abs(k-x))^1.5;
12162      dist+= w*norm(RGB - RGB0);
12163      sumw+=w;
12164    );
12165    dist/=sumw"
12166  u {is} rm.
12167
12168#@cli map_clut : [clut] | "clut_name"
12169#@cli : Map specified RGB color LUT to selected images.
12170#@cli : $ image.jpg uniform_distribution {2^6},3 mirror[-1] x +map_clut[0] [1]
12171map_clut :
12172  e[^-1] "Map color LUT $1 on image$?."
12173  if !$! return fi
12174  to_color
12175  if ${"is_image_arg $1"} pass$1 0 to_rgb. else clut "$1" fi
12176  l={round((w*h*d)^(1/3))}
12177  if w*h*d!=$l^3 error "Command '$0': Specified CLUT $1 has invalid dimensions "({w},{h},{d},{s}). fi
12178  r. $l,$l,$l,3,-1
12179  repeat $!-1 l[$>,-1] nm={0,n} split_opacity[0] /[0] {256/$l}
12180    +warp. [0],0,1,1
12181    rm[0] mv. 0 a[^-1] c nm[0] $nm
12182  endl done rm.
12183
12184#@cli mix_rgb : a11,a12,a13,a21,a22,a23,a31,a32,a33
12185#@cli : Apply 3x3 specified matrix to RGB colors of selected images.
12186#@cli : Default values: 'a11=1', 'a12=a13=a21=0', 'a22=1', 'a23=a31=a32=0' and 'a33=1'.
12187#@cli : $ image.jpg +mix_rgb 0,1,0,1,0,0,0,0,1
12188#@cli : $$
12189mix_rgb : skip ${1=1},${2=0},${3=0},${4=0},${5=1},${6=0},${7=0},${8=0},${9=1}
12190  e[^-1] "Apply matrix [ $1 $2 $3 ; $4 $5 $6 ; $7 $8 $9 ] to RGB colors of image$?."
12191  to_color repeat $! sh[$>] 0,2 mix_channels. (${1-3};${4-6};${7-9}) rm. done
12192
12193#@cli oklab2rgb
12194#@cli : Convert color representation of selected images from OKlab to RGB.
12195#@cli : (see colorspace definition at: <https://bottosson.github.io/posts/oklab/> ).
12196#@cli : See also: ''rgb2oklab''.
12197oklab2rgb :
12198  e[^-1] "Convert color representation of image$? from Oklab to RGB."
12199  repeat $! l[$>] split_opacity to_rgb[0]
12200    f[0] "
12201     l = (i0 + 0.3963377774*i1 + 0.2158037573*i2)^3;
12202     m = (i0 - 0.1055613458*i1 - 0.0638541728*i2)^3;
12203     s = (i0 - 0.0894841775*i1 - 1.2914855480*i2)^3;
12204     [ 4.0767245293*l - 3.3072168827*m + 0.2307590544*s,
12205       -1.2681437731*l + 2.6093323231*m - 0.3411344290*s,
12206       -0.0041119885*l - 0.7034763098*m + 1.7068625689*s ]"
12207    * 255
12208  a c endl done
12209
12210#@cli palette : palette_name | palette_number
12211#@cli : Input specified color palette at the end of the image list.
12212#@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | \
12213# parula | spring | summer | autumn | winter | bone | copper | pink | vga | \
12214# algae | amp | balance | curl | deep | delta | dense | diff | gray | haline | ice | \
12215# matter | oxy | phase | rain | solar | speed | tarn | tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | \
12216# uzebox | amiga7800 | amiga7800mess | fornaxvoid1 }
12217#@cli : $ palette hsv
12218+palette :
12219  names=${-_palette_names} N={narg($names)}
12220  l[] if isint("$1") name=${"arg 1+($1%"$N"),"$names} else name="$1" fi onfail name="$1" endl
12221  e[^-1] "Input color palette '"$name"'."
12222  _palette_$name
12223  nm. $name
12224
12225_palette_names :
12226  u default,hsv,lines,hot,cool,jet,flag,cube,rainbow,\
12227    parula,spring,summer,autumn,winter,bone,copper,pink,vga,\
12228    algae,amp,balance,curl,deep,delta,dense,diff,gray,haline,ice,\
12229    matter,oxy,phase,rain,solar,speed,tarn,tempo,thermal,topo,turbid,aurora,hocuspocus,srb2,uzebox,\
12230    amiga7800,amiga7800mess,fornaxvoid1
12231
12232_palette2code : # Convert a set of input palettes
12233  sort_list +,n
12234  repeat $! l[$>]
12235    r {whd},1,1,100%,-1
12236    img2base64 0,0
12237    e[] "_palette_"{n}" : "
12238    b64=\"${}\"
12239    l[] ('$b64') s x,-119 ('\\\\\n') a[0--3] .,x rm. a x b64={t} rm endl # No more than 120 char / lines
12240    e[] "  base642img \\\n"$b64"\n"
12241  endl done
12242
12243# The color palettes below comes from the CImg library 'http://cimg.eu/'.
12244_palette_default :
12245  256,1,1,3,[16+32*int(x>>5),16+32*int((x>>2)&7),32+64*int(x&3)]
12246
12247_palette_hsv :
12248  256,1,1,3,[x*359/(w-1),1,1] hsv2rgb. round.
12249
12250_palette_lines :
12251  base642img \
12252"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2MTcKeJwNkhGgu1AYxT8YDP4QDB4Eg+BKEATBYBAEQXDhQhAMHgweBBeuXB\
12253gMgmAwCIIgCIJBEAQPguBKEASDwWDwYDD4/tGx75zf+Q4gAuhCPFVzM0CgvUXFmr0plIpkB2KwW4bppr16aMgRzge16wbwTbxW0L6Y0IxsjiFyZyQvAISoD\
12254PFMYgWlnTUI/M4bsllO+I5M0GuoWm+LP6kImrtktjhu8sxjaJ9V6zwNDy0FijoRcloRzGvghbDTAiO+Z8/Hv9JoKRbRr79JjwXqpOfBGxRasEejCH2kHyhr\
12255uCHk2jXJ5xkgGFdjPgp/4IZ4HDTk4LTjXrAED56dzkIskRGG8Gwh9FdqTh8bkMgp0+JG9tb+xXpLf58ugHG/GoPG6ZmnSGh+Bn1OyCkX23dUAjon753DhyT\
12256JuhiCaxcy8acf79DuuwbQNo/NItIqXXWF491E8R0ECNOppKlesjlaK8wsIqpKwL3QJwnhCygGtbCWbgbvJ0W4GFMOSrgnpIQ9rZHDYMyVS261dE1U1B/hkU\
12257InqiU/44MDQ/m4BMtHV9Wq5bgqjtJ6laFWKUDtOwsRFvaMLvSAZbHj+u9o/u0itAN5xHX85KSu4GblHN0BJgEHfPGtksZjK4JkzFY1AvkIMTEJBX7tO4HCF\
122586Hov85VvUXaEWosOIqirYUDkWW/OH7FGP18B9h0jjs49tyukEYSKYiZTjCmPnkxI7neAWkOOTxrs4A3blzFEhvJsgMXqmsicxgRGMFil7o5wS3HuIZmAGcc\
12259g8wvlM8UfPWXpfJN5JTPj+YlnxB6QD6tvGmdD9oUxoBrWc3mf8h5fjM="
12260
12261_palette_flag :
12262  base642img \
12263"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICMyNAp4nPv/n4Hh/wjGDCMeD4JIGEAMAH3RPtA="
12264
12265_palette_cube :
12266  base642img \
12267"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjMxIDEgMSAzICMzMzgKeJxj5BCQkFfXN7Nz8wuLS80trWnpmTJn8erNuw6dunTr0cuPP39/e/f83v\
12268Xzxw/s2LBy4cyJnY1VRVnJ0cHezjYmOqqyoryszFxCUoqahhaOHgER8en55XVtfdPmLV27dc+RM1fuPH79+ffvL2+e3Ll69ui+beuWz5ve395QUZCRGBHo6\
12269WhlpKUsLczNwsIjIqOsbWzt7BUUlZhZWNnYMWHGguXrt+87fu7avadvv/759enVo1uXTx/es2XNkjlTe1vryvLS4sL83e0tDDQUJQQ5Wdj4xGRVdU1tXX1C\
12270YpKzi6ubuybNWrhy486DJy/cePD8/fd/DIQAE6eghKKGgYW9u39YXFpeWV1r79Q5S9Zs2XP49OVbj159+vWf9oCQN5kI+oIOgIVbWFpZy8jK0TMwIjGjoKK\
12271hvX/6vOXrtu07evbqnSdvvvymQ0ABAC/raR8=" r. 256,1,1,3,3 round.
12272
12273_palette_rainbow :
12274  base642img \
12275"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjI1IDEgMSAzICM1NjcKeJxj5ublE+DnF5KQUVBWU9fS0tHV0dXVAVL6RiZm5haWFpaWVpZWVtbW1i\
12276BsbWMDJq3BwMrK0sLMWF9LRYqLkYGRAQygFAYbG2DkVrH2iUjNq2zpnTp3+eadB09fvvXo+dvPP/7+//v375/fv37++PH9+7dvX799/fr1GwQAWV+/ACEUA\
12277Bmf3796+uDW1XMnD+3dum75wpkTOhvK81Ii/Z3NDbSUxNhZuPkEhISFhMWl5RRV1LV09AyMjE1MLWwcnJxd3T29vH18/fz9AwIDg4JDQCA0DAjCI0BkRGRk\
12278dExsQnJaZm5ReW1T54Tp85au3rBp8+ZNmzZt3Lhxw4YN69euXbd2zWpMABTfsHHTpq3bd+7ae+Dg4eMnTp+9cPnC2RNHDuzatnH10gWzJvW01JUXZCbHhvt\
122795OttbW5gYGepDgJ6enq6OjpaWpoa6mqqKorysnJy8goKCopKSopKigrycrLSkuIggHy+/kJCQAB8PFzOHiIyKnqm9R0BYbHJmXmlVY2tn38SpM2bPmTd/wc\
12280JFi5csWbZ82dJly5avWLFiJRCsAoPVYGL1GqBDN27eugPoyqMnz1+/++gxMCRPH9mzdd3KhbOn9rU3VBZmxEcEByGD4BAwPzgoMDAgwM/P19vT093d1dnJ3\
12281trK3NhIX0dDTVUJ5GCgi5VVVIFpSkdXT9/A0AAGQH7U1dXW1gJ5UA2E1NTUNdQ1NDTU1dXUVJQU5WQkxYT4efkFhIQE+Xm5mAHNkQH/"
12282  r. 256,1,1,3,3 round.
12283
12284# The color palettes below have been converted from
12285# 'https://stackoverflow.com/questions/33273340/matlab-set-color-map-color-range'.
12286_palette_parula :
12287  base642img \
12288"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgMSAxIDMgIzIwMwp4nAHAAD//NTY2NDAoHAwDAwUJDhETFBQSEAwJBwYGBggLEBYdJS44Qk1ZZX\
12289F7hpGbpK22v8fP2N/n7/b8//79+/j29fX2+C0zOT9FTVZeZGlucnZ6foKGio+UmZ2hpKeprK6xs7W3ubu8vb6/v7+/v76+vby8u7q5ubm7vsLHzNLX3OLp8\
12290PiMmKWyvsvX3uHh4N7c2tjW1NPS0tHPzcrGwr65tK+ppJ6Yko2HgXx4dHBsaGVhXlpXU09LRj85NDAsKCMfGhUQJK9mpg==" r. 256,1,1,3,3 round.
12291
12292_palette_jet :
12293  base642img \
12294"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNzYgMSAxIDMgIzEzMQp4nGNgwA24JFSN7H2i0ksa+2av3Hb4wr3XP/9jgt/vH145tnPN/ImtMH1MvN\
12295Iapk7+sVnlLRPnrdl57PLDd7+xaPz55t7Fw9tWzulrKkmP8rU3UpXgYoTbPWXh+j0nrz35+BeLxm8vb587sHnZzO76wpRwLxt9JVF2PL5gYAAAc+1twA=="
12296  r. 256,1,1,3,3 round.
12297
12298_palette_hot :
12299  base642img \
12300"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI0IDEgMSAzICMxNDEKeJzjFRKXVdLQNTK3dXb3DY6IS87IK66oa+nsmzJz3pKV67fu2n/k1IWrt+\
123014/ff3+66//1AUMpABGdl5BcRlFdZBTndx9g0BOzS2urGvuADl18cr1W3buP3LyPMipr4BO/UdNu0kEjFxC0kpaRtbO3kHRydnFNS3dk2cvXr1lz5EzV+88e\
12302fP1LwA796yq" r. 256,1,1,3,3 round.
12303
12304_palette_cool :
12305  base642img \
12306"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzIgMSAxIDMgIzc4CnicY+ERkVHRMbFx8QmJSckpqWnpmTJnyZote46cuXLnyZsvf/58efPkzpUzR/\
12307ZsWbNkzpSelpqSnJSYEB8XGxMdFRkRHpb/BAAAHuY/4Q==" r. 256,1,1,3,3 round.
12308
12309_palette_spring :
12310  base642img \
12311"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjggMSAxIDMgIzcxCnic+/8fN2DhFVPQNLJ28Q1LyCyqbumdtmDlpt1Hzl578PLT79+fXj64dvbI7k\
123120rF0zrbakuykwI83WxNtJUEONlAQDKiDfN" r. 256,1,1,3,3 round.
12313
12314_palette_summer :
12315  base642img \
12316"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjggMSAxIDMgIzcxCnicY+EVU9A0snbxDUvILKpu6Z22YOWm3UfOXnvw8tPvprbuCVNmzl20bPX6LT\
12317v2Hjx26vyVm3cfPXv94cvPv2l4AACR+i4M" r. 256,1,1,3,3 round.
12318
12319_palette_autumn :
12320  base642img \
12321"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjYgMSAxIDMgIzQyCnic+/8fF2Dhk1DSMXPwDI5NL6xu6ZuxaM22Aycv333+8TcDTgAAaHcm2w=="
12322  r. 256,1,1,3,3 round.
12323
12324_palette_winter :
12325  base642img \
12326"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzEgMSAxIDMgIzc5CnicY2DAC1h4RGRUdU1t3fzC4tLzyxs6JsxYuHLjzkOnLt16/Prz778/v358+/\
12327Lpwzs3rlw4ffzw/t3bN69bvXzxvFnTJvV1tTUBACEGJp4=" r. 256,1,1,3,3 round.
12328
12329_palette_bone :
12330  base642img \
12331"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KOTkgMSAxIDMgIzI1Mgp4nGNkZuXg4uETFBaVkJKRU1BWVdfU0TMwNjG3tLZzcHJx9/T28w8KCY+Mjk\
12332tISk3PzM4rKCwpq6iqbWhqaevs7p0wccq0GbPnzl+4ZNnKtRu3bN+9/9Cxk2cuXLl+696jpy/ffPj8/dc/RmKsCI2IiU9KzcjJLyqtqK5rbGnv6ps4Zfqsu\
12333QsWAw3esHnbzr37Dx09cfrM+YtXrt24fef+w8fPnr968+7j56/ff/35x8TKwcMvJCopLa+koq6lZ2hibmXnCDTZNyA4PDI2ITktE2hwSVlldV1DU2t7Z3cf\
123342N2zwO5esXrtho1btu3YvXf/wSPHcFoBAJTokrQ=" r. 256,1,1,3,3 round.
12335
12336_palette_copper :
12337  base642img \
12338"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTE1IDEgMSAzICMzMzkKeJxj4+TmExQWk5SWU1RW09TRNzKxsLK1d3b18PINCA6NiI5LSE7LyM4rKC\
123396rqK5rbGnv7O2fNGX6rLnzFy1dvmrthk1bd+zee+DQ0ROnz164fO3mnXsPnzx7+eb9xy/ff/359x87YGFl5+Dm5RMQFBYVk5CSlpVXVFZRU9fU1tEzMDIxN\
12340be0trG1d3R2dfP08vH1DwwKCY2Iio6NT0hKTk3PyMrJKygsLi2rqKyurW9sam1r7+zu7eufOGXa9Jmz5sybv3DxkmUrVq1Zu37Dpi1bt+/avXffgUOHjx5j\
12341YgZZysnFzcPLxy8oJCwiIiYuISklLSMrp6CopKyiqqauoakFdIG+gaGRsYmpmbmFpZWNrZ29g6OTs4ubu4ent4+vn39AYFBwaFh4RGRUdExsXHxCYnJKalp\
123426RmZWdm5uXn5BUXFJaVl5RWVVTW1dPQCk5ZAs" r. 256,1,1,3,3 round.
12343
12344_palette_pink :
12345  base642img \
12346"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTIxIDEgMSAzICMzMDUKeJxT1DG1cfbwC46ISUzNzC0sraiub2rr7O6bOGX6zDnzFy5ZtnL1ug2bt2\
123477fuXvvgUOHDx85euz4iZOnTp85e+7c+QsXL12+cuXqtes3bt66ffvO3Xv37j94+PDR4ydPnj57/uLFy1evX799++79hw8fP33+8uXr12/ff/z49ev37z9//\
12348/37zyoso6SurW9sZmVr7+Tq7uXjHxQSFh4ZHRuflJyanpGVnZtfWFRSWl5RWV1b39Ta0d0PddLiZSvXIJx05NiJU2fOXbh89fqNW3fu3X/0BGgzFS1uaGxq\
12349aW1r7+zu6e2bMHHS5CnTps+YOWv2nHnzFy5avGTpsuUrVq5avWbtuvUbNm4COmn3vgOHj504fRbonhu3795/+OTZi1dv33/68u3Hrz//AH2168E="
12350  r. 256,1,1,3,3 round.
12351
12352_palette_vga :
12353  base642img \
12354"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICMxNzgKeJz7/+fn10/vXz9/8uDOjasXzpw4sv/IiTMXrt648+DJ89fvP339+ec/Bv\
12355jx8dXj25dOHdi2ZsGU1tIEVwbCwDWhtHXKgjXbDpy6dPvxq48/MM2Q0zF3cPcLiYpPycgtLK2saSAMaipLC3MzUuKjQvzcHcx15IhwB4Yt/zH8v2vr+pWL5\
1235607r72goz00KdTPEdDthQEYIYbgMw3c0CSEi/E9G7JIRQoT9T0YYEgYAJsphWQ=="
12357
12358# The color palettes below have been converted from 'https://matplotlib.org/cmocean/'.
12359_palette_algae :
12360  base642img \
12361"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNTAgMSAxIDMgIzE2MQp4nAGWAGn/1M3Hwbq0raegmZKLhH11bWVcU0k+MygeFg8JBwYICg0PEhQVFx\
12362gZGRkZGRkYFxYVExL38u7p5ODc19PPy8fEwLy4tbGtqqainpqWko2JhH96dnFtaGRfW1ZRTUlEQDw3My4qJczFvbavqKGak42GgHp0bmhjX1pXVFJRUFBPT\
12363k1MS0lIRkRCPz07ODUyLywpJiMgHBkV1p88Lw==" r. 256,1,1,3,3 round.
12364
12365_palette_amp :
12366  base642img \
12367"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjEgMSAxIDMgIzE5NAp4nAG3AEj/8O3r6ejm5OLh397c29nY1tXU0tHPzszLycjGxMPBv728uri1s7\
12368CtqqainpqVkIuFgHp0b2lkXlhTTkhDPurm4dzX0s3Iw765tLCrpqGcmJOOiYSAe3ZxbGdiXVhTTklDPTgyLSgjHhkWEhAODg0ODg4ODg4NDQwLCgnq5N7Y0\
12369szGwLq0rqihnJWPiYN9eHJsZmBbVU9KRUA7NjIuKygmJCQkJCUmJicoKSkpKCcmJSMhHxwaGBUS64BWpQ==" r. 256,1,1,3,3 round.
12370
12371_palette_balance :
12372  base642img \
12373"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTIxIDEgMSAzICMzNzQKeJwBawGU/hgaHB4gIiMlJigoKSkoJyQgGhMNCgoMERYcIictMzg+Q0lOVV\
12374thaG51fIKJkJado6mwtrzDyc7V2+Dm7PDu7Oro5+Xj4t/e3dva2NfW1NLR0M7Ny8rIxsXDwcC+vLq4tbOwraqno56alZCLhYB6dG9pY15YU01IQj0dICImK\
12375CsuMDM2OTs/QUVJTFFVW19kaGxxdHh8gISHi46SlZmcoKOmqqyvs7a5vL/CxcnMz9PW2t3h5ens6OPe2dTPysXAu7axraijnpmVkIuGgXx3cm1pY15ZVE9K\
12376RD45My4oIx8aFhMQDw4NDg4ODg4ODQ0MCwoJREpRWF5lbHN7goqSmqGpsLa7vb69vby8u7u6urm5ubm5ubq6uru8vL2+v8HDxcbIys3P0dXX2t3f4ubp6+f\
12377h2tXPyMK9trCqpJ6YkYuGf3l0bWhiXFZQS0ZBPDczLisoJiQkJCQlJSYnKCkpKSgnJiQiIR4cGhcVEqaTs4c=" r. 256,1,1,3,3 round.
12378
12379_palette_curl :
12380  base642img \
12381"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI0IDEgMSAzICMzODEKeJwTFROXkJCUkpKWlpaRARJSkuJiosJCggKCwmLSCup6Znbu/uHx6fnldW\
1238229U2YtXLF+6+6DJ85duXn/6euP3/78+fn96+eP79++efXy+bOnjx89uH/3zu2b169duXT+7KkTRw/u271984Y1K5YsmDN9Ul9nS31VaUF2anxUqL+Xi72Vq\
12383ZySmpaekZmVraOrp29gSERMQnJGTkFxeXV9U3tX38SpM2bPW7h0xaq1GzZv27F738Ejx0+dvXDl+q27D5+8ePPhy+d3r57cv3X1wumjB/ds37hm+UKgHb0d\
12384TbUVxXmZKQnR4UF+Xq6OtlZmxvraGqpKCrJgn4kIg/wmwMfr4ubh7esfFBIWGR2bkJSSnpWTV1BUXFpeUVVdU1tXV9/Q2NTc2tbR1Tth8rRZcxcuXbVu846\
123859h0+cvQz2+cfXT+5eO3/i4O4ta5cvnD21v6u1obq8uCA3KyMtJTkpMSEhIT4+PgEG4uPjYqMjw0OD/L09nO2tzACwu7nF" r. 256,1,1,3,3 round.
12386
12387_palette_deep :
12388  base642img \
12389"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDkgMSAxIDMgIzE1OAp4nAGTAGz/+e7j2M7Dt6yhlYqAdm5mYVxYVVJPTUtKSEZEQ0FAPz49PT0+P0\
12390BBQT8+Ozk2MzAsKfv38/Ds6OXh3dnV0czIw764s66po56Yk46Ig396dG9qZF5ZU05IQz46NjIvKycjHxvJw724s6+rqKWko6Kjo6Ojo6OjoqKhoJ+dnJuam\
12391ZeWlZSTkY+Mh4B4b2ZeVU1FPTYvM2JJnA==" r. 256,1,1,3,3 round.
12392
12393_palette_delta :
12394  base642img \
12395"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM3NzAKeJwTEBIWEROXkJSWkZWTV1BUUlZRVVUDAlVlJUV5OVlZGWlpKRCQlp\
12396aWASmBqFFT19DU0tHVNzA0NjWzsLKxc3Bycff08Q8Ki4xNSMnIKSipqGlo6ejpnzx91rxFS1euWb952869Bw4fP3X2wuVrN+8+ePzs5Zv3n7/9/Pvv7+9fP\
1239779/+/r508f37968fvXi2dPHjx7cu3PrxrUrl86fPX3i6OEDe3dt37Jx3arlSxbMnTVtcn9PR2tjXVV5cUFORmpibFRYkL+3u4uDraWZkb62hqqSvIykuIiQ\
12398AB8vDzcXFzc3Dw8vH7+AINCbIqJiYuLiEhISkmAAZIiLi8srKCmrqqlramnr6ukbGBmbmJpbWFqDPeTm4e3rHxgcFhEVE5eQlJKWkZWTV1BUUlZRWV1b39j\
12399c2t7Z3ds3YdKUaTNmzZk7f+HipctXrFqzdv3GTVu2bt+xc/eeffsPHjpy9NiJk6fPnLtw8fKVa9dv3r5z78Gjx0+fv3z99t2HT1++/fj15+f3L58+vH398t\
12400mTR/fv3r55/eqlC+fOnDp5/Ojhgwf27dm9c8e2rZs3bVy/bu2aVStXLF+2ZPGihQvmzZ0ze+aM6dOmTp40sb+vt7urs72tpbmxoa62uqqirLS4sCAvJyszP\
12401TU5MT4uJioiLCQowM/H29Pd1dnR3tba0tzUxMhAT0dLQ01F2d7JFRJvMQnJ6dl5RWVVtY2tnb0TgL6aOWv27Dlz5gLBvHnz5s+fv2DBgoULFy5atHjxkiVL\
12402ly5dtmz58hUrVq5ctWr16jVr1q5dt27d+vUbNmzYuHHT5s1btm4DhsEuUCAcOHj4yNHjJ04BQ+H8xUtXrl6/cQsYDPcfPHz0+MnTp6ePH96/e9umdSuXLpw\
12403zY0p/d3tTXVVpYW5GSkJ0eDAwYp3trS1MDHS11JUVZKUlRIUF+Hi4ONjZWIGAjZ2Dk5uHj18QlIIlpYCpE5J+gYkTmDq1tLV14EBbW1tLS1NDQ11NVQWYsB\
12404Xk5WSkpSTERUWEAGwPYP8="
12405
12406_palette_dense :
12407  base642img \
12408"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNzQgMSAxIDMgIzIyNAp4nHt878alM8cO7N66YfXSBbOnT+rram9uqKmqKCstKQaBktLSsvLyioqKys\
12409pKIFleDpIoLMjLyc5MS0mKj4kMC/L38XBxsDF//+bV8ycP792+ce3ShTMnjx3ev3fXts3r16xYsmDOjCkTejpaGmoqivOz05Pjo8NDAnw8XB3trMyNDfW0N\
12410FSVFGSlJcXFRISFhQQF+Pnev3398sWzp0+ePH4EA48fP34CBo8fP3p4/+7tm9euXDh78tihfbu2bly7YvG8mVP6u1rqK0vyMpJjwwN93BxtzI30NFUBgy55\
12411hg==" r. 256,1,1,3,3 round.
12412
12413_palette_diff :
12414  base642img \
12415"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTQzIDEgMSAzICM0NDAKeJwBrQFS/gcJCw0ODxESFBYWGBseIiYqLjI2Oj5CRUlNUVRYXF9jZmtvcn\
12416Z5fYCEiYyQk5ebn6Onq66yt7q/wsfLz9PX3N/k5+vu8fP19fX08e/s6ebi39zZ1dLOy8jEwr67uLWyr6yppqOgnZqYlZKPjIqHhIF+fHl2c3Bua2hkYF1ZV\
12417lJPS0hFQT47NzQwLSomIyAdIyYpLC8zNTk7P0JFSEtNUFNVWFteYGNmaGtucHN2eXx+gYSHioyPkpWYm56hpKeqrbC0trq+wMTHy8/R1djc3+Pm6ezu8PHx\
124188O/s6ufj4NzZ1dLOy8fDwLy5trKvq6mlop+bmJWSj4yJhoOAfXp3dHFubGhmY2BeW1hWU1FOTEpHRUI/PTs4NTIwLSooJSJAQ0dKTlFUWFteYWRmaGpsbnB\
12419yc3V4enx9gIKEhoiKjY+Rk5aYmpyfoaSnqauusLO2uLu+wcTGyszP0tXY29/i5efq7O7v8PDv7Onm4t3Z1dDLxsK9ubSvq6einZqVkY2IhIB8d3Nva2djX1\
12420tXU09LR0RAOzg0MS0qJyQiISAeHRsbGRgWFRQSEA4ODAoIBjEs26k=" r. 256,1,1,3,3 round.
12421
12422_palette_gray :
12423  256,1,1,1,x r. 100%,1,1,3
12424
12425_palette_haline :
12426  base642img \
12427"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDYgMSAxIDMgIzE0OQp4nAGKAHX/KiwuLSgiGxQPDAwPEhcbICUpLTA0Nzo8QEJGSU5TWF9mb3mEkJ\
124286sucXR2+bw+RgaHB8mMDlBSE9VW19laW5zeHyBhouQlZqfpKmus7i9wsbLztLV2Nrd4OPm6e1xfoyZoaKfnJiVkpCOjIuKiYmIiIiHh4aFhIOBfnt4dHBrZ\
124292JeW1xgZW12gIqU7TdADQ==" r. 256,1,1,3,3 round.
12430
12431_palette_ice :
12432  base642img \
12433"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA5IDEgMSAzICMzMzgKeJwBRwG4/gQGBwoMDhETFRgaHB4gIiQmKCorLS8wMjQ1Nzg5Ojs8PD0+Pj\
124344+Pj4+Pj4+Pj4+Pj4+Pz9AQUJDREVGSEpLTE9QUlRWWFpcXmFjZWhqbXBzdnl8f4OGio6Slpqfo6essLS5vsLGy8/T2Nzg5OkGCAkMDhASFBYYGRwdHyEjJ\
12435CYoKSwtLzEzNDc4Ojw+QEJER0lLTlBSVVhaXWBiZWdqbW9ydXd6fX+ChIaJjI6Rk5WYmp2goqSnqayvsbS2uLu9v8LEx8rLzdDS1NfZ293f4eTm6evt8PL0\
124369/n7ExYaHSAkJyouMTU5PEBDR0pOUlZZXWFlaW1xdHh8gISIjI+Tlpmcn6KkpqiqrK6vsLKztLW2t7i5urq7vL2+v8DAwcLDxMXGx8jJysvMzc3P0NDS09P\
12437V1tfY2dvc3t/h4+Xn6evt7/L09fj6/ETzn2w=" r. 256,1,1,3,3 round.
12438
12439_palette_matter :
12440  base642img \
12441"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgMSAxIDMgIzIwMwp4nAHAAD///fz8+/v6+vn4+Pf29fTz8vHw7+3s6ujn5eLg3drX1NDNycXAvL\
12442ezrqmkn5qVkIqFgHt1cGplYFpVUEpFQDs2Mevl4NrUz8nEvrmzrqijnZiSjYiCfXdybGdiXVhTTkpGQj46NzMwLSsoJiQiIB8dHBsaGhkYGBcXFhYVFBMSE\
12443Q+uqKOemJOPioWBfHh0cGxpZWJfXFpYVlRTUlJSU1NUVVdYWVtcXV5fYGFiYmNjY2NiYWBfXVxaV1VST0xJRkM/PV5d6A==" r. 256,1,1,3,3 round.
12444
12445_palette_oxy :
12446  base642img \
12447"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2NTYKeJyzd3RydnP39PL18w8MDg2LiIqJjUtMSknNyMzOyS8oKi4rr6iqqa\
12448tvaG5pbevo7Oru6e0LDAgMDA4JCQ0Pj4iMigaqj09ISEpOTklLS8/IzMrOyc3LLygsKi4uLSsvr6yqrq6tq2tobGoCGgAzoX/CxEmTp0ydNn3mzFmz586bP\
124493/hosVLli5bvmLV6jVr163fsHHzlq3btu/YuXvP3n0HDh46dOToseMnT50+c+78hYuXr1y9duPmrdt3791/8Ojxk6fPX7x89ebt+w+fPn/5/u3L54/v3719\
12450/frVq1cvIeAFEDx//vzZs2dPnz558uTx40ePHj18+ODB/fv37t29e+cOKxCwQQE7DED5rGDAwgJSws7Bxc3HLygkKibh6x8QGBQcEhoWHhERFR0dEwcMgMS\
12451k5JTUtPT0zKys7Ny8vPxCYACUAgOgAhgANbV1DcAAaAGFIDAAenp7+4D+nwTy/wyg/+cAAwDo/8Vg/wMDAOT/TZu3QP2/dz8wAAj7/937T5+//vv75/fPH6\
12452BQ+PTh3ZvXL58/ffLowb27d25ev3b10sXz586cOnni6JFDB/fv3bN7x/ZtWzdt3LAO4n92dg4OTk4uLi5ubh4eHl4g4AMCfhAAMYB8Hm5uLk5OTg4OdnZvs\
12453P+DQ8LC4AkgPjEpKTk1FSUBFJWUlpVVVFZW1dTW1jc0NgJTUGt7R2cnSgIABsCcuXPnLYAmgJXAAAAlgE2bNoMSwK7de/bsAwbAYWAAnAAGwNlz585fvHT5\
12454yrXrN27evnP3HigAngED4DUwAD58fJSRlpIYExUe4uft4epgZ2NlYWZqYmRooK+ro6OtqamhrqaqqqKspKSoIC8vJysrIyMlJSkJADQjlG8="
12455
12456_palette_phase :
12457  base642img \
12458"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA2IDEgMSAzICMzMjkKeJwBPgHB/qissLO2uby/wsTHyczO0dPU1tjZ29zd3d7e3t7d3NvZ19XSz8\
12459zJxcG9ubWxrKeinpiTjYeBe3VuaGFbVE1HQTs1MCsnIyEeHBkXFRMRDw0MCwsOEhgeJS01PkdQWWJqcXh/hIqPlJidoqZ2dHJwbmtoZWNhXVtYVVJPTElFQ\
12460j47NzQwLSsoJiUlJicpLC8zNjo/Q0dLT1NXW19iZmltcHN2eXx+gYOGiIqLjY6PkJGSk5OUlZWWlpeXmJiZmZmampmZmZiXlpSSkI6MioiGhIF/fXp4DhIX\
12461Gx4iJiouMjY6PkNHTFFXXGJobnV8g4qSmaGor7e+xMvQ1tvf4+fq7O/w8vPz9PPz8vHv7ero5eHd2dTPysbBu7axrKejnpqVkIyHgnx3cWtlXlhRSUI5Mio\
12462jHRcTEA8ODQ0NDQ0NDPDwmdk=" r. 256,1,1,3,3 round.
12463
12464_palette_rain :
12465  base642img \
12466"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTE1IDEgMSAzICMzNDIKeJx7++bVy2dPHz96eP/e3Tu3b928cf3q5Yvnz5w8dmjf7u2b169evmTBnB\
12467lTJvZ2tjbWVpYU5makxEeHBfq4O9taGOtpqirIiIsI8nJxsLGyMDMzs7CwcXBy8woIiYhJSEnLyikoKCopq6ioqIIAkFZRVlZSUnzz6vnTRw/u3bl57cqlC\
124682dPnzh25NCBfXt37dyxfduWzZs2rl+3ds2qlSuWL12yeOGC+fPmzJ41Y/q0qZMnTZzQ19Pd1dHW0txYX1tdWVFaXJifm52ZnpqcGB8bFREWEhTg5+Pl7urs\
12469aG9rbWluYmSgr6OloaaiKC8r/fHdq+ePH9y9efXimZNHDgA9tm7V0oVzZ06bPLG/p6sTZGZTQ31dbU1VZUV5aWlJcVFhQX5+Xl4uDORBAYidk5OTnZWVmZm\
12470enpaanJSYEBcTHRkRGgy23RNovZMD0H4rSwDxo6Ac" r. 256,1,1,3,3 round.
12471
12472_palette_solar :
12473  base642img \
12474"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzUgMSAxIDMgIzExNgp4nAFpAJb/N0BJUltkbXZ/h46VnKGnrLG1ur7BxcjMztHU1tja3N3f3+AVGB\
12475odHyIlJysvNDpARk5VXGRrc3uDi5OcpK22v8jR2+Xu+RkcHyEjJCQkIiAeHBoYFhQTExITFBUXGh0gJCgsMDU6PkNIm9UqlA=="
12476  r. 256,1,1,3,3 round.
12477
12478_palette_speed :
12479  base642img \
12480"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDkgMSAxIDMgIzE1OAp4nAGTAGz//fn28+/r5+Pf2tXOyMG5samgl46FfHJpYFZNQzoxKCAZEg4LCg\
12481wOEBMVFxgZGRkYF/rz7efh29bQy8bCvbm1sq6rqKWinpuYlZKOi4eDf3t3c25pZWBbVlFMSEI9ODMvKSTIvbKnnJCFeW5jWE1COS8nHxgRDAcFBQgLDxQYG\
12482x8iJScpKywsLCwrKiknJCIfGxgUF7w7kA==" r. 256,1,1,3,3 round.
12483
12484_palette_tarn :
12485  base642img \
12486"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA1IDEgMSAzICMzMjYKeJwBOwHE/hcaHiEjJyktMDU8Q0lQV11kanF4f4aNk5uiqbG4v8XKzc/S1N\
12487bY2t3f4uPm6evu8PT2+vv8+/j08Ovn4tzWzsa+t6+ooJmSioR9dm5nX1dPRz84MSolIh8eHRwaGRcUExAOCwoICQsODyQoLTE1OT5CRklMT1JVWFpdYGNlZ\
124882psb3F0dnl7fYCEiY6Ump+lq7C2vMHHzdLY3uTq7/T39vPu6eTf2tXRzcrGwr66trOvq6ekoJyZlpKOi4eDf3t3cm5qZGFcV1NOSkVBPTgzLikkIA0ODw8Q\
12489Dw8ODQwNEBETFhgbHR8hIyUnKisuMTQ3O0FIUlpia3N8hI2Vnqavt8DJ0dri6/H18uzj29HJwLewq6ikop+dm5mXlJKPjYuJh4WEgoF/fnx7enh2c3JvbWt\
12490paGZkYmFfXVpVUfhNmgM=" r. 256,1,1,3,3 round.
12491
12492_palette_tempo :
12493  base642img \
12494"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTEyIDEgMSAzICMzNDcKeJwBUAGv/v369/Tx7uvn5OHe29fU0c3Kx8PAvLm1sq+rp6OgnJiVkI2JhY\
12495B9eHRwa2hjX1pVUUxIQz86NTEtKSUhHhsYFhMSEREQEBEREhMUFBUWFxcYGRkaGhsbGxscHBwcHBsbGxsbGhoaGRkZGBgXFxYWFRX08vDu7Oro5eTi4N7c2\
12496tjW1dPRz87MysnHxcPCwL+9u7q4t7W0srGvrqyqqaempKKhn52cmpiWlZKQj42KiYeEgoB+fHp4dXNxb21raWZkYmBeXFpXVVNRT01LSEZFQ0A+PDo4NjQx\
12497Ly4rKSclIiAe8+/s6eXi3tvY1dLOy8jFwr+9ure0sa+sqqelo6CenJqYlpSSkY+OjIuKiIeGhYSDg4KBgYGAgH9/fn5+fXx8e3t6eXh4d3Z1dHNycXBvbm1\
12498samloZmVkY2FgX11cW1pYV1ZUU1JQT05MS0pIR0ZFRA7PoM4=" r. 256,1,1,3,3 round.
12499
12500_palette_thermal :
12501  base642img \
12502"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA3IDEgMSAzICMzMjUKeJxjYWFhZWVj5+Ti4RcUkZBRUNUyMLWyd/HwCQiJiI5PSsvMyS8qq6ypb2\
12503rr7J0wefqseQuXrlizYfP2XfsOHj155vzla7fuPnj07MXrt+8/fv7y7fuPn79+/YaAX79+/vzx/dvXL58/fXz/7s2rF8qq6hpa2jp6+gaGRkbGYACkTMzML\
12504aysbe0dnJxd3T28vH39/AODgkNCwyMio6JjY+MTEpOSU1LTMjKzcnLzC4pKyiqraxua2zp7JkyeNmveoqUr127cumPPgSMnzly4cuPOgycv3nz8+tPE0tbJ\
125053ScwPCYpI6+0urGjb8qM2fPmz58/b+6c2bNmzpg2dcrkSRMn9Pf39fb2dHd1drS3tbY0NzXU11RXlpcWFeRlZ6QmJcREhgUH+nl7uLk6Odrb2dqAgK2tHdS\
12506lQIcGBoeGRwIArFub1Q==" r. 256,1,1,3,3 round.
12507
12508_palette_topo :
12509  base642img \
12510"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2OTEKeJxT19TS1tXTNzQyNjE1s7C0sraxtbWzt3dwcAQBBwcHe3s7OztbCA\
12511Cy7MFyTk7Ozi6urm7u7h6enl7e3j6+fn7+AYFBwSGhYRGR0TFxCUkp6Zk5+UVllbWNrZ29E6fOmrd4+eoNW3bsPXj05LlL127de/Ts9fvP33/z8vELCAoJC\
125124uIiolLSEpKScvIysopKCqpqGvpGhibWVrbOTq7eXr7BQaHRUbHJSSnZWTnFRSXVVTXNTS3dXT3TZg8bebseQuXLF+5Zv2mrTt27zt46PDRY8dPnDx1+szZ\
12513c+cvXLp85eq16zdu3rp95979Bw8fPX767PmLV6/fvHv/8dPnr99/SEmDrFNWUVMHB4SBobGJmbmllY2tvaOzq5uHt69/YHBoRBTYR2mZ2bn5hcWl5ZXVtfW\
12514NTS1tHV09fRMmTZk2Y9ac+QsXL12+cvXa9Rs3b92+c/fe/QcPHz1+8tSZcxcuXr567cZNkPVg21+8fP3m7fsPQMu//fj5+4+qmoaWDjD8jYGhD7HVxc3DC2\
12515htQCAwMMMjIqNiYuPiE5OSU1LTMzKzcnLz8guLikvKyisqq2tq6xoam5pb29o7urp7+vonTJw0ZSrIMfMWLFoCdcy2Hbv27AO5BeSUy1ev37pz78Hjp89fv\
12516Xn/8fO3H7/+6ugZmphb2tg7uXp4+wWFRkTHJ6UCI664rLKmvqm1vbO7F2zupMmTp0yZOnXatGnTp8+YMXPmzFmzZs+eM2fu3Lnz5s2fv2DBgoULFwHBYgwA\
12517FVyyZOmy5StWrlq9dt0GRAgdO3EKGPHAmAdHvZS0tLQMMPqBCQAM5MHxoqEJS59m5uagBGptYwNLiSjADgIcXDx8/IPDomITUzKywQmwoaW9u2/S1JlzFix\
12518evmrdxq079xw4fPzUuUtXb9558AQAVgJv5Q=="
12519
12520_palette_turbid :
12521  base642img \
12522"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI2IDEgMSAzICMzNzAKeJx78fzpk8ePHj64f+/undu3bt64fu3qlcuXLl28cP7c2TOnT506cfzY0S\
12523OHDx08sH/f3j27d+7Yvm3L5k0b1q9ds2rl8mVLFi2YN2fWzOlTJ0/s7+3p6mhraWqoq6mqKC0uzM/NzkxPTU6Ii4kMDw0O9Pf19nR3dXa0t7W2NDc1NtTX1\
12524dZUV1X+8unD29cvnz15dP/u7RvXrlw6f/b0yWNHDh3Yu3vn9q2bNqxbvXI5xI4Z06ZMmtAHsaKxvra6sqKsBGRJTlZmelpKcmJCXGxMVGREWGhIcGCAv5+P\
12525t5enh7uri5Ojg72djbWVpYW5qYmxkaG+nq6OlqaGuqqKsqKC/KrlSxbOmz1j6qT+3s72lsa6msryksL8nCyguxPjY6IiQkOCAvx8vb083N1cXZyBZgENs7O\
125261tbWxsbEGASsYsIQACyAwNzc3MzMzNTUxMQbaZ2hgoK8HtFNHW0sTaK2aqoqKshLQZjlZGWkAeF+paA=="
12527  r. 256,1,1,3,3 round.
12528
12529# The color palettes below comes from the LoSpec palette list 'https://lospec.com/palette-list'.
12530_palette_aurora :
12531  base642img \
12532"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2ODYKeJxt0kFo01AYB/B3qYwwaDbCaEG6aS4T3ME4qJrgVpCiLUqolEgYbI\
12533QirIce9kQLknXbZSplWaGMSaeUYWU0g21QD0XbFLaasimMQqmUOiyBHiyOemgZZfpMql7Ew/v43vtOv+//QN/5yze89x89eZ58++HzMQJjQJnXizmktH4gh\
12534JSQN6SgbqO/bgHgfnfwejYwG88d1hqtdivEwSVZ1U60PQnSDiitv1Er31R5Cd5zWkmapTnok9ZrKXmRNdtI2ifJalbekDiONg/Stzj4UJIPNVmCHOOckWS5\
12535gbRaWS3suqnhcSFRKaEWaqNyJRWfvCuEN3fqCBViK9DPDA/1EhghuoHpbH//GTDC+CNesQc3DZgBBSzktGuZzwZdAoMDspedWlzLpMvlkzqqZDJh3uegvPE\
12536CQp3TemY1Av7xG9iuXz/zOaMjxkLGo/KnilvJeHQhMB18FkumCzVAO7xQkovqS8iN/PVralYSWRvL+fRRTv10cqylxGl9rG/qe1FNG2Cr4Q/o/opmrIOkxj\
12537kOblbVdFwS+eEBzGIhhjwML2QSsQDvplhhLrpTKn1FzWphIzDqnFgp11EznwpfWd07avxEjXr5aI2/aHf5PP7gi1fb+XSAdzBncfwcjlMOIZLYfQ89Qxdww\
12538kIAzGoFrD9+zYaPEv/zG0xWzx8ddP3GJhS9GPnrAeSeLkcXpyZnHojh2FYR0Yzhr8iQowdJmvPqMaqy5OWuU2Y7zeoRL0jrnf380qSTdHb9Ta2qqhIc0b+G\
12539lNzXvw4yrr/zr7W0YjYdj9hJWx/AcBdGCZcYMwZMBAZ4gPG4y+G0mkyEqafXhGGQJ9g71FzEv5H4iFCzdDThEddW2pnqrk7oNFG7VclEb24fFg1S5xShLyi\
12540ffewTnPaFoJJPh4NXb9t/Aejmgpc="
12541
12542_palette_hocuspocus :
12543  base642img \
12544"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2MzQKeJxjk3x5buu8lopLu5f0lsV5mHk7WxlpKkrINhWlhnla6iotmtpakR\
12545Ht7b5oUlNRUpCT+anN87qK4jzteqtz4/wdjVRfXNy1pK880UdUiJ+Xi52VyUJTXpSPg1ns57Vt08sjLGTeHp5bFW2vovrr3qFVfWVRLk9Ob53TmhMW8OT0p\
12546tlNOUF2DASAqIiwkKAAPx8vDzcXFycH+9PrZw5uW71g+uJpnQ1lWQkRMGW/Hp3aNKspK1wUAjR/3Tu2blpdWsDzFy9fv3n3/uOHG4fXz2orjGfklTN0DEwq\
12547bZ26bOfJm6/+MIg83t2fZq1amRUb6GiqIQu0go2NlUVgamtpWpibuUZ+Upifq6WBfn1xerSfk5n29LaytDB3c8OOyowoL0sdhRktxUlBrqYWx3fPbczwMZD\
12548ePK0ywdtcWevjkRnFweYy3Dc29GT7mcrIf9ndGW+rxM0Mda/AgcWdBVHuRqpk+n9qX3NFYWZ8eKCosCAfDxcHO5Dm5+XmZP9258jKSeUJEcBwnt9ZHOt5cP\
12549Oymf1NpSncvHwCgkIiouj+F5adluOmIchflwfyhqbslb3LJlQm+bismNJSkOBjo1eQHO4P9L/pjiVTmvPj/ewXT2goiPGxNN+zfEp9TpiLcU9lZoyPvZGqi\
12550CAo+pkZfz29sHvZxJriO1vA/uZ+cnhZZ7afoQjMP0yioiIiIsLCwkJCQoKCggLK3z69evbo3q1rl86dOnZo365tm9YuWzRv1rRJfV1tTXVVZc115cX52enJ\
125518dHhwf7e7s6/P715/vDOtQtH9+3avnn9mhVPz+9cPLE61R/mf3h6gMY/AyMzCxs7hxyu+AcA2aYUlw=="
12552
12553_palette_srb2 :
12554  base642img \
12555"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM0OTcKeJz7//398/vXzx/fv3398vnT+9vry/PT48P93e3N9dXlxfnZGcAS0/\
12556ra6kpzU2NDfJz3b169eDZIXXZ8sLe9sfZ/GIAYdBrG/fz0xul9G5dMh8ufX9+f76/P///19cPrZ7cXw4Tf3z+/f/38/vr8eH97fXn+/5+f3z69f/NyqIWh5\
12557QWZiVFB3s42prpqckD33Du+dmpNipcRAxT8/39///z6ePuXR+aWhzjqSV/dv2pqQ1Yo2Pkg+efH1vYUBxlKMKCB89vnt+eH26v/J+T/5QtmTOhsrCzKSooK\
12558BjrDuLo4Pzs5PjzY390ZqEZb+fXj25eB2neDgmVyd3P9uzund66Y3gZ2vK+rrRmG/2/vnl0d7awNcbY8uru2L+6vzo72t9dTV5AU4mZl+P/hwYUDGxZMaCh\
12559ICHAwUOD//+TE2knlUfZQb8vnQ/XB/P/x9eO7186f2D+3vzx+P1ABDv//h4c3If/3dzRUFILiwMvJ2lhbRdob6G9rc2N9bXVleWlxYf77l4/v3rx89uTm6u\
12560Ls5Ohg/zs7p5eGmknCbXoP9TeMXxzurC8vzM5AwP8wvr+3m6ONuZGuhrKcpAg/F8uKKQ3ZES4Gsujx//nhud3Lm2NMYKENEgX5Hx78z89tndOSHeRvb64uz\
125618/+Hxb/ADJVV38="
12562
12563_palette_uzebox :
12564  base642img \
12565"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1Mwp4nGNQ8ciduO3Wf4aRSkOBChR4QEEuFEyEgm1QcAsK/kPBcNFPLgilEK\
12566yiEPynEAAA16V90A=="
12567
12568_palette_amiga7800 :
12569  base642img \
12570"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1MzMKeJxjEFQydglNK++YuWr3mbvv/v////3Z1SObF01syI/ztdGWMnQKTiltm7\
125715i56nbb/7+h4CfL64f27pkclNhgr+dfUBiUfOUpduO33j5Cyr9/93dM7tXzewoTwt1MZbVcwhMKm6Zumz7iZsvfwMlP9w/t3fN7K7KjHA3UxVhJgYGDkkta\
125725/YvPoJCzcdvvIUTRIM2MQ1LL2ic2r75m84eGD9vN6a7ChPC3UxVqg0g4CikXNIaln7jJW7Tj+8sH/d3J7qrEgPczVRFqAkn7yBY1BySeu05TtO3nr95///\
12573b0+vHN60cEJ9XqyPtZYkA5r/v399cvnQxgX9dbkx3laaEuwoll96/AVFMwfYen4FpEC6cXzb0inNRYkB9nqyPJjOu4MUdLoy3EBZLmkdW7/4gsZJi7ccvfb\
125748x/8fz68d3bJ4UmNBvJ+tjjSXlLaNb1x+w8RFm49cffYdHLwoIahu4RmVXdM7b/2Bi48+Q4L//b2ze1bP6qxID3M1UUZ1PVAW3X9o/p/eDIl3TgZkAHVk65\
12575xNt46sm9FcnBTooC/HC5Jh5peGu/DEvfdfPr97+QRug4+jaUxGCSTyX71+/eb1+y8/voPBJ0g81RZm1EDi/dt/ZAB1ZGWyr5pNUAay9/5+fApPnhZKAtw8g\
12576mLS8BDcvP/04undVZkR7maqIsJAIMDFzgEGvHL6oHTY1DetCdV/ADmMewY="
12577
12578_palette_amiga7800mess :
12579  base642img \
12580"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM3MTQKeJxj1LONLG9ZsP3sjbsPXn349v//PyAEgxu7Z5aHOLoXLdh84smf/2jg7+\
12581fnV/ZvmFsV712/+cT9t7//QDVBNP9+/+T6qd2rpjame3nGpFZNmrts454j567euvfw2dkDG+bPmNBQmBrl76LKycbKo2DmFp6YW905ce7yTbsPX75weP3sy\
12582b0FYb6exrKycrKyTEyMTEDMKG2XWtY1e+PxCye2LWqrLQ7yM9eQ5GdhYBCTUdbQs3IPjC1s7Z2+dMfOTy9unzu0Y/W87sqcBA9XSx03n9DcCWsOXbz//N33\
12583vyD3AcGfn28fXti/dXVvYYQjI7r////88uzGhaOrJzZk+JtrKEjKqVoGZjX1zFx35Oixa0+e37p4aNOSGV2VeUl+ZvraKvLiUrKKqjZBCQX1E+at2nni1I7\
125841S+f21hWlR/na6kowMzIys/ApW7v6R6XmljVPmLNq2+XT+zatWjCpuTQjysfWQE2OQ8bQKTg2s7y1d8aS9TsOXnz78sn9OzeObVs5q60wxtNC0ye2oGXm6j\
125853n7zx8/urth88gB/5/8/jm2V1LJteXZod4OASnVfTMWLp+9/nHr7+CJP+8f3jtzP5N83vrC8OCXEyt7N0iynoWbth37OytZ59+/rxy+tjB7WsXTG4tTwm0U\
12586pWXQvf/mpkVYYG+NsCwF5cQYGNkYBGUU1RSkFfVtQiJzO1YtPrikc3zumszQ73ttVX4BXj5WHjFJWUsQxOT6/rnr9137t6bVy+ePbp17jDQA50lCd7muR0T\
12587l+07eenuiw8/QYGPlITe3rt6amlzZe/izUfO33rx/R807YAiCMh6c/P88TWTy2Oi8lvmbDt19ws0Xf6Dxb+vuTo4/hkYGBkZeCSV9Z3D4zOnTZ++ujInJNj\
12588TRl+GixEkycDJxcnJwa2gb+vqm1q75OiVGc2FCTGRfi5WBurywhyszABUd6ys"
12589
12590_palette_fornaxvoid1 :
12591  base642img \
12592"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1MjkKeJyrKi3ISU+OiwwN8HF3srM01Te3d/MLi03NLalp6Z48exEDO6+IlIK6ro\
12593mVg7tvSFTChwcXDmxYMKGhICHAwUBBgOH/p2e3zh3aunLuxNaKnIQQj8/Pb184snPtwmld9cXpMQGu/6HgwYEFDQlAHTeObJjbWZrkZ6UuzAAC/9/dPbN71\
12594cyO8rRQF2MlQQYGVk5eITEpOWV1bX0TCxsHRkYGFIChXkBez9YnOquqc8bybUevPP7/6enNM0AXNoDcp6EgsGzT3hOX77388v/Wtom5HioMjy8f2bpsekdl\
12595ZpS3ja4cP4Oqlr6xhY2jm5d/cERsYlrWvWvnju3btm7ZvGl9bXVleWlnDu/auGLB9L7WmpLspMgAjw8vH925eu7EwV1b1i5bMHNSD7r/+zqaasoKslLiIoJ\
1259683BysTG8cWjMN6HG4+9H9r62hpqKkICcjKS4qLMjPy43u//8fHl46vGXptI7KrGgfWz2FBXNnz5w+dfLEvp6ujraWpgYGTiFpFV0ze4/AqOSc0rq2A2tmth\
12597UnGDDgAuj+/48GXn/6icI/c+Hy9Vt3Hz558frdp68///zH8D+RABb//79/ePHw5qVTh3dvWTuxLNrh/7d3T+9eOXN496ZVC2dO6GhgEFQydglNK++YuWr3m\
12598bvv/qP7n6B6KHhx4QA4Cfy/v39+fby9PAOXsIyqnjlGeAIAmOWLbw=="
12599
12600# Command that tries to compress a smooth-enough palette.
12601compress_palette :
12602  N={w}
12603  do
12604    N-=1
12605    +r. $N,1,1,3,2 round.
12606    ri. ..,3 round.
12607    deltaE. ..
12608    dEavg,dEmax={[ia,iM]} rm.
12609    e[] "N = "$N", dEavg = "$dEavg", dEmax = "$dEmax
12610  while $dEavg<0.75" && "$dEmax<1.25
12611  N+=1
12612  r $N,1,1,3,2 round.
12613  e[] ""
12614  img2base64 0,0 rm
12615  str=\"${}\"
12616  ('$str') s. x,-119 ('\\\n') if $!>2 a[0--3] .,x fi rm.
12617  i[0] ('"  base642img \\\n"')
12618  ('"\n  r. 256,1,1,3,3 round.\n"')
12619  a x
12620  ot pal.txt
12621
12622#@cli pseudogray : _max_increment>=0,_JND_threshold>=0,_bits_depth>0
12623#@cli : Generate pseudogray colormap with specified increment and perceptual threshold.
12624#@cli : If 'JND_threshold' is 0, no perceptual constraints are applied.
12625#@cli : Default values: 'max_increment=5', 'JND_threshold=2.3' and 'bits_depth=8'.
12626#@cli : $ pseudogray 5
12627pseudogray : check "isint(${1=5}) && $1>=0 && ${2=2.3}>=0 && isint(${3=8}) && $3>0"
12628  e[^-1] "Generate pseudogray colormap with increment $1, JND threshold $2 and $3 bits depth."
12629
12630  # Generate all possible sRGB colors with given increments.
12631  {round(2^$3)},1,1,3,'x'
12632  if !$1 n. 0,255 return fi
12633  {$1+1},{$1+1},{$1+1},1,'x' +f. 'y' +f. 'z' a[-3--1] c r. {w*h*d},1,1,3,-1
12634  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)'
12635  permute. cxyz discard. -1 r. 3,{h/3},1,1,-1 permute. yzcx
12636  r.. {w*100}% ri. ..,0,2 +[-2,-1]
12637  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)'
12638  permute. cxyz discard. -1 r. 3,{h/3},1,1,-1 permute. yzcx
12639  n. 0,255
12640  +srgb2lab. rv[-2,-1] a[-2,-1] y sort. +,x  # Sort by increasing lightness.
12641  if !$2 rows. 1
12642  else # Add perceptual constraint if requested.
12643
12644    # Constraint 1 : keep colors close enough to equivalent 'pure' grays.
12645    s. y rv[-2,-1] . sh. 1,2 f. 0 rm. -[-2,-1] norm.
12646    <=. $2 *. 'x+1' discard. 0 -. 1 map. .. rm..
12647
12648    # Constraint 2 : remove neighboring colors that are above the JND.
12649    repeat 10000
12650      +srgb2lab. +shift. 0,{1-2*($>%2)},0,0,1 -[-2,-1] norm.
12651      <=. $2
12652      if im rm. break fi
12653      *. 'y+1' discard. 0 -. 1 map. .. rm..
12654    done
12655    transpose.
12656  fi
12657  nm. pseudogray$1
12658
12659#@cli replace_color : tolerance[%]>=0,smoothness[%]>=0,src1,src2,...,dest1,dest2,...
12660#@cli : Replace pixels from/to specified colors in selected images.
12661#@cli : $ image.jpg +replace_color 40,3,204,153,110,255,0,0
12662replace_color : check "$1>=0 && $2>=0"
12663  l[] (${3--1}) y c s c,2 col1={0,^} col2={1,^} rm endl
12664  e[^-1] "Replace color ("$col1") by color ("$col2") in image$?, with tolerance $1 and smoothness $2."
12665  repeat $! l[$>]
12666    1,1,1,100%,$col1 ri[1] [0]
12667    if $1 -[1] [0] norm[1] <=[1] $1 else ==[1] [0] l[1] s c & endl fi
12668    b[1] $2
12669    1,1,1,{0,s},$col2 ri[2] [0] j[0] [2],0,0,0,0,1,[1] k[0]
12670  endl done
12671
12672#@cli retinex : _value_offset>0,_colorspace={ hsi | hsv | lab | lrgb | rgb | ycbcr },\
12673# 0<=_min_cut<=100,0<=_max_cut<=100,_sigma_low>0,_sigma_mid>0,_sigma_high>0
12674#@cli : Apply multi-scale retinex algorithm on selected images to improve color consistency.
12675#@cli : (as described in the page <http://www.ipol.im/pub/art/2014/107/>).
12676#@cli : Default values: 'offset=1', 'colorspace=hsv', 'min_cut=1', 'max_cut=1', 'sigma_low=15','sigma_mid=80' \
12677# and 'sigma_high=250'.
12678retinex : check "${1=5}>0 && ${3=1}>=0 && $3<=100 && ${4=1}>=0 && $4<=100 && ${5=15}>0 && ${6=80}>0 && ${7=250}>0"
12679  skip "${2=hsv}"
12680  e[^-1] "Apply Retinex color consistency algorithm on image$?, with value offset $1, colorspace '$2', cuts ($3,$4)
12681          and sigmas (${5-7})."
12682  if '$2'=='hsi' mode=hsi_i
12683  elif '$2'=='hsv' mode=hsv_v
12684  elif '$2'=='lab' mode=lab_l
12685  elif '$2'=='rgb' mode=rgb
12686  elif '$2'=='lrgb' mode=lrgb
12687  elif '$2'=='ycbcr' mode=ycbcr_y
12688  else error[0--2] "Command '$0': Invalid colorspace argument '$2'."
12689  fi
12690  ac "_retinex $1,${3--1}",$mode
12691
12692_retinex :
12693  - {im-$1} {[w,h,d,s]},1
12694  repeat 3 +b[0] {arg(1+$>,${4-6})} +/[0,-1] rm.. *[1,-1] done
12695  rm[0] log c $2%,{100-$3}% n 0,255
12696
12697#@cli rgb2bayer : _start_pattern=0,_color_grid=0
12698#@cli : Transform selected color images to RGB-Bayer sampled images.
12699#@cli : Default values: 'start_pattern=0' and 'color_grid=0'.
12700#@cli : $ image.jpg +rgb2bayer 0
12701rgb2bayer : skip ${1=0},${2=0}
12702  e[^-1] "Transform image$? to a RGB-Bayer "${arg\ 1+!$2,color,monochrome}" grid, starting from pattern '$1'."
12703  to_rgb repeat $! l[$>]
12704    _rgb2bayer$1 ri[1] [0],0,2 * if !$2 s c + fi
12705  endl done
12706
12707_rgb2bayer0 : (1,0;0,0^0,1;1,0^0,0;0,1)
12708_rgb2bayer1 : (0,0;0,1^0,1;1,0^1,0;0,0)
12709_rgb2bayer2 : (0,1;0,0^1,0;0,1^0,0;1,0)
12710_rgb2bayer3 : (0,0;1,0^1,0;0,1^0,1;0,0)
12711
12712#@cli rgb2cmy
12713#@cli : Convert color representation of selected images from RGB to CMY.
12714#@cli : $ image.jpg rgb2cmy split c
12715rgb2cmy :
12716  e[^-1] "Convert color representation of image$? from RGB to CMY."
12717  to_rgb * -1 + 255 c 0,255
12718
12719#@cli rgb2cmyk
12720#@cli : Convert color representation of selected images from RGB to CMYK.
12721#@cli : $ image.jpg rgb2cmyk split c
12722#@cli : $ image.jpg rgb2cmyk split c fill[3] 0 append c cmyk2rgb
12723rgb2cmyk :
12724  e[^-1] "Convert color representation of image$? from RGB to CMYK."
12725  rgb2cmy repeat $! l[$>]
12726    s c +min -[0-2] . +/. 255 -. 1 *. -1 +==. 0 +[-2,-1]
12727    /[0-2] . rm. a c
12728  endl done
12729
12730#@cli rgb2hcy
12731#@cli : Convert color representation of selected images from RGB to HCY.
12732#@cli : $ image.jpg rgb2hcy split c
12733rgb2hcy :
12734  e[^-1] "Convert color representation of image$? from RGB to HCY."
12735  to_color f "
12736    M = max(R,G,B);
12737    C = M - min(R,G,B);
12738    H = 60*(C==0?0:M==R?((G-B)/C)%6:M==G?(B-R)/C+2:(R-G)/C+4);
12739    Y = 0.299*R + 0.587*G + 0.114*B;
12740    [ H,C/255,Y/255 ]"
12741
12742#@cli rgb2hsi
12743#@cli : Convert color representation of selected images from RGB to HSI.
12744#@cli : $ image.jpg rgb2hsi split c
12745rgb2hsi :
12746  e[^-1] "Convert color representation of image$? from RGB to HSI."
12747  to_color
12748  f "
12749    m = min(R,G,B);
12750    M = max(R,G,B);
12751    C = M - m;
12752    sum = R + G + B;
12753    H = 60*(C==0?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4);
12754    S = sum<=0?0:1 - 3*m/sum;
12755    I = sum/(3*255);
12756    [ H, S, I ]"
12757
12758#@cli rgb2hsi8
12759#@cli : Convert color representation of selected images from RGB to HSI8.
12760#@cli : $ image.jpg rgb2hsi8 split c
12761rgb2hsi8 :
12762  e[^-1] "Convert color representation of image$? from RGB to HSI8."
12763  rgb2hsi _rgb2hsx8
12764
12765#@cli rgb2hsl
12766#@cli : Convert color representation of selected images from RGB to HSL.
12767#@cli : $ image.jpg rgb2hsl split c
12768#@cli : $ image.jpg rgb2hsl +split c add[-3] 100 mod[-3] 360 append[-3--1] c hsl2rgb
12769rgb2hsl :
12770  e[^-1] "Convert color representation of image$? from RGB to HSL."
12771  to_color
12772  f "
12773    m = min(R,G,B);
12774    M = max(R,G,B);
12775    C = M - m;
12776    H = 60*(C==0?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4);
12777    L = 0.5*(m + M)/255;
12778    S = L==1 || L==0?0:C/(1 - abs(2*L - 1))/255;
12779    [ H, S, L ]"
12780
12781#@cli rgb2hsl8
12782#@cli : Convert color representation of selected images from RGB to HSL8.
12783#@cli : $ image.jpg rgb2hsl8 split c
12784rgb2hsl8 :
12785  e[^-1] "Convert color representation of image$? from RGB to HSL8."
12786  rgb2hsl _rgb2hsx8
12787
12788#@cli rgb2hsv
12789#@cli : Convert color representation of selected images from RGB to HSV.
12790#@cli : $ image.jpg rgb2hsv split c
12791#@cli : $ image.jpg rgb2hsv +split c add[-2] 0.3 cut[-2] 0,1 append[-3--1] c hsv2rgb
12792rgb2hsv :
12793  e[^-1] "Convert color representation of image$? from RGB to HSV."
12794  to_color
12795  f "
12796    M = max(R,G,B);
12797    C = M - min(R,G,B);
12798    H = 60*(C==0?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4);
12799    S = M<=0?0:C/M;
12800    [ H, S, M/255 ]"
12801
12802#@cli rgb2hsv8
12803#@cli : Convert color representation of selected images from RGB to HSV8.
12804#@cli : $ image.jpg rgb2hsv8 split c
12805rgb2hsv8 :
12806  e[^-1] "Convert color representation of image$? from RGB to HSV8."
12807  rgb2hsv _rgb2hsx8
12808
12809_rgb2hsx8 :
12810  repeat $!
12811    sh[$>] 0 *. 0.708333 rm.
12812    sh[$>] 1,2 *. 255 rm.
12813  done
12814
12815#@cli rgb2int
12816#@cli : Convert color representation of selected images from RGB to INT24 scalars.
12817#@cli : $ image.jpg rgb2int
12818rgb2int :
12819  e[^-1] "Convert color representation of image$? from RGB to INT24 scalars."
12820  to_rgb round repeat $! l[$>]
12821    s c <<[0] 16 <<[1] 8 +
12822  endl done
12823
12824#@cli rgb2jzazbz : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12825#@cli : Convert color representation of selected images from RGB to Jzazbz.
12826#@cli : Default value: 'illuminant=2'.
12827rgb2jzazbz : skip "${1=,}"
12828  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
12829  e[^-1] "Convert color representation of image$? from RGB to Jzazbz, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12830  rgb2xyz $illu xyz2jzazbz
12831
12832#@cli rgb2lab : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12833#@cli : Convert color representation of selected images from RGB to Lab.
12834#@cli : Default value: 'illuminant=2'.
12835rgb2lab : skip "${1=,}"
12836  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
12837  e[^-1] "Convert color representation of image$? from RGB to Lab, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12838  rgb2xyz $illu xyz2lab $illu
12839
12840#@cli rgb2lab8 : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12841#@cli : Convert color representation of selected images from RGB to Lab8.
12842#@cli : Default value: 'illuminant=2'.
12843#@cli : $ image.jpg rgb2lab8 split c
12844rgb2lab8 : skip "${1=,}"
12845  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
12846  e[^-1] "Convert color representation of image$? from RGB to Lab8, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12847  rgb2lab $illu repeat $!
12848    sh[$>] 0 *. 2.55 rm.
12849    sh[$>] 1 +. 127 *. 0.85 rm.
12850    sh[$>] 2 +. 149 *. 0.836 rm.
12851  done c 0,255
12852
12853#@cli rgb2lch : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12854#@cli : Convert color representation of selected images from RGB to Lch.
12855#@cli : Default value: 'illuminant=2'.
12856#@cli : $ image.jpg rgb2lch split c
12857rgb2lch : skip "${1=,}"
12858  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
12859  e[^-1] "Convert color representation of image$? from RGB to Lch, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12860  rgb2lab $illu lab2lch
12861
12862#@cli rgb2lch8 : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12863#@cli : Convert color representation of selected images from RGB to Lch8.
12864#@cli : Default value: 'illuminant=2'.
12865#@cli : $ image.jpg rgb2lch8 split c
12866rgb2lch8 : skip "${1=,}"
12867  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
12868  e[^-1] "Convert color representation of image$? from RGB to Lch8, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12869  pi,facth={[pi,255/(2*pi)]}
12870  rgb2lch $illu repeat $!
12871    sh[$>] 0 *. 2.55 rm.
12872    sh[$>] 1 *. 1.1086 rm.
12873    sh[$>] 2 +. $pi *. $facth rm.
12874  done c 0,255
12875
12876#@cli rgb2luv
12877#@cli : Convert color representation of selected images from RGB to LUV.
12878#@cli : $ image.jpg rgb2luv split c
12879rgb2luv :
12880  e[^-1] "Convert color representation of image$? from RGB to LUV."
12881  repeat $! l[$>]
12882    +rgb2xyz rgb2lab.. channels.. 0 s. c
12883    *. 3 +*.. 15 +[-2,-1] +. ... +. 1e-8  # Z <- X+15Y+3Z
12884    *... 4 *.. 9 /[-3,-2] . rm.
12885    -.. 0.2009 -. 0.4610
12886    +*... 13 *... . *[-2,-1] a c
12887  endl done
12888
12889#@cli rgb2oklab
12890#@cli : Convert color representation of selected images from RGB to Oklab.
12891#@cli : (see colorspace definition at: <https://bottosson.github.io/posts/oklab/> ).
12892#@cli : See also: ''oklab2rgb''.
12893rgb2oklab :
12894  e[^-1] "Convert color representation of image$? from RGB to Oklab."
12895  repeat $! l[$>] split_opacity to_rgb[0] /[0] 255
12896    f[0] "
12897      l = cbrt(0.4121656120*R + 0.5362752080*G + 0.0514575653*B);
12898      m = cbrt(0.2118591070*R + 0.6807189584*G + 0.1074065790*B);
12899      s = cbrt(0.0883097947*R + 0.2818474174*G + 0.6302613616*B);
12900      [ 0.2104542553*l + 0.7936177850*m - 0.0040720468*s,
12901        1.9779984951*l - 2.4285922050*m + 0.4505937099*s,
12902        0.0259040371*l + 0.7827717662*m - 0.8086757660*s ]"
12903  a c endl done
12904
12905#@cli rgb2ryb
12906#@cli : Convert color representation of selected images from RGB to RYB.
12907#@cli : $ image.jpg rgb2ryb split c
12908rgb2ryb :
12909  e[^-1] "Convert color representation of image$? from RGB to RYB."
12910  to_color
12911  f "red = R;
12912     green = G;
12913     blue = B;
12914     white = min(red,green,blue);
12915     red-=white;
12916     green-=white;
12917     blue-=white;
12918     maxgreen = max(red,green,blue);
12919     yellow = min(red,green);
12920     red-=yellow;
12921     green-=yellow;
12922     blue>0 && green>0?(blue/=2; green/=2);
12923     yellow+=green;
12924     blue+=green;
12925     maxyellow = max(red,yellow,blue);
12926     maxyellow>0?(
12927       N = maxgreen/maxyellow;
12928       red*=N;
12929       yellow*=N;
12930       blue*=N;
12931     );
12932     red+=white;
12933     yellow+=white;
12934     blue+=white;
12935     [ red,yellow,blue ]"
12936
12937#@cli rgb2srgb
12938#@cli : Convert color representation of selected images from linear RGB to sRGB.
12939rgb2srgb :
12940  e[^-1] "Convert color representation of image$? from linear RGB to sRGB."
12941  f "val = i/255; sval = val<=0.0031308?val*12.92:1.055*val^0.416667 - 0.055; cut(255*sval,0,255)"
12942
12943#@cli rgb2xyz : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12944#@cli : Convert color representation of selected images from RGB to XYZ.
12945#@cli : Default value: 'illuminant=2'.
12946#@cli : $ image.jpg rgb2xyz split c
12947rgb2xyz : skip "${1=,}"
12948  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
12949  e[^-1] "Convert color representation of image$? from RGB to XYZ, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12950  if $illu==2 # E
12951     mix_rgb {[0.488718,0.3106803,0.2006017,\
12952               0.1762044,0.8129847,0.0108109,\
12953               0,0.0102048,0.9897952]/255}
12954  elif $illu==1 # D65
12955    mix_rgb {[0.4124564,0.3575761,0.1804375,\
12956              0.2126729,0.7151522,0.0721750,\
12957              0.0193339,0.1191920,0.9503041]/255}
12958  else # D50
12959    mix_rgb {[0.43603516,0.38511658,0.14305115,\
12960              0.22248840,0.71690369,0.06060791,\
12961              0.01391602,0.09706116,0.71392822]/255}
12962  fi
12963
12964#@cli rgb2xyz8 : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
12965#@cli : Convert color representation of selected images from RGB to XYZ8.
12966#@cli : Default value: 'illuminant=2'.
12967#@cli : $ image.jpg rgb2xyz8 split c
12968rgb2xyz8 : skip "${1=,}"
12969  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
12970  e[^-1] "Convert color representation of image$? from RGB to XYZ8, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
12971  rgb2xyz $illu repeat $!
12972    sh[$>] 0 *. 255 rm.
12973    sh[$>] 1 *. 255 rm.
12974    sh[$>] 2 *. 231.8182 rm.
12975  done
12976
12977#@cli rgb2yiq
12978#@cli : Convert color representation of selected images from RGB to YIQ.
12979#@cli : $ image.jpg rgb2yiq split c
12980rgb2yiq :
12981  e[^-1] "Convert color representation of image$? from RGB to YIQ."
12982  mix_rgb 0.299,0.587,0.114,\
12983          0.595716,-0.274453,-0.321263,\
12984          0.211456,-0.522591,0.311135
12985
12986#@cli rgb2yiq8
12987#@cli : Convert color representation of selected images from RGB to YIQ8.
12988#@cli : $ image.jpg rgb2yiq8 split c
12989rgb2yiq8 :
12990  e[^-1] "Convert color representation of image$? from RGB to YIQ."
12991  rgb2yiq
12992  repeat $!
12993    sh[$>] 1 +. 151.908 *. 0.8393238012481239 rm.
12994    sh[$>] 2 +. 133.261 *. 0.9567690472081104 rm.
12995  done
12996
12997#@cli rgb2ycbcr
12998#@cli : Convert color representation of selected images from RGB to YCbCr.
12999#@cli : $ image.jpg rgb2ycbcr split c
13000rgb2ycbcr :
13001  e[^-1] "Convert color representation of image$? from RGB to YCbCr."
13002  mix_rgb 66,129,25,-38,-74,112,112,-94,-18
13003  repeat $!
13004    sh[$>] 0,2 +. 128 /. 256 rm.
13005    sh[$>] 0 +. 16 rm.
13006    sh[$>] 1,2 +. 128 rm.
13007  done
13008
13009#@cli rgb2yuv
13010#@cli : Convert color representation of selected images from RGB to YUV.
13011#@cli : $ image.jpg rgb2yuv split c
13012rgb2yuv :
13013  e[^-1] "Convert color representation of image$? from RGB to YUV."
13014  mix_rgb {[0.299,0.587,0.114,\
13015            -0.14713,-0.28886,0.436,\
13016            0.615,-0.51498,-0.10001]/255}
13017
13018#@cli rgb2yuv8
13019#@cli : Convert color representation of selected images from RGB to YUV8.
13020#@cli : $ image.jpg rgb2yuv8 split c
13021rgb2yuv8 :
13022  e[^-1] "Convert color representation of image$? from RGB to YUV8."
13023  rgb2yuv repeat $!
13024    sh[$>] 0 *. 255 rm.
13025    sh[$>] 1 +. 0.44 *. 289.773 rm.
13026    sh[$>] 2 +. 0.62 *. 205.645 rm.
13027  done
13028
13029#@cli remove_opacity
13030#@cli : Remove opacity channel of selected images.
13031remove_opacity :
13032  e[^-1] "Remove opacity channel of image$?."
13033  repeat $! l[$>]
13034    if s==2 channels 0
13035    elif s==4 channels 0,2
13036    fi
13037  nm {n} endl done
13038
13039#@cli ryb2rgb
13040#@cli : Convert color representation of selected images from RYB to RGB.
13041ryb2rgb :
13042  e[^-1] "Convert color representation of image$? from RYB to RGB."
13043  to_color
13044  f "red = R;
13045     yellow = G;
13046     blue = B;
13047     white = min(red,yellow,blue);
13048     red-=white;
13049     yellow-=white;
13050     blue-=white;
13051     maxyellow = max(red,yellow,blue);
13052     green = min(yellow,blue);
13053     yellow-=green;
13054     blue-=green;
13055     blue>0 && green>0?(blue*=2; green*=2);
13056     red+=yellow;
13057     green+=yellow;
13058     maxgreen = max(red,green,blue);
13059     maxgreen>0?(
13060       N = maxyellow/maxgreen;
13061       red*=N;
13062       green*=N;
13063       blue*=N;
13064     );
13065     red+=white;
13066     green+=white;
13067     blue+=white;
13068     [ red,green,blue ]"
13069
13070#@cli select_color : tolerance[%]>=0,col1,...,colN
13071#@cli : Select pixels with specified color in selected images.
13072#@cli : $ image.jpg +select_color 40,204,153,110
13073#@cli : $$ https://gmic.eu/oldtutorial/_select_color
13074select_color : skip ${1=0}
13075  e[^-1] "Select color (${2--1}) in image$?, with tolerance $1."
13076  repeat $! l[$>]
13077    +fc ${2--1} - norm <= $1
13078  endl done
13079
13080#@cli sepia
13081#@cli : Apply sepia tones effect on selected images.
13082#@cli : $ image.jpg sepia
13083sepia :
13084  e[^-1] "Apply sepia tones effect on image$?."
13085  (0,44,115,143,196,244^0,20,84,119,184,235^0,5,44,73,144,200) r. 256,1,1,3,3
13086  repeat $!-1 l[$>,-1] split_opacity luminance[0] map[0] . a[^-1] c endl done
13087  rm.
13088
13089#@cli solarize
13090#@cli : Solarize selected images.
13091#@cli : $ image.jpg solarize
13092solarize :
13093  e[^-1] "Solarize image$?."
13094  luminance n 0,128 map 1
13095
13096#@cli split_colors : _tolerance>=0,_max_nb_outputs>0,_min_area>0
13097#@cli : Split selected images as several image containing a single color.
13098#@cli : One selected image can be split as at most 'max_nb_outputs' images.
13099#@cli : Output images are sorted by decreasing area of extracted color regions and have an additional alpha-channel.
13100#@cli : Default values: 'tolerance=0', 'max_nb_outputs=256' and 'min_area=8'.
13101#@cli : $ image.jpg quantize 5 +split_colors , display_rgba
13102split_colors : check "${1=0}>=0 && isint(${2=256}) && $2>0 && ${3=8}>=1"
13103  e[^-1] "Split image$? as single color outputs, with tolerance $1, $2 maximal outputs and minimal color area $3."
13104  repeat $! l[$>]
13105    +label 0,1 norm. area. 0,1
13106
13107    # Loop over biggest color regions.
13108    repeat $2-1
13109      coordsM={1,[xM,yM,zM,cM]}
13110      if {1,i($coordsM)<$3} break fi
13111      color={0,I($coordsM)}
13112      +select_color[0] $1,$color
13113      ==. 0 *[1] . ==. 0
13114    done
13115    if iM#1 !=[1] 0 mv[1] $! # Residual mask, if any.
13116    else rm[1] fi
13117    r[^0] [0],[0],[0],{0,s+1}
13118    N={$!-1}
13119    sh[^0] 0,{s-2} *[-$N--1] [0] rm[-$N--1]
13120    sh[^0] 100% *[-$N--1] 255 rm[0,-$N--1]
13121
13122  endl done
13123
13124#@cli split_opacity
13125#@cli : Split color and opacity parts of selected images.
13126split_opacity :
13127  e[^-1] "Split color and opacity parts of image$?."
13128  repeat $! l[$<] s c,{s==4?-3:s==2?-1:-s} endl done
13129
13130#@cli srgb2lab : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
13131#@cli : Convert color representation of selected images from sRGB to Lab.
13132#@cli : Default value: 'illuminant=2'.
13133#@cli : $ image.jpg srgb2lab split c
13134#@cli : $ image.jpg srgb2lab +split c mul[-2,-1] 2.5 append[-3--1] c lab2srgb
13135srgb2lab : skip "${1=,}"
13136  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
13137  e[^-1] "Convert color representation of image$? from sRGB to Lab, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
13138  srgb2rgb rgb2lab $illu
13139
13140#@cli srgb2lab8 : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
13141#@cli : Convert color representation of selected images from sRGB to Lab8.
13142#@cli : Default value: 'illuminant=2'.
13143srgb2lab8 : skip "${1=,}"
13144  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
13145  e[^-1] "Convert color representation of image$? from sRGB to Lab8, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
13146  srgb2rgb rgb2lab8 $illu
13147
13148#@cli srgb2rgb
13149#@cli : Convert color representation of selected images from sRGB to linear RGB.
13150srgb2rgb :
13151  e[^-1] "Convert color representation of image$? from sRGB to linear RGB."
13152  f "sval = i/255; val = sval<=0.04045?sval/12.92:((sval + 0.055)/(1.055))^2.4; cut(255*val,0,255)"
13153
13154#@cli to_a
13155#@cli : Force selected images to have an alpha channel.
13156to_a :
13157  e[^-1] "Force image$? to have an alpha channel."
13158  repeat $! l[$>]
13159    if s==1||s==3 channels 0,{s} sh. {s-1} f. 255 rm. fi
13160  endl done
13161
13162#@cli to_color
13163#@cli : Force selected images to be in color mode (RGB or RGBA).
13164to_color :
13165  e[^-1] "Force image$? to be in color mode."
13166  repeat $! l[$>]
13167    if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
13168    elif s==2 r 100%,100%,1,4,0,1,0,0,0,1
13169    elif s==1 r 100%,100%,1,3,1
13170    fi
13171  endl done
13172
13173#@cli to_colormode : mode={ 0=adaptive | 1=G | 2=GA | 3=RGB | 4=RGBA }
13174#@cli : Force selected images to be in a given color mode.
13175#@cli : Default value: 'mode=0'.
13176to_colormode : skip ${1=0}
13177  if $1==1 to_gray
13178  elif $1==2 to_graya
13179  elif $1==3 to_rgb
13180  elif $1==4 to_rgba
13181  else
13182    is_rgb=0 is_alpha=0
13183    repeat $!
13184      if {$>,!s||s>4} error "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
13185      elif {$>,s==2} is_alpha=1
13186      elif {$>,s==3} is_rgb=1
13187      elif {$>,s==4} is_rgb=1 is_alpha=1
13188      fi
13189    done
13190    to_colormode {if($is_rgb,3,1)+$is_alpha}
13191  fi
13192
13193#@cli to_gray
13194#@cli : Force selected images to be in GRAY mode.
13195#@cli : $ image.jpg +to_gray
13196to_gray :
13197  e[^-1] "Force image$? to be in GRAY mode."
13198  repeat $! l[$>]
13199    if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
13200    elif s>=3 channels 0,2 luminance
13201    elif s==2 r 100%,100%,100%,1,0
13202    fi
13203  endl done
13204
13205#@cli to_graya
13206#@cli : Force selected images to be in GRAYA mode.
13207to_graya :
13208  e[^-1] "Force image$? to be in GRAYA mode."
13209  repeat $! l[$>]
13210    if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
13211    elif s==4 +channels 3 channels.. 0,2 luminance.. a c
13212    elif s==3 luminance channels 0,1 sh. 1 f. 255 rm.
13213    elif s==1 channels 0,1 sh. 1 f. 255 rm.
13214    fi
13215  endl done
13216
13217#@cli to_pseudogray : _max_step>=0,_is_perceptual_constraint={ 0 | 1 },_bits_depth>0
13218#@cli : Convert selected scalar images ([0-255]-valued) to pseudo-gray color images.
13219#@cli : Default values: 'max_step=5', 'is_perceptual_constraint=1' and 'bits_depth=8'.
13220#@cli : The original pseudo-gray technique has been introduced by Rich Franzen <http://r0k.us/graphics/pseudoGrey.html>.
13221#@cli : Extension of this technique to arbitrary increments for more tones, has been done by David Tschumperlé.
13222to_pseudogray : check "isint(${1=5}) && $1>=0 && isint(${3=8}) && $3>0" skip ${2=1}
13223  e[^-1] "Convert scalar image$? to pseudo-gray color images, with steps $1."
13224  channels 0 srgb2rgb pseudogray $1,{2.3*$2},$3
13225
13226  # Compute colormap with 65336 entries, to have match corresponding lightness.
13227  +srgb2lab. channels. 0 *. {65535/100} round. rows. 0,2
13228  rv[-2,-1] permute. xcyz +. 1 a[-2,-1] y pointcloud. 0
13229  +norm. !=. 0 distance. 1 *. -1 watershed.. . rm. -. 1
13230
13231  # Map colormap to images, with lightness preservation.
13232  repeat $!-1
13233    to_rgb[$>] rgb2lab[$>] channels[$>] 0 *[$>] {65535/100} round[$>] c[$>] 0,65535
13234    map[$>] .
13235  done rm.
13236
13237#@cli to_rgb
13238#@cli : Force selected images to be in RGB mode.
13239to_rgb :
13240   e[^-1] "Force image$? to be in RGB mode."
13241   repeat $! l[$>]
13242     if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
13243     elif s==4 channels 0,2
13244     elif s==2 channels 0,0 r 100%,100%,100%,3
13245     elif s==1 r 100%,100%,100%,3
13246     fi
13247   endl done
13248
13249#@cli to_rgba
13250#@cli : Force selected images to be in RGBA mode.
13251to_rgba :
13252   e[^-1] "Force image$? to be in RGBA mode."
13253   repeat $! l[$>]
13254     if s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)."
13255     elif s==3 channels 0,3 sh. 3 f. 255 rm.
13256     elif s==2 r 100%,100%,100%,4 sh. 2 f. .. rm.
13257     elif s==1 r 100%,100%,100%,4 sh. 3 f. 255 rm.
13258     fi
13259   endl done
13260
13261#@cli transfer_histogram : [reference_image],_nb_levels>0,_color_channels
13262#@cli : Transfer histogram of the specified reference image to selected images.
13263#@cli : Argument 'color channels' is the same as with command 'apply_channels'.
13264#@cli : Default value: 'nb_levels=256' and 'color_channels=all'.
13265#@cli : $ image.jpg 100,100,1,3,"u([256,200,100])" +transfer_histogram[0] [1]
13266transfer_histogram : check ${"is_image_arg $1"}" && ${2=1024}>0" skip "${3=0}"
13267  channels=${"_ac_list \"$3\""}
13268  e[^-1] "Transfer histogram from image ["${"pass$1 -1"}"] to image$?, "\
13269         "with $2 levels, for channels '"$channels"'."
13270  pass$1 1 sref={s} rm.
13271  to_colormode $sref
13272  if ['$channels']!='all'
13273    pass$1 {$sref==3?2:0}
13274    to_color
13275    ac. "+store _transfer_histogram_reference",$channels rm.
13276    ac "_transfer_histogram $2",$channels,1
13277  else
13278    pass$1
13279    store. _transfer_histogram_reference
13280    repeat $! _transfer_histogram[$>] $2 done
13281  fi
13282  _transfer_histogram_reference=
13283
13284_transfer_histogram :
13285  $_transfer_histogram_reference
13286  repeat min(s#0,s#1)
13287    sh $>
13288    +histogram[-2,-1] $1 cumulate[-2,-1] /.. {-2,i[-1,2]} /. {i[-1,2]}
13289    f.. "*
13290      const w1 = w -1;
13291      val = i; X = x;
13292      val<i[#-1,X]?(
13293        step = int(X/2);
13294        while (X && step>=1, nX = max(0,X - step); val<i[#-1,nX]?(X = nX):(step = int(step/2)));
13295        X?(vc = i[#-1,X]; vp = i[#-1,X - 1]; (vc - val)>(val - vp)?--X); # Rounding
13296      ):(
13297        step = int((w1 - X)/2);
13298        while (X<w1 && step>=1, nX = min(w1,X + step); val>i[#-1,nX]?(X = nX):(step = int(step/2)));
13299        X<w1?(vc = i[#-1,X]; vn = i[#-1,X + 1]; (val - vc)>(vn - val)?++X); # Rounding
13300      );
13301      im#-3 + (iM#-3 - im#-3)*X/w1"
13302    n[-4] 0,{w-1} round[-4] map[-4] ..
13303    k[0,1]
13304  done
13305  rm.
13306
13307#@cli transfer_pca : [reference_image],_color_channels
13308#@cli : Transfer mean and covariance matrix of specified vector-valued reference image to selected images.
13309#@cli : Argument 'color channels' is the same as with command 'apply_channels'.
13310#@cli : Default value: 'color_channels=all'.
13311#@cli : $ sample lena,earth +transfer_pca[0] [1]
13312transfer_pca : check ${"is_image_arg $1"} skip "${2=all}"
13313  channels=${"_ac_list \"$2\""}
13314  e[^-1] "Transfer mean vector and covariance matrix from image ["${"pass$1 -1"}"] to image$?, "\
13315         "for channels '"$channels"'."
13316  pass$1 1 sref={s} rm.
13317  to_colormode[^-1] $sref
13318  if $sref==1 # Scalar case can to be solved more easily
13319    pass$1
13320    var_ref,avg_ref={[iv,ia]} rm.
13321    repeat $! l[$>] - {ia} * {sqrt($var_ref/iv)} + $avg_ref endl done
13322  elif ['$channels']!='all'
13323    pass$1 {$sref==3?2:0}
13324    to_color
13325    ac. "+store _transfer_pca_reference",$channels rm.
13326    ac "_transfer_pca",$channels,1
13327  else
13328    pass$1
13329    store. _transfer_pca_reference
13330    repeat $! _transfer_pca[$>] done
13331  fi
13332  _transfer_pca_reference=
13333
13334_transfer_pca :
13335  $_transfer_pca_reference
13336  f.. "*begin(
13337         cov = [ "${"covariance_colors[0] _avg"}" ];
13338         avg = [ "$_avg" ];
13339         eig = eig(cov);
13340         lambda = 1/sqrt(1e-6 + eig[0,s]);
13341         rot = mul(diag(lambda),eig[s,s*s],s);
13342
13343         cov_ref = [ "${"covariance_colors[1] _avg_ref"}" ];
13344         avg_ref = [ "$_avg_ref" ];
13345         eig_ref = eig(cov_ref);
13346         lambda_ref = sqrt(eig_ref[0,s]);
13347         rot_ref = mul(transpose(eig_ref[s,s*s],s),diag(lambda_ref),s);
13348
13349         M = mul(rot_ref,rot,s);
13350       );
13351       avg_ref + M*(I - avg)"
13352  rm.
13353
13354#@cli transfer_rgb : [target],_gamma>=0,_regularization>=0,_luminosity_constraints>=0,_rgb_resolution>=0,\
13355# _is_constraints={ 0 | 1 }
13356#@cli : Transfer colors from selected source images to selected reference image (given as argument).
13357#@cli : 'gamma' determines the importance of color occurrences in the matching process (0=none to 1=huge).
13358#@cli : 'regularization' determines the number of guided filter iterations to remove quantization effects.
13359#@cli : 'luminosity_constraints' tells if luminosity constraints must be applied on non-confident matched colors.
13360#@cli : 'is_constraints' tells if additional hard color constraints must be set (opens an interactive window).
13361#@cli : Default values: 'gamma=0.3','regularization=8', 'luminosity_constraints=0.1', 'rgb_resolution=64' and \
13362# 'is_constraints=0'.
13363#@cli : $ sample pencils,wall +transfer_rgb[0] [1],0,0.01
13364transfer_rgb : check "${2=0.3}>=0 && ${3=8}>=0 && ${4=0.15}>=0 && ${5=64}>=0 && isint(${6=0})"
13365  e[^-1] "Transfer colors of image $1 to image$?."
13366  sigma=1.5
13367  repeat $! pass$1 0 l[$>,-1]
13368    nm_source={0,b} nm_target={1,b}
13369    nm[0] source nm[1] target
13370
13371    # Generate matching functions from 3D RGB features.
13372    +_transfer_rgb[source,target] $2,$sigma,$5
13373    nm.. fsource nm. ftarget
13374    n[fsource,ftarget] 0,255
13375
13376    # Manage color constraints.
13377    if $6
13378      h0={2*{*,v}/3} ws0={source,max(1,w*$h0/h)} wt0={target,max(1,w*$h0/h)}
13379      w1={2*{*,u}/3} hs1={source,max(1,h*$w1/w)} ht1={target,max(1,h*$w1/w)}
13380      if abs($ws0+$wt0-$w1)<abs($hs1+$ht1-$h0) # Append along X.
13381        +r2dy[source,target] $h0 +b[-2,-1] 0.5% a[-4,-3] x a[-2,-1] x
13382      else # Append along Y.
13383        +r2dx[source,target] $w1 +b[-2,-1] 0.5% a[-4,-3] y a[-2,-1] y
13384      fi
13385      if w>$w1 r2dx[-2,-1] $w1 fi
13386      if h>$h0 r2dy[-2,-1] $h0 fi
13387      nm.. visu nm. both
13388
13389      w[visu] -1,-1
13390      N=0 do
13391        w[] -1,-1,"[G'MIC] Add Color Guide (Constraint ""#"{1+$N}")"
13392        +select[$visu] 1 if i==-1 rm. break fi
13393        line[$visu] {i[0]},{i[1]},{i[3]},{i[4]},1,0xF0F0F0F0,0
13394        line[$visu] {i[0]},{i[1]},{i[3]},{i[4]},1,0x0F0F0F0F,255
13395        circle[$visu] {i[0]},{i[1]},5,1,0,0,0 circle[$visu] {i[0]},{i[1]},3,1,255,0,0
13396        circle[$visu] {i[3]},{i[4]},5,1,0,0,0 circle[$visu] {i[3]},{i[4]},3,1,0,255,0
13397        s. y,2 rows[-2,-1] 0,1 a[-2,-1] x permute. xczy
13398        +warp[$both] .,0,0 rm..
13399        *. {($5-1)/255} s. x,2
13400        -. .. *. -1 a[-2,-1] c
13401        N+=1
13402      while {*}
13403      if $N a[-$N--1] x permute. xczy nm. constraints fi
13404      rm[$visu,$both] w 0
13405    fi
13406
13407    # Estimate warping field.
13408    if $constraints
13409      +pointcloud. 0 r. ...,...,...,3,0 +compose_channels. + a[-2,-1] c
13410      displacement[fsource] [ftarget],0.001,5,0,10000,1,. rm[ftarget,constraints,-1]
13411    else
13412      displacement[fsource] [ftarget],0.005 rm[ftarget]
13413    fi
13414    nm[fsource] displacement
13415
13416    # Generate video of matching.
13417    # if videos
13418    #   N=$!
13419    #   [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1
13420    #   +_transfer_rgb[source] 0,$sigma,{w} *.. . distance. 1 *. -1 watershed.. . rm.
13421    #   +_transfer_rgb[source] 0,$sigma,{w} a[-2,-1] c
13422
13423    #   [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1
13424    #   +_transfer_rgb[target] 0,$sigma,{w} *.. . distance. 1 *. -1 watershed.. . rm.
13425    #   +_transfer_rgb[target] 0,$sigma,{w} a[-2,-1] c
13426    #   warp. [displacement],1,1,1,48
13427
13428    #   l[$N--1]
13429    #     f3d 1200
13430    #     ap "s. c,-3 view_func. 6,.. rm.."
13431    #     ap "makefig"
13432    #     append[^0] [0],x rm[0]
13433    #     r2dx 1024
13434    #     [-1]x20
13435    #     o videos/video_${nm_source}_to_${nm_target}.mp4,10
13436    #     rm
13437    #   endl
13438    # fi
13439
13440    # Estimate confidence of the color mapping.
13441    +_transfer_rgb[target] 0,$sigma,{displacement,w}
13442    warp. [displacement],1,1,1 c. 0,100% nm. fconfidence
13443    +map_clut[source] . nm. confidence
13444
13445    # Generate transfer CLUT.
13446    [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1
13447    +_transfer_rgb[target] 0,0,{w} *.. .
13448    warp[-2,-1] [displacement],1,0,1
13449    distance. 1 *. -1 watershed.. . rm. -. 1
13450    nm. clut
13451    b[clut] $sigma%
13452
13453    # Enforce luminosity constraint for non-confident colors.
13454    if $4>0
13455      ^[fconfidence] {$4/10} *[fconfidence] -1 +[fconfidence] 1
13456      +f[fconfidence] x +f. y +f. z a[-3--1] c *. {255/(w-1)}
13457      rgb2hsv[clut,-1] channels. 100%
13458      j[clut] .,0,0,0,2,1,[fconfidence] rm.
13459      hsv2rgb[clut]
13460    fi
13461
13462    # Map CLUT to get result.
13463    +map_clut[source] [clut] nm. res_noregul
13464
13465    # Apply guided smoothing to remove quantization artifacts.
13466    if !$3
13467      nm[res_noregul] res
13468    else
13469      l[source,res_noregul]
13470        rgb2ycbcr  # Perform regularization in YCbCr to avoid creation of false colors.
13471        +-[1] [0] repeat $3 guided. [0],5,5 done +. [0] c. 0,255
13472        ycbcr2rgb
13473      endl
13474      nm. res
13475      j[res] [res_noregul],0,0,0,0,1,[confidence]
13476      rm[res_noregul]
13477    fi
13478
13479    k[res]
13480  endl done
13481
13482# _transfer_rgb : _gamma>=0,_smoothness>=0,_resolution>0
13483# Convert selected images into 3D volumetric scalar function for color matching.
13484_transfer_rgb : l[] check "${1=0}>=0 && ${2=1.5}>=0 && ${3=128}>0" gamma=$1 smoothness=$2 res=$3
13485  onfail noarg gamma=0 smoothness=1.5 res=128 endl
13486  e[^-1] "Convert image$? as 3D volumetric scalar functions for color matching, with gamma "$gamma",
13487          smoothness "$smoothness" and resolution "$res"."
13488  to_rgb repeat $! l[$>]
13489    b 0.3%
13490    r {w*h},3,1,1,-1 * {($res-1)/255}
13491    pointcloud 1,$res,$res,$res f 'if(i,i^$gamma,0)' b $smoothness% n 0,1
13492  endl done
13493
13494#@cli xyz2jzazbz
13495#@cli : Convert color representation of selected images from XYZ to RGB.
13496xyz2jzazbz :
13497  e[^-1] "Convert color representation of image$? from XYZ to Jzazbz."
13498  repeat $! l[$>] split_opacity
13499    f[0] ${-_jzazbz_const}"
13500      X = i0*255;
13501      Y = i1*255;
13502      Z = i2*255;
13503      Xp = Jzazbz_b*X - (Jzazbz_b - 1)*Z;
13504      Yp = Jzazbz_g*Y - (Jzazbz_g - 1)*X;
13505      Zp = Z;
13506      L = 0.41478972*Xp + 0.579999*Yp + 0.0146480*Zp;
13507      M = -0.2015100*Xp + 1.120649*Yp + 0.0531008*Zp;
13508      S = -0.0166008*Xp + 0.264800*Yp + 0.6684799*Zp;
13509      tmp = (L/peakLum)^Jzazbz_n;
13510      Lp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p;
13511      tmp = (M/peakLum)^Jzazbz_n;
13512      Mp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p;
13513      tmp = (S/peakLum)^Jzazbz_n;
13514      Sp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p;
13515      Iz  = 0.5*Lp + 0.5*Mp;
13516      az = 3.52400*Lp - 4.066708*Mp + 0.542708*Sp;
13517      bz = 0.199076*Lp + 1.096799*Mp - 1.295875*Sp;
13518      Jz = (1 + Jzazbz_d)*Iz/(1 + Jzazbz_d*Iz) - Jzazbz_d0;
13519      [ Jz,az,bz ]"
13520  a c endl done
13521
13522#@cli xyz2lab : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
13523#@cli : Convert color representation of selected images from XYZ to Lab.
13524#@cli : Default value: 'illuminant=2'.
13525xyz2lab : skip "${1=,}"
13526  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
13527  e[^-1] "Convert color representation of image$? from XYZ to Lab, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
13528  to_color
13529  f "
13530    begin(
13531      const epsilon = 216/24389;
13532      const kappa = 24389/27;
13533      lab(x) = (x>epsilon?cbrt(x):(x*kappa + 16)/116);
13534      D65 = [ 0.4124564, 0.3575761, 0.1804375,
13535              0.2126729, 0.7151522, 0.0721750,
13536              0.0193339, 0.1191920, 0.9503041 ];
13537      D50 = [ 0.43603516, 0.38511658, 0.14305115,
13538              0.22248840, 0.71690369, 0.06060791,
13539              0.01391602, 0.09706116, 0.71392822 ];
13540      E = [ 0.488718,0.3106803,0.2006017,
13541            0.1762044,0.8129847,0.0108109,
13542            0,0.0102048,0.9897952 ];
13543      white = ("$illu"==2?E:"$illu"==1?D65:D50)*[ 1,1,1 ];
13544    );
13545    xr = i0/white[0];
13546    yr = i1/white[1];
13547    zr = i2/white[2];
13548    fx = lab(xr);
13549    fy = lab(yr);
13550    fz = lab(zr);
13551    [ cut(116*fy - 16,0,100), 500*(fx - fy), 200*(fy - fz) ]"
13552
13553#@cli xyz2rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
13554#@cli : Convert color representation of selected images from XYZ to RGB.
13555#@cli : Default value: 'illuminant=2'.
13556xyz2rgb : skip "${1=,}"
13557  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
13558  e[^-1] "Convert color representation of image$? from XYZ to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
13559  if $illu==2 # E
13560    mix_rgb {[2.3706743,-0.9000405,-0.4706338,\
13561              -0.513885,1.4253036,0.0885814,\
13562              0.0052982,-0.0146949,1.0093968]*255}
13563  elif $illu # D65
13564    mix_rgb {[3.2404542,-1.5371385,-0.4985314,\
13565              -0.9692660,1.8760108,0.0415560,\
13566              0.0556434,-0.2040259,1.0572252]*255}
13567  else # D50
13568    mix_rgb {[3.134274799724,-1.617275708956,-0.490724283042,\
13569              -0.978795575994,1.916161689117,0.033453331711,\
13570              0.071976988401,-0.228984974402,1.405718224383]*255}
13571  fi
13572  c 0,255
13573
13574#@cli xyz82rgb : illuminant={ 0=D50 | 1=D65 | 2=E } : (no arg)
13575#@cli : Convert color representation of selected images from XYZ8 to RGB.
13576#@cli : Default value: 'illuminant=2'.
13577xyz82rgb : skip "${1=,}"
13578  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
13579  e[^-1] "Convert color representation of image$? from XYZ8 to RGB, using the "${arg\ 1+$illu,D50,D65,E}" illuminant."
13580  repeat $!
13581    sh[$>] 0 /. 255 rm.
13582    sh[$>] 1 /. 255 rm.
13583    sh[$>] 2 /. 231.8182 rm.
13584  done xyz2rgb $illu
13585
13586#@cli ycbcr2rgb
13587#@cli : Convert color representation of selected images from YCbCr to RGB.
13588ycbcr2rgb :
13589  e[^-1] "Convert color representation of image$? from YCbCr to RGB."
13590  repeat $!
13591    sh[$>] 0 -. 16 rm.
13592    sh[$>] 1,2 -. 128 rm.
13593    sh[$>] 0,2 mix_rgb. 298,0,409,\
13594                        298,-100,-208,\
13595                        298,516,0
13596    +. 128 /. 256 c. 0,255 rm.
13597  done
13598
13599#@cli yiq2rgb
13600#@cli : Convert color representation of selected images from YIQ to RGB.
13601yiq2rgb :
13602  e[^-1] "Convert color representation of image$? from YIQ to RGB."
13603  mix_rgb 1,0.9563,0.6210,\
13604          1,-0.2721,-0.6474,\
13605          1,-1.1070,1.7046
13606  c 0,255
13607
13608#@cli yiq82rgb
13609#@cli : Convert color representation of selected images from YIQ8 to RGB.
13610yiq82rgb :
13611  e[^-1] "Convert color representation of image$? from YIQ8 to RGB."
13612  repeat $!
13613    sh[$>] 1 /. 0.8393238012481239 -. 151.908 rm.
13614    sh[$>] 2 /. 0.9567690472081104 -. 133.261 rm.
13615  done
13616  mix_rgb 1,0.9563,0.6210,\
13617          1,-0.2721,-0.6474,\
13618          1,-1.1070,1.7046
13619  c 0,255
13620
13621#@cli yuv2rgb
13622#@cli : Convert color representation of selected images from YUV to RGB.
13623yuv2rgb :
13624  e[^-1] "Convert color representation of image$? from YUV to RGB."
13625  mix_rgb {[1,0,1.13983,\
13626            1,-0.39465,-0.5806,\
13627            1,2.03211,0]*255}
13628  c 0,255
13629
13630#@cli yuv82rgb
13631#@cli : Convert selected images from YUV8 to RGB color bases.
13632yuv82rgb :
13633  e[^-1] "Convert color representation of image$? from YUV8 to RGB."
13634  repeat $!
13635    sh[$>] 0 /. 255 rm.
13636    sh[$>] 1 /. 289.773 -. 0.44 rm.
13637    sh[$>] 2 /. 205.645 -. 0.62 rm.
13638  done yuv2rgb
13639
13640#---------------------------------
13641#
13642#@cli :: Geometry Manipulation
13643#
13644#---------------------------------
13645
13646#@cli a : eq. to 'append' : (+)
13647
13648#@cli append : [image],axis,_centering : axis,_centering : (+)
13649#@cli : Append specified image to selected images, or all selected images together, along specified axis.
13650#@cli : (eq. to 'a').\n
13651#@cli : 'axis' can be { x | y | z | c }.
13652#@cli : Usual 'centering' values are { 0=left-justified | 0.5=centered | 1=right-justified }.
13653#@cli : Default value: 'centering=0'.
13654#@cli : $ image.jpg split y,10 reverse append y
13655#@cli : $ image.jpg repeat 5 +rows[0] 0,{10+18*$>}% done remove[0] append x,0.5
13656#@cli : $ image.jpg append[0] [0],y
13657
13658#@cli append_tiles : _M>=0,_N>=0,0<=_centering_x<=1,0<=_centering_y<=1
13659#@cli : Append MxN selected tiles as new images.
13660#@cli : If 'N' is set to 0, number of rows is estimated automatically.
13661#@cli : If 'M' is set to 0, number of columns is estimated automatically.
13662#@cli : If 'M' and 'N' are both set to '0', auto-mode is used.
13663#@cli : If 'M' or 'N' is set to 0, only a single image is produced.
13664#@cli : 'centering_x' and 'centering_y' tells about the centering of tiles when they have different sizes.
13665#@cli : Default values: 'M=0', 'N=0', 'centering_x=centering_y=0.5'.
13666#@cli : $ image.jpg split xy,4 append_tiles ,
13667append_tiles : check "isint(${1=0}) && isint(${2=0}) && ${3=0}>=0 && $3<=1 && ${4=$3}>=0 && $4<=1"
13668  if !$!
13669    e[0--3] "Append image$? as a 0x0-tiled image."
13670    return
13671  elif !$1" && "!$2 # Auto-mode
13672    N={int(sqrt($!))} M={ceil($!/$N)}
13673    e[0--3] "Append image$? as a "${M}x${N}"-tiled image (auto-mode)."
13674  elif !$2 # Auto-rows
13675    M=$1 N={round($!/$1,1,1)}
13676    e[0--3] "Append image$? as a "${M}x${N}"-tiled image."
13677  elif !$1 # Auto-columns
13678    M={round($!/$2,1,1)} N=$2
13679    e[0--3] "Append image$? as a "${M}x${N}"-tiled image."
13680  else
13681    e[0--3] "Append image$?, as $1x$2-tiled images."
13682    M=$1 N=$2
13683  fi
13684  W,H=${-max_wh} rr2d $W,$H,2,1
13685  MN={$M*$N} if $!%$MN 0x{$MN-($!%$MN)} fi
13686  repeat $!/$MN l[$>-{$>+$MN-1}]
13687    repeat $!/$M a[$>-{$>+$M-1}] x,$3 done a y,$4
13688  endl done
13689
13690#@cli apply_scales : "command",number_of_scales>0,_min_scale[%]>=0,_max_scale[%]>=0,_scale_gamma>0,_interpolation
13691#@cli : Apply specified command on different scales of selected images.
13692#@cli : 'interpolation' can be { 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos }.
13693#@cli : Default value: 'min_scale=25%', 'max_scale=100%' and 'interpolation=3'.
13694#@cli : $ image.jpg apply_scales "blur 5 sharpen 1000",4
13695apply_scales : check "isint($2) && $2>0 && ${3=25%}>=0 && ${4=100%}>=0 && ${5=1}>0 && isint(${6=3}) && $6>=0"
13696               skip "${1=}"
13697  s0="no" s1="nearest-neighbor" s2="average" s3="linear" s4="grid" s5="bicubic" s6="lanczos"
13698  e[^-1] "Apply command '$1' on image$? for $2 scales ($3 -> $4) and "${s{min(6,$6)}}" interpolation."
13699  repeat $! l[$<] nm={0,n}
13700    scale0={if(${"is_percent $3"},$3*max(w,h,d),$3)}
13701    scale1={if(${"is_percent $4"},$4*max(w,h,d),$4)}
13702    repeat $2
13703      scale={$scale0+($scale1-$scale0)*($>/max(1,$2-1))^$5}
13704      w={0,w==1?1:max(1,round($scale*w/max(w,h,d)))}
13705      h={0,h==1?1:max(1,round($scale*h/max(w,h,d)))}
13706      d={0,d==1?1:max(1,round($scale*d/max(w,h,d)))}
13707      +r[0] $w,$h,$d,100%,$6
13708      if narg("$1") l. $1 endl fi
13709    done
13710    rm[0] nm $nm
13711  endl done
13712
13713#@cli autocrop : value1,value2,... : (no arg) : (+)
13714#@cli : Autocrop selected images by specified vector-valued intensity.
13715#@cli : If no arguments are provided, cropping value is guessed.
13716#@cli : $ 400,400,1,3 fill_color 64,128,255 ellipse 50%,50%,120,120,0,1,255 +autocrop
13717
13718#@cli autocrop_components : _threshold[%],_min_area[%]>=0,_is_high_connectivity={ 0 | 1 },\
13719# _output_type={ 0=crop | 1=segmentation | 2=coordinates }
13720#@cli : Autocrop and extract connected components in selected images, according to a mask given as the last channel of
13721#@cli : each of the selected image (e.g. alpha-channel).
13722#@cli : Default values: 'threshold=0%', 'min_area=0.1%', 'is_high_connectivity=0' and 'output_type=1'.
13723#@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 \
13724# +autocrop_components ,
13725autocrop_components : skip ${1=0%} check "${2=0.1%}>=0 && isbool(${3=0}) && isint(${4=1}) && $4>=0 && $4<=2"
13726  e[^-1] "Autocrop connected components from image$?, with threshold $1, minimal area $2, "\
13727         ${arg\ 1+$3,low,high}" connectivity "\
13728         "and output type set to '"${arg\ 1+$4,crop,segmentation,coordinates}"'.\n"
13729  repeat $! l[$>]
13730    min_area={max(1,round(if(${is_percent\ $2},$2*w*h,$2)))}
13731    +channels 100% >. $1 area_fg. 0,$3 >=. $min_area  # Discard background and small objects.
13732    +area. 0,1 <. $min_area -|[-2,-1] label_fg. 0,1    # Fill small holes in objects.
13733
13734    # Extract detected objects.
13735    N={iM} repeat iM
13736      n={1+$>}
13737      e[] "\r  > "$n/$N
13738      rprogress {100*$n/$N}
13739      +==[1] $n +*[0,-1] rm..
13740      if $4==0 coords=${autocrop_coords.\ auto} rm. +z[0] $coords
13741      elif $4==1 autocrop.
13742      else coords=${autocrop_coords.\ auto} rm. ($coords) y.
13743      fi
13744    done
13745    rm[0,1]
13746    if !$! 0 fi
13747    if $4==2 a x fi
13748  endl done
13749
13750#@cli autocrop_seq : value1,value2,... | auto
13751#@cli : Autocrop selected images using the crop geometry of the last one by specified vector-valued intensity,
13752#@cli : or by automatic guessing the cropping value.
13753#@cli : Default value: auto mode.
13754#@cli : $ image.jpg +fill[-1] 0 ellipse[-1] 50%,50%,30%,20%,0,1,1 autocrop_seq 0
13755autocrop_seq : skip ${1=auto}
13756  e[^-1] "Auto-crop image$? using crop geometry of last image by vector '$*'."
13757  if !$! return fi
13758  is_auto={['"$1"']=='auto'}
13759  if $!==1 _autocrop$is_auto ${1--1} return fi
13760  coords=${autocrop_coords.\ ${1--1}}
13761  x0={arg(1,$coords)} y0={arg(2,$coords)} z0={arg(3,$coords)}
13762  x1={arg(4,$coords)} y1={arg(5,$coords)} z1={arg(6,$coords)}
13763  if $x0>$x1" || "$y0>$y1" || "$z0>$z1 i[0--2] 0 rm[1--1:2]
13764  else z $x0,$y0,$z0,$x1,$y1,$z1
13765  fi
13766
13767#@cli channels : c0[%],_c1[%]
13768#@cli : Keep only specified channels of selected images.
13769#@cli : Dirichlet boundary is used when specified channels are out of range.
13770#@cli : Default value: 'c1=c0'.
13771#@cli : $ image.jpg channels 0,1
13772#@cli : $ image.jpg luminance channels 0,2
13773channels : skip ${2=$1}
13774  e[^-1] "Keep channels $1...$2 of image$?."
13775  z 0,0,0,$1,100%,100%,100%,$2
13776
13777#@cli columns : x0[%],_x1[%]
13778#@cli : Keep only specified columns of selected images.
13779#@cli : Dirichlet boundary is used when specified columns are out of range.
13780#@cli : Default value: 'x1=x0'.
13781#@cli : $ image.jpg columns -25%,50%
13782columns : skip ${2=$1}
13783  e[^-1] "Keep columns $1...$2 of image?."
13784  z $1,$2
13785
13786#@cli z : eq. to 'crop'. : (+)
13787
13788#@cli crop : x0[%],x1[%],_boundary_conditions : x0[%],y0[%],x1[%],y1[%],_boundary_conditions : \
13789# x0[%],y0[%],z0[%],x1[%],y1[%],z1[%],_boundary_conditions : \
13790# x0[%],y0[%],z0[%],c0[%],x1[%],y1[%],z1[%],c1[%],_boundary_conditions : (+)
13791#@cli : Crop selected images with specified region coordinates.
13792#@cli : (eq. to 'z').\n
13793#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
13794#@cli : Default value: 'boundary_conditions=0'.
13795#@cli : $ image.jpg +crop -230,-230,280,280,1 crop[0] -230,-230,280,280,0
13796#@cli : $ image.jpg crop 25%,25%,75%,75%
13797
13798#@cli diagonal
13799#@cli : Transform selected vectors as diagonal matrices.
13800#@cli : $ 1,10,1,1,'y' +diagonal
13801diagonal :
13802  e[^-1] "Transform vector$? as diagonal matrix."
13803  y repeat $! r[$>] {$>,h+1},100%,1,1,0 r[$>] {$>,h},100%,1,1,-1 done
13804
13805# downsize_aliased : 100<=factor<=0
13806# Downsize selected images with a specific algorithm to better render anti-aliased rendering
13807# over transparent background (from aliased transparent images, manage transparent border pixels with more care).
13808downsize_aliased : check "${1=50}>=0 && $1<=100"
13809  if $1==100 return elif !$1 r 1,1,1,100%,2 return fi
13810  N={ceil(1+100/$1)}
13811  repeat $! l[$>]
13812    split_opacity
13813    if $!==1 continue fi
13814    +dilate.. $N +==.. 0
13815    j[0] ..,0,0,0,0,1,. rm[-2,-1]
13816    a c
13817    r $1%,$1%,$1%,100%,2
13818  endl done
13819
13820#@cli elevate : _depth,_is_plain={ 0 | 1 },_is_colored={ 0 | 1 }
13821#@cli : Elevate selected 2D images into 3D volumes.
13822#@cli : Default values: 'depth=64', 'is_plain=1' and 'is_colored=1'.
13823elevate : check "${1=64}>0" skip ${2=1},${3=1}
13824  e[^-1] "Elevate 2D image$? into $1-slices volume(s)."
13825  r 100%,100%,1,100%
13826  repeat $! l[$>] nm={0,n}
13827    +norm 100%,100%,$1,{if($3,{0,s},1)}
13828    m={-2,im} d={-2,iM-$m}
13829    repeat $1
13830      if $2 +>=[1] {$m+$d*($>+1)/$1}
13831      else +ir[1] {$m+$d*$>/$1},{$m+$d*($>+1)/$1}
13832      fi
13833      r. 100%,100%,1,.. if $3 *. [0] fi
13834      j.. .,0,0,$> rm.
13835    done
13836  rm[0,1] nm $nm endl done
13837
13838#@cli expand_x : size_x>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
13839#@cli : Expand selected images along the x-axis.
13840#@cli : Default value: 'boundary_conditions=1'.
13841#@cli : $ image.jpg expand_x 30,0
13842expand_x : check "$1>=0 && ${2=1}>=0 && $2<=3"
13843  e[^-1] "Expand image$? along the x-axis with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
13844          boundary conditions."
13845  repeat $! r[$>] {$>,w+2*$1},100%,100%,100%,0,$2,0.5,0.5,0.5 done
13846
13847#@cli expand_xy : size>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
13848#@cli : Expand selected images along the xy-axes.
13849#@cli : Default value: 'boundary_conditions=1'.
13850#@cli : $ image.jpg expand_xy 30,0
13851expand_xy : check "$1>=0 && ${2=1}>=0 && $2<=3"
13852  e[^-1] "Expand image$? along the xy-axes with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
13853          boundary conditions."
13854  repeat $! r[$>] {$>,w+2*$1},{$>,h+2*$1},100%,100%,0,$2,0.5,0.5,0.5 done
13855
13856#@cli expand_xyz : size>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
13857#@cli : Expand selected images along the xyz-axes.
13858#@cli : Default value: 'boundary_conditions=1'.
13859expand_xyz : check "$1>=0 && ${2=1}>=0 && $2<=3"
13860  e[^-1] "Expand image$? along the xyz-axes with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
13861          boundary conditions."
13862  repeat $! r[$>] {$>,w+2*$1},{$>,h+2*$1},{$>,d+2*$1},100%,0,$2,0.5,0.5,0.5 done
13863
13864#@cli expand_y : size_y>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
13865#@cli : Expand selected images along the y-axis.
13866#@cli : Default value: 'boundary_conditions=1'.
13867#@cli : $ image.jpg expand_y 30,0
13868expand_y : check "$1>=0 && ${2=1}>=0 && $2<=3"
13869  e[^-1] "Expand image$? along the y-axis with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
13870          boundary conditions."
13871  repeat $! r[$>] 100%,{$>,h+2*$1},100%,100%,0,$2,0.5,0.5,0.5 done
13872
13873#@cli expand_z : size_z>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
13874#@cli : Expand selected images along the z-axis.
13875#@cli : Default value: 'boundary_conditions=1'.
13876expand_z : check "$1>=0 && ${2=1}>=0 && $2<=3"
13877  e[^-1] "Expand image$? along the z-axis with size $1 and "${"arg 1+$2,dirichlet,neumann,periodic,mirror"}"
13878          boundary conditions."
13879  repeat $! r[$>] 100%,100%,{$>,d+2*$1},100%,0,$2,0.5,0.5,0.5 done
13880
13881#@cli extract : "condition",_output_type={ 0=xyzc-coordinates | 1=xyz-coordinates | 2=scalar-values | 3=vector-values }
13882#@cli : Extract a list of coordinates or values from selected image, where
13883#@cli : specified mathematical condition holds.
13884#@cli : For N coordinates matching, result is a 1xNx1x4 image.
13885#@cli : Default values: 'output_type=0'.
13886#@cli : $ sp lena +extract "norm(I)>128",3
13887extract : check "isin(${2=0},0,1,2,3)"
13888  s0,s1,s2,s3=xyzc-coordinates,xyz-coordinates,scalar-values,vector-values
13889  e[^-1] "Extract "$s$2" from image$? verifying condition '$1'."
13890  str=">begin(run('1,32,1,',arg(1+$2,4,3,1,s)));($1)?("
13891  if $2==0   str.="da_push([x,y,z,c]));i"
13892  elif $2==1 str.="da_push([x,y,z]));I"
13893  elif $2==2 str.="da_push(i));i"
13894  else       str.="da_push(I));I"
13895  fi
13896  str.=";end(resize(#-1,1,da_size(),1,s(#-1),0))"
13897  repeat $! nm={$>,n} eval[$>] $str nm. $nm rv[$>,-1] rm. done
13898
13899#@cli extract_region : [label_image],_extract_xyz_coordinates={ 0 | 1 },_label_1,...,_label_M
13900#@cli : Extract all pixels of selected images whose corresponding label in '[label_image]' is equal to 'label_m',
13901#@cli : and output them as M column images.
13902#@cli : Default value: 'extract_xyz_coordinates=0'.
13903#@cli : $ image.jpg +blur 3 quantize. 4,0 +extract_region[0] [1],0,1,3
13904extract_region : check ${"is_image_arg $1"}" && isnum(${2=0})"
13905  if $#<3 e[0--3] "Extract pixels of image$? for labels [] in image $1, with"${"arg 1+!!$2,out"}" coordinates
13906                   -> no labels provided, ignoring." return fi
13907  e[^-1] "Extract pixels of image$? for labels {${3--1}} in image $1, with"${"arg 1+!!$2,out"}" coordinates."
13908  pass$1 mv. 0 repeat $!-1 l[0,{1+$<}] nm={n}
13909    1,16,1,{s+($2?3:0)} if $#>3 .x{$#-3} fi
13910    f[0] ">
13911      begin(
13912        const N = iM + 1;  # Number of labels
13913        R = [ ${3--1} ];   # Requested labels
13914        hash = vectorN(0); # Correspondence table
13915        repeat (size(R),k, hash[R[k]] = k + 2);
13916      );
13917      (ind = hash[i])>0?(
13918        $2?da_push(#ind,[ I(#1),x,y,z ]):da_push(#ind,I(#1));
13919      ); i;
13920      end(
13921        repeat (l - 2,k, resize(#k + 2,1,da_size(#k + 2),1,-100,0))
13922      )"
13923    rm[1] nm $nm
13924  endl done rm[0]
13925
13926#@cli montage : "_layout_code",_montage_mode={ 0<=centering<=1 | 2<=scale+2<=3 },\
13927# _output_mode={ 0=single layer | 1=multiple layers },"_processing_command"
13928#@cli : Create a single image montage from selected images, according to specified layout code :
13929#@cli : - 'X' to assemble all images using an automatically estimated layout.
13930#@cli : - 'H' to assemble all images horizontally.
13931#@cli : - 'V' to assemble all images vertically.
13932#@cli : - 'A' to assemble all images as an horizontal array.
13933#@cli : - 'B' to assemble all images as a vertical array.
13934#@cli : - 'Ha:b' to assemble two blocks 'a' and 'b' horizontally.
13935#@cli : - 'Va:b' to assemble two blocks 'a' and 'b' vertically.
13936#@cli : - 'Ra' to rotate a block 'a' by 90 deg. ('RRa' for 180 deg. and 'RRRa' for 270 deg.).
13937#@cli : - 'Ma' to mirror a block 'a' along the X-axis ('MRRa' for the Y-axis).
13938#@cli : A block 'a' can be an image index (treated periodically) or a nested layout expression 'Hb:c','Vb:c','Rb' or
13939#@cli : 'Mb' itself.
13940#@cli : For example, layout code 'H0:V1:2' creates an image where image [0] is on the left, and images [1] and [2]
13941#@cli : vertically packed on the right.
13942#@cli : Default values: 'layout_code=X', 'montage_mode=2', output_mode='0' and 'processing_command=""'.
13943#@cli : $ image.jpg sample ? +plasma[0] shape_cupid 256 normalize 0,255 frame 3,3,0 frame 10,10,255 to_rgb \
13944# +montage A +montage[^-1] H1:V0:VH2:1H0:3
13945montage : check "isnum(${2=2}) && $2>=0 && $2<=3" skip "${1=X}",${3=0},"${4=}"
13946  if $2<=1 e[0--3] "Create aligned montage from image$?, with layout code '$1' and centering $2."
13947  else e[0--3] "Create scaled montage from image$?, with layout code '$1' and scale "{$2-2}"."
13948  fi
13949  to_colormode 0
13950
13951  # Manage automatic layout.
13952  if lowercase('"$1"')=='x' +l
13953    repeat $! nm[$>] $> done
13954    repeat $!-1
13955      if {-2,w>h}" && "w>h mode=V      # Both landscape.
13956      elif {-2,h>w}" && "h>w mode=H    # Both portrait.
13957      elif {-2,w>h}" && "h>w # Landscape - portrait.
13958        if {-2,h/w}<(w/h) mode=V else mode=H fi
13959      else # Portrait - landscape.
13960        if {-2,w/h}<(h/w) mode=H else mode=V fi
13961      fi
13962      name=$mode{-2,n}:{n}
13963      montage[-2,-1] $mode,$2
13964      mv. 0 nm[0] $name
13965    done
13966    layout={0,n}
13967    rm endl montage $layout,$2,$3,"$4"
13968
13969  else # Non-automatic layout.
13970
13971    # Format and check validity of layout code.
13972    N=$!
13973    l[] _scode="$1" _mode=$2
13974      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.
13975      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.
13976      elif s=lowercase('"$1"');s=='a'||s=='b' # Montage as an array.
13977        if $N<2 return fi
13978        nr={round(sqrt($N))} nc={round($N/$nr,1,1)} # Horizontal array.
13979        if lowercase('"$1"')=='b' n=$nr nr=$nc nc=$n fi # Vertical array.
13980        $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
13981        if $nr>1 i[0] {$nr-1},1,1,1,-2 a x fi y
13982      else # Other complex montage.
13983        ('"$1"') f "if(i==72 || i==104,-1,
13984                    if(i==86 || i==118,-2,
13985                    if(i==82 || i==114,-3,
13986                    if(i==77 || i==109,-4,
13987                    if(i>=48 && i<=57,i-48,-5)))))"
13988        s +,-1 s +,-2 s +,-3 s +,-4 s +,-5
13989        repeat $! l[$>] if im>=0 ++. 48 =.. {t} rm. rows 0 fi endl done a y discard -5
13990      fi
13991      f 'if(i<0,i,i%$N)'
13992    endl
13993    if $!==$N rm return fi  # Empty layout code.
13994    nm[^-1] 0 repeat h c={i[$>]} # Duplicate multiple references.
13995      if $c>=0 if {$c,n} i.. [$c] i={$!-2} =. $i,0,$> ref$i=$c else nm[$c] 1 ref$c=$c fi fi
13996    done
13997    _code={^} _lcode={narg($_code)} rm.
13998
13999    # Determine image positions and sizes.
14000    N=$! repeat $N ($>,0,0,{$>,w},{$>,h},0,0,0) done
14001    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
14002    onfail error[0--3] "Too many input images."
14003    endl
14004
14005    # Render montage.
14006    if narg("$4") m "__montage : $4 k[0]"
14007    else m "__montage : if $""7%2 mirror x fi if $""8%2 mirror y fi rotate {90*$""6}
14008                        r {max(1,round($""4,1,1))},{max(1,round($""5,1,1))},1,100%,3"
14009    fi
14010
14011    s=${max_s[^-1]}
14012    repeat h
14013      i={i(0,$>)} xi={i(1,$>)} yi={i(2,$>)} wi={i(3,$>)} hi={i(4,$>)} ai={i(5,$>)} mxi={i(6,$>)} myi={i(7,$>)}
14014      if $3||!$> i.. $w,$h,1,$s fi
14015      __montage[$i] ${ref$i},$xi,$yi,{max(1,$wi)},{max(1,$hi)},$ai,$mxi,$myi
14016      j.. [$i],$xi,$yi
14017    done
14018    um __montage
14019    rm[0-{$N-1},-1]
14020
14021  fi
14022  nm "[Montage '$1']"
14023
14024_montage :
14025  if $_p>$_lcode error "Command 'montage': Incomplete layout code '"$_scode"'." fi
14026  c={arg($_p,$_code)}
14027  if $c>=0 _p+=1 u $c # Single index.
14028  elif $c==-4 # Mirror.
14029    _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
14030  elif $c==-3 # Rotation.
14031    _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
14032  else # Merge.
14033    _p+=1
14034    l=${-_montage} lw={$l,@3} lh={$l,@4}
14035    r=${-_montage} rw={$r,@3} rh={$r,@4}
14036
14037    if $c==-1 # Horizontal merge.
14038      if $_mode<2
14039        h={max($lh,$rh)}
14040        +[$l] '0,0,{($h-$lh)*min(1,$_mode)},0,0,0,0,0'
14041        +[$r] '0,$lw,{($h-$rh)*min(1,$_mode)},0,0,0,0,0'
14042      else
14043        h={($_mode-2)*max($lh,$rh)+(3-$_mode)*min($lh,$rh)}
14044        lf={$h/$lh} rf={$h/$rh} lw={$lw*$lf} rw={$rw*$rf}
14045        *[$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'
14046      fi
14047      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
14048
14049    else # Vertical merge.
14050      if $_mode<2
14051        w={max($lw,$rw)}
14052        +[$l] '0,{($w-$lw)*min(1,$_mode)},0,0,0,0,0,0'
14053        +[$r] '0,{($w-$rw)*min(1,$_mode)},$lh,0,0,0,0,0'
14054      else
14055        w={($_mode-2)*max($lw,$rw)+(3-$_mode)*min($lw,$rw)}
14056        lf={$w/$lw} rf={$w/$rw} lh={$lh*$lf} rh={$rh*$rf}
14057        *[$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'
14058      fi
14059      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
14060    fi
14061    u $l
14062  fi
14063
14064#@cli mirror : { x | y | z }...{ x | y | z } : (+)
14065#@cli : Mirror selected images along specified axes.
14066#@cli : $ image.jpg +mirror y +mirror[0] c
14067#@cli : $ image.jpg +mirror x +mirror y append_tiles 2,2
14068
14069#@cli permute : permutation_string : (+)
14070#@cli : Permute selected image axes by specified permutation.
14071#@cli : 'permutation' is a combination of the character set {x|y|z|c},
14072#@cli : e.g. 'xycz', 'cxyz', ...
14073#@cli : $ image.jpg permute yxzc
14074
14075#@cli r : eq. to 'resize'. : (+)
14076
14077#@cli resize : {[image_w] | width>0[%]},_{[image_h] | height>0[%]},_{[image_d] | depth>0[%]},\
14078# _{[image_s] | spectrum>0[%]},_interpolation,_boundary_conditions,_ax,_ay,_az,_ac : (+)
14079#@cli : Resize selected images with specified geometry.
14080#@cli : (eq. to 'r').\n
14081#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14082# 4=grid | 5=bicubic | 6=lanczos }.
14083#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14084#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14085#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14086#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14087#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0 or 4'
14088#@cli : (set to '0' by default, must be defined in range [0,1]).
14089#@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14090#@cli : $ image.jpg +resize[-1] 256,128,1,3,2 +resize[-1] 120%,120%,1,3,0,1,0.5,0.5 \
14091# +resize[-1] 120%,120%,1,3,0,0,0.2,0.2 +resize[-1] [0],[0],1,3,4
14092
14093#@cli ri : eq. to 'resize_as_image'.
14094ri : skip "${2=1},${3=0},${4=0},${5=0},${6=0},${7=0}" # Faster than the non-shortcut version of the command
14095  pass$1 r[^-1] .,.,.,.,${2--1} rm.
14096
14097#@cli resize_as_image : [reference],_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14098#@cli : Resize selected images to the geometry of specified [reference] image.
14099#@cli : (eq. to 'ri').
14100#@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14101#@cli : $ image.jpg sample duck +resize_as_image[-1] [-2]
14102resize_as_image : check ${"is_image_arg $1"}" && isint(${2=1}) && $2>=-1 && $2<=6 && "\
14103   "isint(${3=0}) && $3>=0 && $3<=3 && isnum(${4=0}) && isnum(${5=0}) && isnum(${6=0}) && isnum(${7=0})"
14104  pass$1 r[^-1] .,.,.,.,${2--1} rm.
14105
14106#@cli resize_mn : width[%]>=0,_height[%]>=0,_depth[%]>=0,_B_value,_C_value
14107#@cli : Resize selected images with Mitchell-Netravali filter (cubic).
14108#@cli : For details about the method, see: <https://de.wikipedia.org/wiki/Mitchell-Netravali-Filter>.
14109#@cli : Default values: 'height=100%', 'depth=100%', 'B=0.3333' and 'C=0.3333'.
14110#@cli : $ image.jpg resize2dx 32 resize_mn 800%,800%
14111resize_mn : check "${2=100%}>=0 && ${3=100%}>=0" skip "${4=0.333},${5=0.333}"
14112  e[^-1] "Resize image$? to $1x$2x$3 using Mitchell-Netravali filter (B=$4, C=$5)."
14113  lib="const B = $4; const C = $5; const boundary = 1; const interp = 0;
14114       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
14115                           + ( (B/2+2*C)*P0 + (2*B+C-3)*P1 + (-5*B/2-2*C+3)*P2 -C*P3)*d^2
14116                           + ( (-B/2-C)*P0 + (B/2+C)*P2)*d
14117                           + B/6*P0 + (-B/3+1)*P1 + B/6*P2);"
14118  repeat $! l[$>] nm={n}
14119    nw={${"is_percent $1"}?max(1,round($1*w)):round($1)}
14120    nh={${"is_percent $2"}?max(1,round($2*h)):round($2)}
14121    nd={${"is_percent $3"}?max(1,round($3*d)):round($3)}
14122    if !$nw" || "!$nh" || "!$nd rm 0
14123    elif !w rm $nw,$nh,$nd,1
14124    else
14125      if w==1||$nw<w r $nw,100%,100%,100%,{w==1?1:2}
14126      elif $nw>w
14127        $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);
14128                                  P2 = I(#-1,X+1); P3 = I(#-1,X+2); mn(P0,P1,P2,P3,d);" k.
14129      fi
14130      if h==1||$nh<h
14131        r 100%,$nh,100%,100%,{h==1?1:2}
14132      elif $nh!=h
14133        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);
14134                                  P2 = I(#-1,x,Y+1); P3 = I(#-1,x,Y+2); mn(P0,P1,P2,P3,d);" k.
14135      fi
14136      if d==1" || "$nd<d
14137        r 100%,100%,$nd,100%,{d==1?1:2}
14138      elif $nd!=d
14139        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);
14140                                  P2 = I(#-1,x,y,Z+1); P3 = I(#-1,x,y,Z+2); mn(P0,P1,P2,P3,d);" k.
14141      fi
14142    fi
14143    nm $nm
14144  endl done
14145
14146#@cli resize_pow2 : _interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14147#@cli : Resize selected images so that each dimension is a power of 2.
14148#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14149# 4=grid | 5=bicubic | 6=lanczos }.
14150#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14151#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14152#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14153#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14154#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14155#@cli : (set to '0' by default, must be defined in range [0,1]).
14156#@cli : Default values: 'interpolation=0', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14157#@cli : $ image.jpg +resize_pow2[-1] 0
14158resize_pow2 : check "isint(${1=0}) && $1>=-1 && $1<=6" skip ${2=0},${3=0},${4=0},${5=0},${6=0}
14159  e[^-1] "Resize image$? so that each dimension is a power of 2."
14160  repeat $!
14161    r[$>] {$>,2^(round(log2(w),1,1))},{$>,2^(round(log2(h),1,1))},{$>,2^(round(log2(d),1,1))},100%,${1-6}
14162  done
14163
14164#@cli rr2d : eq. to 'resize_ratio2d'.
14165rr2d :
14166  _gmic_s="$?" v + _resize_ratio2d $*
14167
14168#@cli resize_ratio2d : width>0,height>0,_mode={ 0=inside | 1=outside | 2=padded },0=<_interpolation<=6
14169#@cli : Resize selected images while preserving their aspect ratio.
14170#@cli : (eq. to 'rr2d').
14171#@cli : Default values: 'mode=0' and 'interpolation=6'.
14172resize_ratio2d :
14173  _gmic_s="$?" v + _$0 $*
14174
14175_resize_ratio2d : check "$1>0 && $2>0 && ${3=0}>=0 && $3<=2 && ${4=6}>=0 && $4<=6"
14176  e[0--3] "Resize 2D image"$_gmic_s" to $1x$2 with ratio-"${arg\ 1+$3,inside,outside,padded}\
14177           " mode and interpolation type $4."
14178  repeat $!
14179    ratio={$>,if($3==1,max($1/w,$2/h),min($1/w,$2/h))}
14180    r[$>] {$>,[max(1,round(w*$ratio)),max(1,round(h*$ratio))]},100%,100%,$4
14181  done
14182  if $3==2 r $1,$2,100%,100%,0,0,0.5,0.5 fi
14183
14184#@cli r2din : eq. to 'resize2din'
14185r2din :
14186  _gmic_s="$?" v + _resize2din $*
14187
14188#@cli resize2din : width[%]>0,_height[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14189#@cli : Resize selected images so the size is not larger than 'width'x'height' while preserving 2D ratio.
14190#@cli : (eq. to r2din).\n
14191#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14192# 4=grid | 5=bicubic | 6=lanczos }.
14193#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14194#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14195#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14196#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14197#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14198#@cli : (set to '0' by default, must be defined in range [0,1]).
14199#@cli : Default values: 'height=100%', 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14200#@cli : $ image.jpg +resize2din 100,100 append x
14201resize2din :
14202  _gmic_s="$?" v + _$0 $*
14203
14204_resize2din : check "$1>0 && ${2=100%}>0 && ${3=3}>=0 && $3<=6 && ${4=0}>=0 && $4<=3 && "\
14205                    "${5=0}>=0 && $5<=1 && ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1 && ${8=0}>=0 && $8<=1"
14206  e[0--3] "Resize image"$_gmic_s" so the size is not larger than $1x$2, while preserving 2D ratio."
14207  eval "repeat (l,k,
14208    W = "${"is_percent $1"}"?w#k*$1:$1;
14209    H = "${"is_percent $2"}"?h#k*$2:$2;
14210    dims = round(min(W/w#k,H/h#k)*[w#k,h#k]);
14211    resize(#k,max(1,dims[0]),max(1,dims[1]),d#k,s#k,${3-8});
14212  )"
14213
14214#@cli r3din : eq. to 'resize3din'
14215r3din :
14216  _gmic_s="$?" v + _resize3din $*
14217
14218#@cli resize3din : width[%]>0,_height[%]>0,_depth[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14219#@cli : Resize selected images so the size is not larger than 'width'x'height'x'depth' while preserving 3D ratio.
14220#@cli : (eq. to r3din).\n
14221#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14222# 4=grid | 5=bicubic | 6=lanczos }.
14223#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14224#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14225#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14226#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14227#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14228#@cli : (set to '0' by default, must be defined in range [0,1]).
14229#@cli : Default values: 'height=100%', 'depth=100%', 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14230resize3din :
14231  _gmic_s="$?" v + _$0 $*
14232
14233_resize3din : check "$1>0 && ${2=100%}>0 && ${3=100%}>0 && ${4=3}>=0 && $4<=6 && ${5=0}>=0 && $5<=3 && "\
14234                    "${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1 && ${8=0}>=0 && $8<=1 && ${9=0}>=0 && $9<=1"
14235  e[0--3] "Resize image"$_gmic_s" so the size is not larger than $1x$2x$3, while preserving 3D ratio."
14236  eval "repeat (l,k,
14237    W = "${"is_percent $1"}"?w#k*$1:$1;
14238    H = "${"is_percent $2"}"?h#k*$2:$2;
14239    D = "${"is_percent $3"}"?d#k*$3:$3;
14240    dims = round(min(W/w#k,H/h#k,D/d#k)*[w#k,h#k,d#k]);
14241    resize(#k,max(1,dims[0]),max(1,dims[1]),max(1,dims[2]),s#k,${4-9});
14242  )"
14243
14244#@cli r2dout : eq. to 'resize2dout'
14245r2dout :
14246  _gmic_s="$?" v + _resize2dout $*
14247
14248#@cli resize2dout : width[%]>0,_height[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14249#@cli : Resize selected images so the size is not smaller than 'width'x'height' while preserving 2D ratio.
14250#@cli : (eq. to r2dout).\n
14251#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14252# 4=grid | 5=bicubic | 6=lanczos }.
14253#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14254#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14255#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14256#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14257#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14258#@cli : (set to '0' by default, must be defined in range [0,1]).
14259#@cli : Default values: 'height=100%', 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14260#@cli : $ image.jpg +resize2dout 100,100 append x
14261resize2dout :
14262  _gmic_s="$?" v + _$0 $*
14263
14264_resize2dout : check "$1>0 && ${2=100%}>0 && ${3=3}>=0 && $3<=6 && ${4=0}>=0 && $4<=3 && "\
14265                     "${5=0}>=0 && $5<=1 && ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1 && ${8=0}>=0 && $8<=1"
14266  e[0--3] "Resize image"$_gmic_s" so the size is not larger than $1x$2, while preserving 2D ratio."
14267  eval "repeat (l,k,
14268    W = "${"is_percent $1"}"?w#k*$1:$1;
14269    H = "${"is_percent $2"}"?h#k*$2:$2;
14270    dims = round(max(W/w#k,H/h#k)*[w#k,h#k]);
14271    resize(#k,max(1,dims[0]),max(1,dims[1]),d#k,s#k,${3-8});
14272  )"
14273
14274#@cli r3dout : eq. to 'resize3dout'
14275r2dout :
14276  _gmic_s="$?" v + _resize3dout $*
14277
14278#@cli resize3dout : width[%]>0,_height[%]>0,_depth[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14279#@cli : Resize selected images so the size is not smaller than 'width'x'height'x'depth' while preserving 3D ratio.
14280#@cli : (eq. to r3dout).\n
14281#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14282# 4=grid | 5=bicubic | 6=lanczos }.
14283#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14284#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14285#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14286#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14287#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14288#@cli : (set to '0' by default, must be defined in range [0,1]).
14289#@cli : Default values: 'height=100%', 'depth=100%', 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14290resize3dout :
14291  _gmic_s="$?" v + _$0 $*
14292
14293_resize3dout : check "$1>0 && ${2=100%}>0 && ${3=100%}>0 && ${4=3}>=0 && $4<=6 && ${5=0}>=0 && $5<=3 && "\
14294                     "${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1 && ${8=0}>=0 && $8<=1 && ${9=0}>=0 && $9<=1"
14295  e[0--3] "Resize image"$_gmic_s" so the size is not larger than $1x$2x$3, while preserving 3D ratio."
14296  eval "repeat (l,k,
14297    W = "${"is_percent $1"}"?w#k*$1:$1;
14298    H = "${"is_percent $2"}"?h#k*$2:$2;
14299    D = "${"is_percent $3"}"?d#k*$3:$3;
14300    dims = round(max(W/w#k,H/h#k,D/d#k)*[w#k,h#k,d#k]);
14301    resize(#k,max(1,dims[0]),max(1,dims[1]),max(1,dims[2]),s#k,${4-9});
14302  )"
14303
14304#@cli r2dx : eq. to 'resize2dx'.
14305r2dx :
14306  _gmic_s="$?" v + _resize2dx $*
14307
14308#@cli resize2dx : width[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14309#@cli : Resize selected images along the x-axis, while preserving 2D ratio.
14310#@cli : (eq. to 'r2dx').\n
14311#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14312# 4=grid | 5=bicubic | 6=lanczos }.
14313#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14314#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14315#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14316#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14317#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14318#@cli : (set to '0' by default, must be defined in range [0,1]).
14319#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14320#@cli : $ image.jpg +resize2dx 100,2 append x
14321resize2dx :
14322  _gmic_s="$?" v + _$0 $*
14323
14324_resize2dx : check "$1>0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
14325                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
14326  e[0--3] "Resize 2D image"$_gmic_s" to $1 pixels along the x-axis, while preserving 2D ratio."
14327  repeat $! l[$>]
14328    size={if(${is_percent\ $1},$1*w,$1)}
14329    r {max(1,$size)},{max(1,h*$size/w)},100%,100%,${2-7}
14330  endl done
14331
14332#@cli r2dy : eq. to 'resize2dy'.
14333r2dy :
14334  _gmic_s="$?" v + _resize2dy $*
14335
14336#@cli resize2dy : height[%]>=0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14337#@cli : Resize selected images along the y-axis, while preserving 2D ratio.
14338#@cli : (eq. to 'r2dy').\n
14339#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14340# 4=grid | 5=bicubic | 6=lanczos }.
14341#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14342#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14343#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14344#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14345#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14346#@cli : (set to '0' by default, must be defined in range [0,1]).
14347#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14348#@cli : $ image.jpg +resize2dy 100,2 append x
14349resize2dy :
14350  _gmic_s="$?" v + _$0 $*
14351
14352_resize2dy : check "$1>=0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
14353                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
14354  e[0--3] "Resize 2D image"$_gmic_s" to $1 pixels along the y-axis, while preserving 2D ratio."
14355  repeat $! l[$>]
14356    size={if(${is_percent\ $1},$1*h,$1)}
14357    r {max(1,w*$size/h)},{max(1,$size)},100%,100%,${2-7}
14358  endl done
14359
14360#@cli r3dx : eq. to 'resize3dx'.
14361r3dx :
14362  _gmic_s="$?" v + _resize3dx $*
14363
14364#@cli resize3dx : width[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14365#@cli : Resize selected images along the x-axis, while preserving 3D ratio.
14366#@cli : (eq. to 'r3dx').\n
14367#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14368# 4=grid | 5=bicubic | 6=lanczos }.
14369#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14370#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14371#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14372#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14373#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14374#@cli : (set to '0' by default, must be defined in range [0,1]).
14375#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14376resize3dx :
14377  _gmic_s="$?" v + _$0 $*
14378
14379_resize3dx : check "$1>0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
14380                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
14381  e[0--3] "Resize 3D image"$_gmic_s" to $1 pixels along the x-axis, while preserving 3D ratio."
14382  repeat $! l[$>]
14383    size={if(${is_percent\ $1},$1*w,$1)}
14384    r {max(1,$size)},{max(1,h*$size/w)},{max(1,d*$size/w)},100%,${2-7}
14385  endl done
14386
14387#@cli r3dy : eq. to 'resize3dy'.
14388r3dy :
14389  _gmic_s="$?" v + _resize3dy $*
14390
14391#@cli resize3dy : height[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14392#@cli : Resize selected images along the y-axis, while preserving 3D ratio.
14393#@cli : (eq. to 'r3dy').\n
14394#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14395# 4=grid | 5=bicubic | 6=lanczos }.
14396#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14397#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14398#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14399#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14400#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14401#@cli : (set to '0' by default, must be defined in range [0,1]).
14402#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14403resize3dy :
14404  _gmic_s="$?" v + _$0 $*
14405
14406_resize3dy : check "$1>0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
14407                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
14408  e[0--3] "Resize 3D image"$_gmic_s" to $1 pixels along the y-axis, while preserving 3D ratio."
14409  repeat $! l[$>]
14410    size={if(${is_percent\ $1},$1*h,$1)}
14411    r {max(1,w*$size/h)},{max(1,$size)},{max(1,d*$size/h)},100%,${2-7}
14412  endl done
14413
14414#@cli r3dz : eq. to 'resize3dz'.
14415r3dz :
14416  _gmic_s="$?" v + _resize3dz $*
14417
14418#@cli resize3dz : depth[%]>0,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac
14419#@cli : Resize selected images along the z-axis, while preserving 3D ratio.
14420#@cli : (eq. to 'r3dz').\n
14421#@cli : 'interpolation' can be { -1=none (memory content) | 0=none | 1=nearest | 2=average | 3=linear | \
14422# 4=grid | 5=bicubic | 6=lanczos }.
14423#@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode :
14424#@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless.
14425#@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14426#@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0=none | 1=neumann }.
14427#@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0'
14428#@cli : (set to '0' by default, must be defined in range [0,1]).
14429#@cli : Default values: 'interpolation=3', 'boundary_conditions=0' and 'ax=ay=az=ac=0'.
14430resize3dz :
14431  _gmic_s="$?" v + _$0 $*
14432
14433_resize3dz : check "$1>0 && ${2=3}>=0 && $2<=6 && ${3=0}>=0 && $3<=3 && ${4=0}>=0 && $4<=1 && ${5=0}>=0 && $5<=1 &&
14434                    ${6=0}>=0 && $6<=1 && ${7=0}>=0 && $7<=1"
14435  e[0--3] "Resize 3D image"$_gmic_s" to $1 pixels along the z-axis, while preserving 3D ratio."
14436  repeat $! l[$>]
14437    size={if(${is_percent\ $1},$1*d,$1)}
14438    r[$>] {max(1,w*$size/d)},{max(1,h*$size/d)},{max(1,$size)},100%,${2-7}
14439  endl done
14440
14441#@cli rotate : angle,_interpolation,_boundary_conditions,_center_x[%],_center_y[%] : \
14442# u,v,w,angle,interpolation,boundary_conditions,_center_x[%],_center_y[%],_center_z[%] : (+)
14443#@cli : Rotate selected images with specified angle (in deg.), and optionally 3D axis (u,v,w).
14444#@cli : 'interpolation' can be { 0=none | 1=linear | 2=bicubic }.
14445#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14446#@cli : When a rotation center (cx,cy,_cz) is specified, the size of the image is preserved.
14447#@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'center_x=center_y=(undefined)'.
14448#@cli : $ image.jpg +rotate -25,1,2,50%,50% rotate[0] 25
14449
14450#@cli rotate_tileable : angle,_max_size_factor>=0
14451#@cli : Rotate selected images by specified angle and make them tileable.
14452#@cli : If resulting size of an image is too big, the image is replaced by a 1x1 image.
14453#@cli : Default values: 'max_size_factor=8'.
14454rotate_tileable : check ${2=8}>=0
14455  e[^-1] "Rotate image$? with angle $1 deg. and make them tileable."
14456
14457  # Reduce angle to known fraction.
14458  angle={$1%360}
14459  if $angle>=270 rotate 270 angle-=270
14460  elif $angle>=180 rotate 180 angle-=180
14461  elif $angle>=90 rotate 90 angle-=90
14462  fi
14463  # List of known fractions.
14464  (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)
14465  s. x,2 +/[-2,-1] atan. *. {180/pi}   # Compute corresponding angles.
14466  ($angle) index. .. rm..
14467  p={-3,@{^}} q={-2,@{^}} rm[-3--1]       # Find nearest fraction p/q to atan(angle).
14468  if !$p||!$q return fi
14469
14470  repeat $! l[$>]
14471    # Compute width and height of tile.
14472    theta={atan2($p,$q)}
14473    gcd=${gcd" "{h*$q},{w*$p}}
14474    pw={h*$q/$gcd}
14475    nw={round($pw*w/cos($theta))}
14476    gcd=${gcd" "{h*$p},{w*$q}}
14477    qh={w*$q/$gcd}
14478    nh={round($qh*h/cos($theta))}
14479
14480    # Rotate and make tileable (may result in very large images!).
14481    if !$2" || "($nw<$2*w" && "$nh<$2*h)
14482      r {1.5*$nw},{1.5*$nh},1,100%,0,2
14483      rotate {$theta*180/pi},1,2,50%,50%
14484      r $nw,$nh,1,100%,0,2,0.5,0.5
14485    else error[0--4] "Command '$0': Invalid image dimension "({w},{h},{d},{s}).
14486    fi
14487  endl done
14488
14489#@cli rows : y0[%],_y1[%]
14490#@cli : Keep only specified rows of selected images.
14491#@cli : Dirichlet boundary conditions are used when specified rows are out of range.
14492#@cli : Default value: 'y1=y0'.
14493#@cli : $ image.jpg rows -25%,50%
14494rows : skip ${2=$1}
14495  e[^-1] "Keep rows $1...$2 of image$?."
14496  z 0,$1,100%,$2
14497
14498#@cli scale2x
14499#@cli : Resize selected images using the Scale2x algorithm.
14500#@cli : $ image.jpg threshold 50% resize 50%,50% +scale2x
14501scale2x :
14502  e[^-1] "Double xy-dimensions of image$?, using Scale2x algorithm."
14503  repeat $! l[$>]
14504    r 200%,200%
14505    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);
14506       !dy*(!dx*if(C==A&&C!=D&&A!=B,A,i) + dx*if(A==B&&A!=C&&B!=D,B,i)) +
14507       dy*(dx*if(B==D&&B!=A&&D!=C,D,i) + !dx*if(D==C&&D!=B&&C!=A,C,i))"
14508  endl done
14509
14510#@cli scale3x
14511#@cli : Resize selected images using the Scale3x algorithm.
14512#@cli : $ image.jpg threshold 50% resize 33%,33% +scale3x
14513scale3x :
14514  e[^-1] "Triple xy-dimensions of image$?, using Scale3x algorithm."
14515  repeat $! l[$>]
14516    r 300%,300%
14517    f "dx=x%3;dy=y%3;c0=!dx;c1=(dx==1);c2=(dx==2);
14518       A=j(-3,-3,0,0,0,1);B=j(0,-3,0,0,0,1);C=j(3,-3,0,0,0,1);
14519       D=j(-3,0,0,0,0,1);F=j(3,0,0,0,0,1);
14520       G=j(-3,3,0,0,0,1);H=j(0,3,0,0,0,1);I=j(3,3,0,0,0,1);
14521       !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) +
14522            c2*if(B==F&&B!=D&&F!=H,F,i)) +
14523       (dy==1)*(c0*if((H==D&&H!=F&&D!=B&&i!=A)||(D==B&&D!=H&&B!=F&&i!=G),D,i) + c1*i +
14524            c2*if((B==F&&B!=D&&F!=H&&i!=I)||(F==H&&F!=B&&H!=D&&i!=C),F,i)) +
14525       (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) +
14526            c2*if(F==H&&F!=B&&H!=D,F,i))"
14527  endl done
14528
14529#@cli scale_dcci2x : _edge_threshold>=0,_exponent>0,_extend_1px={ 0=false | 1=true }
14530#@cli : Double image size using directional cubic convolution interpolation,
14531#@cli : as described in <https://en.wikipedia.org/wiki/Directional_Cubic_Convolution_Interpolation>.
14532#@cli : Default values: 'edge_threshold=1.15', 'exponent=5' and 'extend_1px=0'.
14533#@cli : $ image.jpg +scale_dcci2x ,
14534scale_dcci2x : check "${1=1.15}>=0 && ${2=5}>=0" skip ${3=0}
14535  e[^-1] "Double xy-dimensions of image$?, using DCCI2x algorithm."
14536  repeat $! l[$>]
14537    r {2*w-(!$3)},{2*h-(!$3)},1,100%,4
14538
14539    # Estimate diagonal values.
14540    f "begin(
14541         const threshold = $1;
14542         const exponent = $2;
14543         interpolation = 0;
14544         boundary = 1;
14545         j1(x,y) = P[7*y + x + 24]; # eq. to 'j(x,y)', but faster
14546         j2(x,y) = P[7*x - y + 24]; # eq. to 'j(-y,x)', but faster
14547         interp(k) = -k#(-3,-3) + 9*k#(-1,-1) + 9*k#(1,1) - k#(3,3);
14548         d(k) = sum(abs([
14549                    k#(-1,-3) - k#(-3,-1), k#(1,-3) - k#(-1,-1), k#(3,-3) - k#(1,-1),
14550                    k#(-1,-1) - k#(-3,1), k#(1,-1) - k#(-1,1), k#(3,-1) - k#(1,1),
14551                    k#(-1,1) - k#(-3,3), k#(1,1) - k#(-1,3), k#(3,1) - k#(1,3)
14552                ]));
14553       );
14554       if (!((x*y)%2),i,
14555       ref(crop(x - 3,y - 3,0,c,7,7,1,1),P);
14556       d1 = d(j1);
14557       d2 = d(j2);
14558       ratio = (1 + d1)/(1 + d2);
14559       value = ratio>threshold ? interp(j1): # Up-right edge
14560               ratio<(1/threshold) ? interp(j2): # Down-right edge
14561               (w1 = 1/(1 + d1^exponent); w2 = 1/(1 + d2^exponent);
14562                (interp(j1)*w1 + interp(j2)*w2)/(w1 + w2)); # Smooth area
14563       value/=16)"
14564
14565    # Estimate remaining values.
14566    f "begin(
14567         const threshold = $1;
14568         const exponent = $2;
14569         interpolation = 0;
14570         boundary = 1;
14571         j1(x,y) = P[7*y + x + 24]; # eq. to 'j(x,y)', but faster
14572         j2(x,y) = P[7*x - y + 24]; # eq. to 'j(-y,x)', but faster
14573         interp(k) = -k#(0,-3) + 9*k#(0,-1) + 9*k#(0,1) - k#(0,3);
14574         d(k) = sum(abs([
14575                    k#(-1,-2) - k#(1,-2),
14576                    k#(-2,-1) - k#(0,-1), k#(0,-1) - k#(2,-1),
14577                    k#(-3,0) - k#(-1,0), k#(-1,0) - k#(1,0), k#(1,0) - k#(3,0),
14578                    k#(-2,1) - k#(0,1), k#(0,1) - k#(2,1),
14579                    k#(-1,2) - k#(1,2)
14580                ]));
14581       );
14582       if ((x%2) + (y%2)!=1,i,
14583         ref(crop(x - 3,y - 3,0,c,7,7,1,1),P);
14584         d1 = d(j1);
14585         d2 = d(j2);
14586         ratio = (1 + d1)/(1 + d2);
14587         value = ratio>threshold ? interp(j1) : # Horizontal edge
14588                 ratio<(1/threshold) ? interp(j2) : # Vertical edge
14589                 (w1 = 1/(1 + d1^exponent); w2 = 1/(1 + d2^exponent);
14590                  (interp(j1)*w1 + interp(j2)*w2)/(w1 + w2)); # Smooth area
14591         value/=16)"
14592  endl done
14593
14594#@cli seamcarve : _width[%]>=0,_height[%]>=0,_is_priority_channel={ 0 | 1 },_is_antialiasing={ 0 | 1 },\
14595# _maximum_seams[%]>=0
14596#@cli : Resize selected images with specified 2D geometry, using the seam-carving algorithm.
14597#@cli : Default values: 'height=100%', 'is_priority_channel=0', 'is_antialiasing=1' and 'maximum_seams=25%'.
14598#@cli : $ image.jpg seamcarve 60%
14599# The main code of this algorithm has been done by Andy (Garagecoder).
14600seamcarve : check "${2=100%}>=0 && ${5=25%}>=0" skip ${3=0},${4=1}
14601  e[^-1] "Resize image$? to $1x$2 using seam-carving algorithm, "${arg\ 1+!$3,with,without}" priority channel, "\
14602         ${arg\ 1+!$4,with,without}" anti-aliasing and maximum seams $5."
14603  repeat $! l[$>]
14604    nw={max(1,round(if(${is_percent\ $1},$1*w,$1)))}
14605    nh={max(1,round(if(${is_percent\ $2},$2*h,$2)))}
14606    if $nw!=w _seamcarve $nw,$3,$4,$5 fi
14607    if $nh!=h transpose _seamcarve $nh,$3,$4,$5 transpose fi
14608  endl done
14609
14610# Subroutine to remove/add vertical seams/
14611# $1 = desired width.
14612# $2 = is_priority_channel={ 0 | 1 }
14613# $3 = is_antialiasing={ 0 | 1 }
14614# $4 = max number of seams added/removed at once.
14615_seamcarve :
14616  do
14617    max_seams={max(1,round(if(${is_percent\ $4},$4*w,$4)))}
14618    ssms={max(min(round($1-w),w),1-w)}
14619    sms={min($max_seams,abs($ssms))}
14620
14621    # Compute potential map.
14622    if $2 s[0] c,{1-s} /. 256 fi
14623    +gradient[0] a[-2,-1] c abs. compose_channels. + n. 0,1
14624    if $2 +. .. a[0,1] c fi
14625
14626    # Add x-coordinates channel for anti-aliasing.
14627    if $3 100%,1,1,1,x r. [0],[0] a[0,-1] c fi
14628
14629    # Calculate low matrix (backwards propagation).
14630    .
14631    repeat h
14632      +rows. {$<+1} erode. 3
14633      j.. .,0,$<,0,0,-1 rm.
14634    done
14635
14636    # Initialise seams, top matrix.
14637    100%,100% +rows[1] 0
14638    nm[1] grad nm[2] low nm[3] seam nm[4] top
14639
14640    repeat h#0-1 nr={$>+1}
14641      +rows[low] $nr
14642
14643      # Find optimum matches between two 1D matrices.
14644      +*[4,5] +shift[4] 1 *. [5] +shift[5] 1 *. [4]
14645      +[-2,-1] j[5] [4] a[-3--1] c
14646      f. ">if(c,i,max(j(-1)+j(0,0,0,1),j(-2)+j(0,0,0,2)))"
14647      s. c shift... 1 +.. ... shift... 1 +[-3,-1]
14648      >[-2,-1] f. "<if(j(1)<0,1,-i)"
14649
14650      # Add matched row to seams.
14651      j[seam] .,0,$>
14652
14653      # Distribute matched pixels in top matrix.
14654      a[-2,-1] c f. "j(i,0,0,-1)" channels. 1
14655
14656      # Add next energy row to top matrix.
14657      +rows[grad] $nr +[top,-1]
14658    done
14659
14660    # Add / remove seams.
14661    max={iM*2} repeat $sms =. $max,{xm} done
14662    j[grad] .,0,100% rm[low,top] a[-2,-1] c
14663    f. "<if(c,i,j(j(0,0,0,1),1,0,0,0,1))" channels. 0
14664    +[0] 0.1 !=. $max
14665    w={w} h={h} s={0,s}
14666    if $ssms<0 * discard 0 r {$w-$sms},$h,1,$s,-1  # Remove seams.
14667    elif $ssms>0 # Add seams.
14668      -. 2 s[0] c
14669      repeat $s if $><($s-1) . fi a[$>,-1] c done
14670      permute cxyz a c discard -1 f "if(i<0,j(0,-1),i)"
14671      r {$w+$sms},$h,1,$s,-1
14672    fi
14673
14674    # Perform anti-aliasing step.
14675    if $3
14676      s c,{1-s} g. x,1 round !=. 1
14677      (0.5,0.5) +convolve[0] . rm..
14678      j[0] .,0,0,0,0,1,[1] rm[-2,-1]
14679    fi
14680
14681    rprogress {a=w/$1;if(a<1,a*100,100/a)}
14682  while w!=$1
14683
14684#@cli shift : vx[%],_vy[%],_vz[%],_vc[%],_boundary_conditions,_interpolation={ 0=nearest_neighbor | 1=linear } : (+)
14685#@cli : Shift selected images by specified displacement vector.
14686#@cli : Displacement vector can be non-integer in which case linear interpolation should be chosen.
14687#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14688#@cli : Default value: 'boundary_conditions=0' and 'interpolation=0'.
14689#@cli : $ image.jpg +shift[0] 50%,50%,0,0,0 +shift[0] 50%,50%,0,0,1 +shift[0] 50%,50%,0,0,2
14690
14691#@cli shrink_x : size_x>=0
14692#@cli : Shrink selected images along the x-axis.
14693#@cli : $ image.jpg shrink_x 30
14694shrink_x : check "$1>=0"
14695  e[^-1] "Shrink image$? along the x-axis with size $1."
14696  repeat $! z[$>] $1,{$>,w-$1-1} done
14697
14698#@cli shrink_xy : size>=0
14699#@cli : Shrink selected images along the xy-axes.
14700#@cli : $ image.jpg shrink_xy 30
14701shrink_xy : check "$1>=0"
14702  e[^-1] "Shrink image$? along the xy-axes with size $1."
14703  repeat $! z[$>] $1,$1,{$>,w-$1-1},{$>,h-$1-1} done
14704
14705#@cli shrink_xyz : size>=0
14706#@cli : Shrink selected images along the xyz-axes.
14707shrink_xyz : check "$1>=0"
14708  e[^-1] "Shrink image$? along the xyz-axes with size $1."
14709  repeat $! z[$>] $1,$1,$1,{$>,w-$1-1},{$>,h-$1-1},{$>,d-$1-1} done
14710
14711#@cli shrink_y : size_y>=0
14712#@cli : Shrink selected images along the y-axis.
14713#@cli : $ image.jpg shrink_y 30
14714shrink_y : check "$1>=0"
14715  e[^-1] "Shrink image$? along the y-axis with size $1."
14716  repeat $! z[$>] 0,$1,100%,{$>,h-$1-1} done
14717
14718#@cli shrink_z : size_z>=0
14719#@cli : Shrink selected images along the z-axis.
14720shrink_z : check "$1>=0"
14721  e[^-1] "Shrink image$? along the z-axis with size $1."
14722  repeat $! z[$>] 0,0,$1,100%,100%,{$>,d-$1-1} done
14723
14724#@cli slices : z0[%],_z1[%]
14725#@cli : Keep only specified slices of selected images.
14726#@cli : Dirichlet boundary conditions are used when specified slices are out of range.
14727#@cli : Default value: 'z1=z0'.
14728slices : skip ${2=$1}
14729  e[^-1] "Keep slices $1...$2 of image$?."
14730  z 0,0,$1,100%,100%,$2
14731
14732#@cli sort : _ordering={ + | - },_axis={ x | y | z | c } : (+)
14733#@cli : Sort pixel values of selected images.
14734#@cli : If 'axis' is specified, the sorting is done according to the data of the first column/row/slice/channel
14735#@cli : of selected images.
14736#@cli : Default values: 'ordering=+' and 'axis=(undefined)'.
14737#@cli : $ 64 rand 0,100 +sort display_graph 400,300,3
14738
14739#@cli s : eq. to 'split'. : (+)
14740
14741#@cli split : { x | y | z | c }...{ x | y | z | c },_split_mode : \
14742# keep_splitting_values={ + | - },_{ x | y | z | c }...{ x | y | z | c },value1,_value2,... : (no arg) : (+)
14743#@cli : Split selected images along specified axes, or regarding to a sequence of scalar values
14744#@cli : (optionally along specified axes too).
14745#@cli : (eq. to 's').\n
14746#@cli : 'split_mode' can be { 0=split according to constant values | >0=split in N parts | \
14747# <0=split in parts of size -N }.
14748#@cli : Default value: 'split_mode=-1'.
14749#@cli : $ image.jpg split c
14750#@cli : $ image.jpg split y,3
14751#@cli : $ image.jpg split x,-128
14752#@cli : $ 1,20,1,1,"1,2,3,4" +split -,2,3 append[1--1] y
14753#@cli : $ (1,2,2,3,3,3,4,4,4,4) +split x,0 append[1--1] y
14754
14755#@cli split_tiles : M!=0,_N!=0,_is_homogeneous={ 0 | 1 }
14756#@cli : Split selected images as a MxN array of tiles.
14757#@cli : If M or N is negative, it stands for the tile size instead.
14758#@cli : Default values: 'N=M' and 'is_homogeneous=0'.
14759#@cli : $ image.jpg +local split_tiles 5,4 blur 3,0 sharpen 700 append_tiles 4,5 endlocal
14760split_tiles : skip ${2=$1},${3=0}
14761  if $3 e[^-1] "Split image$? as a $1x$2 array of homogeneous tiles."
14762  else e[^-1] "Split image$? as a $1x$2 array of tiles."
14763  fi
14764  repeat $! l[$<] s y,$2 s x,$1 if $3 r [0],[0],100%,100%,0 fi endl done
14765
14766#@cli undistort : -1<=_amplitude<=1,_aspect_ratio,_zoom,_center_x[%],_center_y[%],_boundary_conditions
14767#@cli : Correct barrel/pincushion distortions occurring with wide-angle lens.
14768#@cli : References:
14769#@cli : [1] Zhang Z. (1999). Flexible camera calibration by viewing a plane from unknown orientation.
14770#@cli : [2] Andrew W. Fitzgibbon (2001). Simultaneous linear estimation of multiple view geometry and lens distortion.
14771#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14772#@cli : Default values: 'amplitude=0.25', 'aspect_ratio=0', 'zoom=0', 'center_x=center_y=50%' \
14773# and 'boundary_conditions=0'.
14774undistort : check "${1=0.1}>=-1 && $1<=1 && ${6=0}>=0 && $6<=3" skip ${2=0},${3=0},${4=50%},${5=50%}
14775  e[^-1] "Undistort barrel/pincushion effect in image$?, with amplitude $1, aspect ratio $2, zoom factor $3,
14776          center ($4,$5) and "${"arg 1+$6,dirichlet,neumann,periodic,mirror"}" boundary conditions."
14777  repeat $! l[$>]
14778    center_x={${"is_percent $4"}?w*$4:$4}
14779    center_y={${"is_percent $5"}?h*$5:$5}
14780    f "
14781      const interpolation = 1;
14782      const boundary = $6;
14783      const center_x = "$center_x";
14784      const center_y = "$center_y";
14785      const alpha = cut($1,-0.999,0.999);
14786      const ratio = $2>=0?1+$2:1/(1-$2);
14787      const zoom = $3>=0?1+$3:1/(1-$3);
14788      const M = max(w,h);
14789      x = 2*(x - center_x)/(zoom*ratio*M);
14790      y = 2*(y - center_y)/(zoom*M);
14791      r = norm(x,y);
14792      nr = r/(1 - alpha*r^2);
14793      if (r>0,
14794        nx = nr/r*x; ny = nr/r*y,
14795        nx = x; ny = y
14796      );
14797      x = 0.5*nx*ratio*M + center_x;
14798      y = 0.5*ny*M + center_y;
14799      I(x,y)"
14800  endl done
14801
14802#@cli y : eq. to 'unroll'. : (+)
14803
14804#@cli unroll : _axis={ x | y | z | c } : (+)
14805#@cli : Unroll selected images along specified axis.
14806#@cli : (eq. to 'y').
14807#@cli : Default value: 'axis=y'.
14808#@cli : $ (1,2,3;4,5,6;7,8,9) +unroll y
14809
14810#@cli upscale_smart : width[%],_height[%],_depth,_smoothness>=0,_anisotropy=[0,1],sharpening>=0
14811#@cli : Upscale selected images with an edge-preserving algorithm.
14812#@cli : Default values: 'height=100%', 'depth=100%', 'smoothness=2', 'anisotropy=0.4' and 'sharpening=10'.
14813#@cli : $ image.jpg resize2dy 100 +upscale_smart 500%,500% append x
14814upscale_smart : skip ${2=100%},${3=100%} check "${4=2}>=0 && ${5=0.4}>=0 && $5<=1 && ${6=10}>=0"
14815  e[^-1] "Upscale image$? to $1x$2x$3, with smoothness $4, anisotropy $5 and sharpening $6."
14816  repeat $! l[$>]
14817    w={w} h={h}
14818    +r. $1,$2,$3,1,0 # Compute desired dimensions.
14819    if w<$w" && "h<$h # Test for downscaling
14820      rm. r. $1,$2,$3,100%,2
14821    else
14822      rm. +diffusiontensors 0,$5,1.2,1.2
14823      r[-2,-1] $1,$2,$3,100%,5
14824      smooth.. .,$4 rm.
14825      ac "sharpen. $6,10",ycbcr_y
14826    fi
14827  endl done
14828
14829#@cli warp : [warping_field],_mode,_interpolation,_boundary_conditions,_nb_frames>0 : (+)
14830#@cli : Warp selected images with specified displacement field.
14831#@cli : 'mode' can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=forward-relative }.
14832#@cli : 'interpolation' can be { 0=nearest-neighbor | 1=linear | 2=cubic }.
14833#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14834#@cli : Default values: 'mode=0', 'interpolation=1', 'boundary_conditions=1' and 'nb_frames=1'.
14835#@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))' \
14836# warp[-2] [-1],1,1,0 quiver[-1] [-1],10,1,1,1,100
14837#@cli : $$ https://gmic.eu/oldtutorial/_warp
14838
14839#@cli warp_patch : [warping_field],patch_width>=1,_patch_height>=1,_patch_depth>=1,_std_factor>0,_boundary_conditions.
14840#@cli : Patch-warp selected images, with specified 2D or 3D displacement field (in backward-absolute mode).
14841#@cli : Argument 'std_factor' sets the std of the gaussian weights for the patch overlap,
14842#@cli : equal to 'std = std_factor*patch_size'.
14843#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14844#@cli : Default values: 'std_factor=0.3' and 'boundary_conditions=3'.
14845warp_patch : check ${is_image_arg\ $1}" && isint(${2=3}) && $2>=1 && isint(${3=$2}) && $3>=1 &&
14846                    isint(${4=1}) && $4>=1 && isnum(${5=0.3}) && $5>0 && isint(${6=3}) && $6>=0 && $6<=3"
14847  e[^-1] "Patch-warp image$? with backward-absolute displacement field $1, using $2x$3x$4 patches, std factor $5
14848          and "${"arg 1+$6,dirichlet,neumann,periodic,mirror"}" boundary conditions."
14849  if $2<=1 pass$1 warp[^-1] .,0 rm. return fi
14850  repeat $! pass$1 l[$>,-1] nm={0,n}
14851    [0],[0],[0],1,1 a[0,-1] c # Add weighting channel
14852    100%,100%,100%,{0,s}
14853
14854    if s#1>=3 # 3D version
14855      eval[1] ">
14856        begin(
14857          const pw = $2;
14858          const ph = $3;
14859          const pd = $4;
14860          const stdf = $5;
14861          const boundary = $6;
14862          const pw1 = int(pw/2);
14863          const pw2 = pw - pw1 - 1;
14864          const ph1 = int(ph/2);
14865          const ph2 = ph - ph1 - 1;
14866          const pd1 = int(pd/2);
14867          const pd2 = pd - pd1 - 1;
14868          const pwhd = pw*ph*pd;
14869          return = vector(s);
14870
14871          # Pre-compute gaussian weights.
14872          if (stdf<5,
14873            ref(vectorpwhd(),weights);
14874            offw = 0;
14875            for (zw = -pd1, zw<=pd2, ++zw,
14876              for (yw = -ph1, yw<=ph2, ++yw,
14877                for (xw = -pw1, xw<=pw2, ++xw,
14878                  weights[offw++] = exp(-xw^2/(2*(stdf*pw)^2) - yw^2/(2*(stdf*ph)^2) - zw^2/(2*(stdf*pd)^2));
14879                );
14880              );
14881            );
14882          );
14883        );
14884        u = i(x,y,z,0);
14885        v = i(x,y,z,1);
14886        w = i(x,y,z,2);
14887        ref(crop(#0,u - pw1, v - ph1,w - pd1,pw,ph,pd,boundary),patch);
14888        stdf<5?
14889          draw(#2,patch,x - pw1,y - ph1,z - pd1,0,pw,ph,pd,s#0,-1,weights):
14890          draw(#2,patch,x - pw1,y - ph1,z - pd1,0,pw,ph,pd,s#0,-1);
14891        return"
14892    else # 2D version
14893      eval[1] ">
14894        begin(
14895          const pw = $2;
14896          const ph = $3;
14897          const stdf = $5;
14898          const boundary = $6;
14899
14900          const pw1 = int(pw/2);
14901          const pw2 = pw - pw1 - 1;
14902          const ph1 = int(ph/2);
14903          const ph2 = ph - ph1 - 1;
14904          const pwh = pw*ph;
14905          return = vector(s);
14906
14907          # Pre-compute gaussian weights.
14908          if (stdf<5,
14909            ref(vectorpwh(),weights);
14910            offw = 0;
14911            for (yw = -ph1, yw<=ph2, ++yw,
14912              for (xw = -pw1, xw<=pw2, ++xw,
14913                weights[offw++] = exp(-xw^2/(2*(stdf*pw)^2) - yw^2/(2*(stdf*ph)^2));
14914              );
14915            );
14916          );
14917        );
14918        u = i(x,y,z,0);
14919        v = i(x,y,z,1);
14920        ref(crop(#0,u - pw1, v - ph1,pw,ph,boundary),patch);
14921        stdf<5?
14922          draw(#2,patch,x - pw1,y - ph1,0,0,pw,ph,1,s#0,-1,weights):
14923          draw(#2,patch,x - pw1,y - ph1,0,0,pw,ph,1,s#0,-1);
14924        return"
14925    fi
14926    s. c,-{0,s-1} /[-2,-1] k. nm $nm
14927  endl done
14928
14929#@cli warp_rbf : xs0[%],ys0[%],xt0[%],yt0[%],...,xsN[%],ysN[%],xtN[%],ytN[%]
14930#@cli : Warp selected images using RBF-based interpolation.
14931#@cli : Each argument (xsk,ysk)-(xtk,ytk) corresponds to the coordinates of a keypoint
14932#@cli : respectively on the source and target images. The set of all keypoints define the overall image deformation.
14933#@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%
14934warp_rbf :
14935  e[^-1] "Warp image$? using RBF interpolation, with keypoints ($*)."
14936  $=arg N={$#/4}
14937  if int($N)!=$N error[0--2] "Command 'warp_rbf': Wrong number of arguments ($#)." fi
14938  repeat $! l[$>]
14939    # Retrieve absolute keypoints coordinates.
14940    4,$N
14941    repeat wh a=${arg{1+$>}} isp=${"is_percent "$a} eval i[$>]=$isp?($>%2?w#0:h#0)*$a:$a done
14942    s. x,2 -. .. a[-2,-1] x permute. yzcx
14943
14944    # Generate warping field by RBF interpolation.
14945    rbf. {0,[w,h]} warp[0] .,1,1,3 rm.
14946  endl done
14947
14948#---------------------------------
14949#
14950#@cli :: Filtering
14951#
14952#---------------------------------
14953
14954#@cli bandpass : _min_freq[%],_max_freq[%]
14955#@cli : Apply bandpass filter to selected images.
14956#@cli : Default values: 'min_freq=0' and 'max_freq=20%'.
14957#@cli : $ image.jpg bandpass 1%,3%
14958#@cli : $$ https://gmic.eu/oldtutorial/_bandpass
14959bandpass : skip ${1=0},${2=20%}
14960  e[^-1] "Apply bandpass filter [$1,$2] to image$?."
14961  repeat $! l[$>]
14962    100%,100%,100% f. "sqrt((x/w-0.5)^2 + (y/h-0.5)^2 + (z/d-0.5)^2)"
14963    n. 0,1 ir. $1,$2 shift. {int(w/2)},{int(h/2)},{int(d/2)},0,2
14964    fft.. *... . *[-2,-1] ifft rm.
14965  endl done
14966
14967#@cli bilateral : [guide],std_deviation_s[%]>=0,std_deviation_r[%]>=0,_sampling_s>=0,_sampling_r>=0 : \
14968# std_deviation_s[%]>=0,std_deviation_r[%]>=0,_sampling_s>=0,_sampling_r>=0 : (+)
14969#@cli : Blur selected images by anisotropic (eventually joint/cross) bilateral filtering.
14970#@cli : If a guide image is provided, it is used for drive the smoothing filter.
14971#@cli : A guide image must be of the same xyz-size as the selected images.
14972#@cli : Set 'sampling' arguments to '0' for automatic adjustment.
14973#@cli : $ image.jpg repeat 5 bilateral 10,10 done
14974
14975#@cli b : eq. to 'blur'. : (+)
14976
14977#@cli blur : std_deviation>=0[%],_boundary_conditions,_kernel : \
14978# axes,std_deviation>=0[%],_boundary_conditions,_kernel : (+)
14979#@cli : Blur selected images by a deriche or gaussian filter (recursive implementation).
14980#@cli : (eq. to 'b').\n
14981#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
14982#@cli : 'kernel' can be { 0=deriche | 1=gaussian }.
14983#@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }.
14984#@cli : Specifying one axis multiple times apply also the blur multiple times.
14985#@cli : Default values: 'boundary_conditions=1' and 'kernel=1'.
14986#@cli : $ image.jpg +blur 5,0 +blur[0] 5,1
14987#@cli : $ image.jpg +blur y,10%
14988#@cli : $$ https://gmic.eu/oldtutorial/_blur
14989
14990#@cli blur_angular : amplitude[%],_center_x[%],_center_y[%]
14991#@cli : Apply angular blur on selected images.
14992#@cli : Default values: 'center_x=center_y=50%'.
14993#@cli : $ image.jpg blur_angular 2%
14994#@cli : $$ https://gmic.eu/oldtutorial/_blur_angular
14995blur_angular : skip ${2=50%},${3=50%}
14996  e[^-1] "Apply angular blur on image$?, with amplitude $1 and center point ($2,$3)."
14997  euclidean2polar $2,$3,1.3,1
14998  repeat $! l[$>] 1,100% =. 1,50%,50% b. y,$1 convolve_fft.. . rm. endl done
14999  polar2euclidean $2,$3,1.3,1
15000
15001#@cli blur_bloom : _amplitude>=0,_ratio>=0,_nb_iter>=0,_blend_operator={ + | max | min },\
15002# _kernel={ 0=deriche | 1=gaussian | 2=box | 3=triangle | 4=quadratic },\
15003# _normalize_scales={ 0 | 1 },_axes
15004#@cli : Apply a bloom filter that blend multiple blur filters of different radii,
15005#@cli : resulting in a larger but sharper glare than a simple blur.
15006#@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }.
15007#@cli : Specifying one axis multiple times apply also the blur multiple times.
15008#@cli : Reference: Masaki Kawase, "Practical Implementation of High Dynamic Range Rendering", GDC 2004.
15009#@cli : Default values: 'amplitude=1', 'ratio=2', 'nb_iter=5', 'blend_operator=+', 'kernel=1', 'normalize_scales=0' \
15010# and 'axes=(all)'
15011#@cli : $ image.jpg blur_bloom ,
15012blur_bloom : check "${1=1}>=0 && ${2=2}>=0 && isint(${3=5}) && $3>=0 && isint(${5=1}) && $5>=0 && $5<=4 &&
15013                    isnum(${6=0})" skip "${4=+},${7=}"
15014  e[^-1] "Apply bloom effect on image$?, with amplitude $1, ratio $2, $3 iterations, blend operator '$4' and "\
15015         ${"arg 1+!$6,\"\",\"no \""}"scale normalization."
15016  if narg("$7") axes=$7, fi
15017  m "_bloom0 : b "$axes"$""1"
15018  m "_bloom1 : b "$axes"$""1,1,1"
15019  m "_bloom2 : boxfilter "$axes"{1+2*$""1},0,1"
15020  m "_bloom3 : boxfilter "$axes"{1+2*$""1},0,1,2"
15021  m "_bloom4 : boxfilter "$axes"{1+2*$""1},0,1,3"
15022  repeat $! l[$>] nm={n} mM={[im,iM]}
15023    [0] repeat $3 sigma={$1*($2^$>)} +_bloom$5[0] $sigma
15024    if $6 n. $mM fi
15025    -$4[1,-1]
15026    done
15027    n. $mM k. nm $nm
15028  endl done
15029  um _bloom0,_bloom1,_bloom2,_bloom3,_bloom4
15030
15031#@cli blur_linear : amplitude1[%],_amplitude2[%],_angle,_boundary_conditions={ 0=dirichlet | 1=neumann }
15032#@cli : Apply linear blur on selected images, with specified angle and amplitudes.
15033#@cli : Default values: 'amplitude2=0', 'angle=0' and 'boundary_conditions=1'.
15034#@cli : $ image.jpg blur_linear 10,0,45
15035#@cli : $$ https://gmic.eu/oldtutorial/_blur_linear
15036blur_linear : skip ${2=0},${3=0},${4=1}
15037  e[^-1] "Apply linear blur on image$?, with angle $3 deg. and amplitudes ($1,$2)."
15038  std1={if(${is_percent\ $1},$1*max(w,h),$1)}
15039  std2={if(${is_percent\ $2},$2*max(w,h),$2)}
15040  stdM={round(1.25*max($std1,$std2))}
15041  if $stdM<=0 return fi
15042  repeat $! l[$>]
15043    expand_xy $stdM,{$4!=0}
15044    {2*$stdM},{2*$stdM} gaussian. $1,$2,$3 normalize_sum.
15045    convolve_fft[0] [1] rm. shrink_xy $stdM
15046  endl done
15047
15048#@cli blur_radial : amplitude[%],_center_x[%],_center_y[%]
15049#@cli : Apply radial blur on selected images.
15050#@cli : Default values: 'center_x=center_y=50%'.
15051#@cli : $ image.jpg blur_radial 2%
15052#@cli : $$ https://gmic.eu/oldtutorial/_blur_radial
15053blur_radial : skip ${2=50%},${3=50%}
15054  e[^-1] "Apply radial blur on image$?, with amplitude $1 and center point ($2,$3)."
15055  euclidean2polar $2,$3,5,1 blur_x $1 polar2euclidean $2,$3,5,1
15056
15057#@cli blur_selective : sigma>=0,_edges>0,_nb_scales>0
15058#@cli : Blur selected images using selective gaussian scales.
15059#@cli : Default values: 'sigma=5', 'edges=0.5' and 'nb_scales=5'.
15060#@cli : $ image.jpg noise 20 cut 0,255 +local[-1] repeat 4 blur_selective , done endlocal
15061#@cli : $$ https://gmic.eu/oldtutorial/_blur_selective
15062blur_selective : check "${1=5}>=0 && ${2=0.5}>=0 && isint(${3=5}) && $3>0"
15063  e[^-1] "Blur image$? using $3 selective gaussian scales, with sigma $1 and edges $2."
15064  repeat $! l[$>] nm={0,n}
15065    +gradient_norm +. 1 ^. {-max(0.01,$2)} quantize. {$3+1},0,1 min. {$3-1} ri. ..
15066    repeat $3 +==. $> *. ... +[-2,-1] b.. {$1/($3+1)} done
15067  rm.. nm $nm endl done
15068
15069#@cli blur_x : amplitude[%]>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
15070#@cli : Blur selected images along the x-axis.
15071#@cli : Default value: 'boundary_conditions=1'.
15072#@cli : $ image.jpg +blur_x 6
15073#@cli : $$ https://gmic.eu/oldtutorial/_blur_x
15074blur_x : check "isint(${2=1}) && inrange($2,0,3)"
15075  e[^-1] "Blur image$? along the x-axis, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
15076  deriche $1,0,x,$2
15077
15078#@cli blur_xy : amplitude_x[%],amplitude_y[%],_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
15079#@cli : Blur selected images along the X and Y axes.
15080#@cli : Default value: 'boundary_conditions=1'.
15081#@cli : $ image.jpg +blur_xy 6
15082#@cli : $$ https://gmic.eu/oldtutorial/_blur_y
15083blur_xy : skip ${2=$1} check "isint(${3=1}) && inrange($3,0,3)"
15084  e[^-1] "Blur image$? along the xy-axes, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
15085  deriche $1,0,x,$3 deriche $2,0,y,$3
15086
15087#@cli blur_xyz : amplitude_x[%],amplitude_y[%],amplitude_z,\
15088# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
15089#@cli : Blur selected images along the X, Y and Z axes.
15090#@cli : Default value: 'boundary_conditions=1'.
15091#@cli : $$ https://gmic.eu/oldtutorial/_blur_xyz
15092blur_xyz : skip ${2=$1},${3=$1} check "isint(${4=1}) && inrange($4,0,3)"
15093  e[^-1] "Blur image$? along the xyz-axes, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
15094  deriche $1,0,x,$4 deriche $2,0,y,$4 deriche $3,0,z,$4
15095
15096#@cli blur_y : amplitude[%]>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
15097#@cli : Blur selected images along the y-axis.
15098#@cli : Default value: 'boundary_conditions=1'.
15099#@cli : $ image.jpg +blur_y 6
15100#@cli : $$ https://gmic.eu/oldtutorial/_blur_y
15101blur_y : check "isint(${2=1}) && inrange($2,0,3)"
15102  e[^-1] "Blur image$? along the y-axis, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
15103  deriche $1,0,y,$2
15104
15105#@cli blur_z : amplitude[%]>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
15106#@cli : Blur selected images along the z-axis.
15107#@cli : Default value: 'boundary_conditions=1'.
15108#@cli : $$ https://gmic.eu/oldtutorial/_blur_z
15109blur_z : check "isint(${2=1}) && inrange($2,0,3)"
15110  e[^-1] "Blur image$? along the z-axis, with sigma $1 and "${arg\ 1+!$2,neumann,dirichlet}" boundary conditions."
15111  deriche $1,0,z,$2
15112
15113#@cli boxfilter : size>=0[%],_order,_boundary_conditions,_nb_iter>=0 : \
15114# axes,size>=0[%],_order,_boundary_conditions,_nb_iter>=0 : (+)
15115#@cli : Blur selected images by a box filter of specified size (fast recursive implementation).
15116#@cli : 'order' can be { 0=smooth | 1=1st-derivative | 2=2nd-derivative }.
15117#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15118#@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }.
15119#@cli : Specifying one axis multiple times apply also the blur multiple times.
15120#@cli : Default values: 'order=0', 'boundary_conditions=1' and 'nb_iter=1'.
15121#@cli : $ image.jpg +boxfilter 5%
15122#@cli : $ image.jpg +boxfilter y,3,1
15123
15124#@cli bump2normal
15125#@cli : Convert selected bumpmaps to normalmaps.
15126#@cli : $ 300,300 circle 50%,50%,128,1,1 blur 5% bump2normal
15127bump2normal :
15128  e[^-1] "Convert bumpmap$? to normalmap."
15129  repeat $! l[$>]
15130    channels 0 g xy,1 +f. 1 a c orientation
15131    * 127 + 128 round c 0,255
15132  endl done
15133
15134#@cli compose_freq
15135#@cli : Compose selected low and high frequency parts into new images.
15136#@cli : $ image.jpg split_freq 2% mirror[-1] x compose_freq
15137compose_freq :
15138  e[^-1] "Compose low and high frequency part$? into new images."
15139  repeat int($!/2) +[$>,{$>+1}] done
15140
15141#@cli convolve : [mask],_boundary_conditions,_is_normalized={ 0 | 1 },_channel_mode,\
15142# _xcenter,_ycenter,_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,\
15143# _xdilation,_ydilation,_zdilation,interpolation_type : (+)
15144#@cli : Convolve selected images by specified mask.
15145#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15146#@cli : 'channel_mode' can be { 0=sum input channels | 1=one-for-one | 2=expand }.
15147#@cli : 'interpolation_type' can be { 0=nearest-neighbor | 1=linear }.
15148#@cli : Default values: 'boundary_conditions=1', 'is_normalized=0', 'channel_mode=1', \
15149# 'xcenter=ycenter=zcenter=-1' (-1=centered), 'xstart=ystart=zstart=0', 'xend=yend=zend=(max-coordinates)', \
15150# 'xstride=ystride=zstride=1', 'xdilation=ydilation=zdilation=1' and 'interpolation_type=0'.
15151#@cli : $ image.jpg (0,1,0;1,-4,1;0,1,0) convolve[-2] [-1] keep[-2]
15152#@cli : $ image.jpg (0,1,0) resize[-1] 130,1,1,1,3 +convolve[0] [1]
15153#@cli : $$ https://gmic.eu/oldtutorial/_convolve
15154
15155#@cli convolve_fft : [mask],_boundary_conditions
15156#@cli : Convolve selected images with specified mask, in the fourier domain.
15157#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15158#@cli : $ image.jpg 100%,100% gaussian[-1] 20,1,45 +convolve_fft[0] [1]
15159convolve_fft : check ${is_image_arg\ $1}" && isin(${2=2},0,1,2,3)"
15160  e[^-1] "Convolve image$? with mask $1, in the fourier domain."
15161  pass$1 store. kernel
15162  repeat $! l[$>] if w
15163    w0,h0,d0={[w,h,d]}
15164    $kernel
15165    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
15166    r ${-max_whd},100%,0,0,0.5,0.5 r 100%,100%,100%,${-max_s}
15167    fft[1] fft[0]
15168    +*[1,2] +*[0,3] +[-2,-1] *[1,3] *[0,2] -[0,1]
15169    ifft rm.
15170    shift {-int(([w,h,d]-1)/2)},0,2
15171    r $w0,$h0,$d0,100%,0,0,0.5,0.5
15172  fi endl done
15173
15174#@cli correlate : [mask],_boundary_conditions,_is_normalized={ 0 | 1 },_channel_mode,\
15175# _xcenter,_ycenter,_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,\
15176# _xdilation,_ydilation,_zdilation,interpolation_type : (+)
15177#@cli : Correlate selected images by specified mask.
15178#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15179#@cli : 'channel_mode' can be { 0=sum input channels | 1=one-for-one | 2=expand }.
15180#@cli : 'interpolation_type' can be { 0=nearest-neighbor | 1=linear }.
15181#@cli : Default values: 'boundary_conditions=1', 'is_normalized=0', 'channel_mode=1', \
15182# 'xcenter=ycenter=zcenter=-1' (-1=centered), 'xstart=ystart=zstart=0', 'xend=yend=zend=(max-coordinates)', \
15183# 'xstride=ystride=zstride=1', 'xdilation=ydilation=zdilation=1' and 'interpolation_type=0'.
15184#@cli : $ image.jpg (0,1,0;1,-4,1;0,1,0) correlate[-2] [-1] keep[-2]
15185#@cli : $ image.jpg +crop 40%,40%,60%,60% +correlate[0] [-1],0,1
15186
15187#@cli cross_correlation : [mask]
15188#@cli : Compute cross-correlation of selected images with specified mask.
15189#@cli : $ image.jpg +shift -30,-20 +cross_correlation[0] [1]
15190cross_correlation : check ${is_image_arg\ $1}
15191  e[^-1] "Compute cross-correlation of image$? with mask $1."
15192  repeat $! pass$1 0 l[$>,-1]
15193    norm fft.. fft. [-2,-1] *.. [-5] *. [-6]
15194    -[-2,-1] *[-5,-3] *[-3,-2] +[-3,-2] ifft rm.
15195  endl done
15196
15197#@cli curvature
15198#@cli : Compute isophote curvatures on selected images.
15199#@cli : $ image.jpg blur 10 curvature
15200curvature :
15201  e[^-1] "Compute isophote curvatures on image$?."
15202  repeat $! l[$>]
15203    if d==1
15204      +g xy,0 hessian... xxxyyy                          # ixx ixy iyy ix iy
15205      *... .. *[-4] . *[-4] -2                           # ixx -2iyixy ixiyy ix iy
15206      +[-4,-3] *... ..                                   # ixx -2ixiyixy+ix^2iyy ix iy
15207      sqr[-2,-1] *[-4] . +[-4,-3]                        # iy^2ixx-2ixiyixy+ix^2iyy ix^2 iy^2
15208      +[-2,-1] +. 0.1 ^. 1.5 /                           # (iy^2ixx+2ixiyixy+ix^2iyy)/(ix^2+iy^2)
15209    else
15210      +inn +gradient_norm.. laplacian...                 # inn+iee inn in
15211      -[-3,-2] +. 0.1 /[-2,-1]                           # iee in
15212      +inn. laplacian.. -                                # iee/in
15213    fi
15214  endl done
15215
15216#@cli dct : _{ x | y | z }...{ x | y | z } : (no arg)
15217#@cli : Compute the discrete cosine transform of selected images, optionally along the specified axes only.
15218#@cli : Output images are always evenly sized, so this command may change the size of the selected images.
15219#@cli : Default values: (no arg)
15220#@cli : See also: ''idct''.
15221#@cli : $ image.jpg +dct +idct[-1] abs[-2] +[-2] 1 log[-2]
15222#@cli : $$ https://gmic.eu/oldtutorial/_dct-and-idct
15223dct : skip ${1=0}
15224  ('"$1"')
15225  is_axes={im>=_'x'" && "iM<=_'z'}
15226  if $is_axes
15227    e[0--3] "Compute discrete cosine transform of image$? along axes '$1'."
15228    repeat w
15229      axis={i[$>]}
15230      if $axis==_'x' repeat $!-1 l[$>] if w>1 _dct fi endl done
15231      elif $axis==_'y' repeat $!-1 l[$>] if h>1 permute yxzc _dct permute yxzc fi endl done
15232      elif $axis==_'z' repeat $!-1 l[$>] if d>1 permute zxyc _dct permute yzxc fi endl done
15233      fi
15234    done
15235    rm.
15236  else
15237    rm.
15238    e[0--3] "Compute discrete cosine transform of image$?."
15239    noarg
15240    repeat $! l[$>]
15241      if w>1 _dct fi
15242      if h>1 permute yxzc _dct permute yxzc fi
15243      if d>1 permute zxyc _dct permute yzxc fi
15244    endl done
15245  fi
15246
15247# 1D direct transform (DCT-II) along the x-axis, for a single image.
15248_dct :
15249  if w%2 r {w+1},100%,100%,100%,0,1 fi
15250  s x l[1--1:2] a x mirror x endl mv[1] $! a x
15251  fft x
15252  100%,1,1,1,2*cos(-x*pi/(2*w)) *[0,2]
15253  100%,1,1,1,2*sin(-x*pi/(2*w)) *[1,2]
15254  -
15255  +z[0] 0,0 /. {sqrt(2)} j.. .,0,0,0 rm. * {sqrt(2/w)}  # Make the transform orthogonal.
15256
15257#@cli deblur : amplitude[%]>=0,_nb_iter>=0,_dt>=0,_regul>=0,_regul_type={ 0=Tikhonov | 1=meancurv. | 2=TV }
15258#@cli : Deblur image using a regularized Jansson-Van Cittert algorithm.
15259#@cli : Default values: 'nb_iter=10', 'dt=20', 'regul=0.7' and 'regul_type=1'.
15260#@cli : $ image.jpg blur 3 +deblur 3,40,20,0.01
15261deblur : check "${2=10}>=0 && ${3=20}>=0 && ${4=0.7}>=0" skip ${5=1}
15262  e[^-1] "Deblur image$? with a regularized Jansson-Van Cittert algorithm, with sigma $1, $2 iterations,
15263          time step $3 and regularization $4."
15264  repeat $! l[$>] nm={0,n}
15265    [0]
15266    repeat $2
15267      if $5>=2 +curvature.                         # TV regularization.
15268      elif $5>=1 +iee.                             # Meancurv. regularization.
15269      else +laplacian.                               # Tikhonov regularization.
15270      fi
15271      *. $4
15272      +b.. $1 -. [-4]                                # Data fidelity term.
15273      -[-2,-1]
15274      *. {$3/(0.0001+max(abs(im),abs(iM)))}          # Adaptive time step.
15275      +[-2,-1]                                       # Update image.
15276      done
15277    rm..
15278  nm $nm endl done
15279
15280#@cli deblur_goldmeinel : sigma>=0,_nb_iter>=0,_acceleration>=0,_kernel_type={ 0=deriche | 1=gaussian }.
15281#@cli : Deblur selected images using Gold-Meinel algorithm
15282#@cli : Default values: 'nb_iter=8', 'acceleration=1' and 'kernel_type=1'.
15283#@cli : $ image.jpg +blur 1 +deblur_goldmeinel[-1] 1
15284###### : (contribution from Jérôme Boulanger).
15285deblur_goldmeinel : check "$1>=0 && ${2=8}>=0 && ${3=1}>=0" skip ${4=1}
15286  e[^-1] "Deblur image$? using Gold-Meinel algorithm, with sigma $1, $2 iterations, acceleration $3 and "\
15287         ${arg\ 1+!$4,"",quasi-}"gaussian kernel."
15288  repeat $! l[$>]
15289    [0] repeat $2
15290      +b. $1,1,$4 +/[0,-1] rm.. ^. $3 *[-1,-2] # u *= f / Hu
15291    done rm[0]
15292  endl done
15293
15294#@cli deblur_richardsonlucy : sigma>=0, nb_iter>=0, _kernel_type={ 0=deriche | 1=gaussian }.
15295#@cli : Deblur selected images using Richardson-Lucy algorithm.
15296#@cli : Default values: 'nb_iter=50' and 'kernel_type=1'.
15297#@cli : $ image.jpg +blur 1 +deblur_richardsonlucy[-1] 1
15298###### : (contribution from Jérôme Boulanger).
15299deblur_richardsonlucy : check "$1>=0 && ${2=50}>=0" skip ${3=1}
15300  e[^-1] "Deblur image$? using Richardson-Lucy algorithm, with sigma $1, $2 iterations and "\
15301         ${arg\ 1+!$3,"",quasi-}"gaussian kernel."
15302  repeat $! l[$>]
15303    [0] repeat $2
15304      +b. $1,1,{$3!=0} max. 1e-6 +/[0,-1] rm.. b. $1,1,{$3!=0} *[-1,-2] # u *= H ( f / Hu )
15305    done rm[0]
15306  endl done
15307
15308#@cli deconvolve_fft :  [kernel],_regularization>=0
15309#@cli : Deconvolve selected images by specified mask in the fourier space.
15310#@cli : Default value: 'regularization>=0'.
15311#@cli : $ image.jpg +gaussian 5 +convolve_fft[0] [1] +deconvolve_fft[-1] [1]
15312deconvolve_fft : check ${is_image_arg\ $1}" && ${2=.001}>=0"
15313  e[^-1] "Deconvolve image$? with mask $1 and regularization $2, in the fourier domain."
15314  repeat $! pass$1 0 l[$>,-1]
15315    w2={0,int(w/2)} h2={0,int(h/2)} d2={0,int(d/2)}
15316    r[1] [0],[0],[0],1,0,0,0.5,0.5,0.5,0.5 shift[1] -$w2,-$h2,-$d2,0,2
15317    fft[0] fft[2]                 # a b a' b'
15318    +l[-1,-2] sqr + + $2 endl     # a b a' b' (a'^2+b'^2+eps)
15319    +*[-4] ...                    # a b a' b' (a'^2+b'^2) ba'
15320    +*[-6] ...                    # a b a' b' (a'^2+b'^2) ba' ab'
15321    -[-2,-1]                      # a b a' b' (a'^2+b'^2) ba'-ab'
15322    *[-6,-4]                      # aa' b b' (a'^2+b'^2) ba'-ab'
15323    *[-4,-3]                      # aa' bb' (a'^2+b'^2) ba'-ab'
15324    +[-4,-3]                      # aa'+bb' (a'^2+b'^2) ba'-ab'
15325    /. .. /[-3,-2]                # divide (aa'+bb') and (ba'-ab') by (a'^2+b'^2)
15326    ifft rm.
15327  endl done
15328
15329#@cli deinterlace : _method={ 0 | 1 }
15330#@cli : Deinterlace selected images ('method' can be { 0=standard or 1=motion-compensated }).
15331#@cli : Default value: 'method=0'.
15332#@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
15333deinterlace : skip ${1=0}
15334  e[^-1] "Deinterlace image$? with "${arg\ 1+!$1,motion-compensated,standard}" method."
15335  repeat $! l[$>]
15336    wh={w},{h}
15337    s y a[0--1:2] y a[^0] y ri.. .,0 r 100%,200%,1,100%,5
15338    if $1!=0 +displacement. ..,0.05 warp... .,1,1,1 rm. fi
15339    + / 2 c 0,255 r $wh
15340  endl done
15341
15342#@cli denoise : [guide],std_deviation_s[%]>=0,_std_deviation_r[%]>=0,_patch_size>0,_lookup_size>0,_smoothness,\
15343# _fast_approx={ 0 | 1 } : \
15344# std_deviation_s[%]>=0,_std_deviation_r[%]>=0,_patch_size>0,_lookup_size>0,_smoothness,\
15345# _fast_approx={ 0 | 1 } : (+)
15346#@cli : Denoise selected images by non-local patch averaging.
15347#@cli : Default values: 'std_deviation_p=10', 'patch_size=5', 'lookup_size=6' and 'smoothness=1'.
15348#@cli : $ image.jpg +denoise 5,5,8
15349
15350#@cli denoise_haar : _threshold>=0,_nb_scales>=0,_cycle_spinning>0
15351#@cli : Denoise selected images using haar-wavelet thresholding with cycle spinning.
15352#@cli : Set 'nb_scales==0' to automatically determine the optimal number of scales.
15353#@cli : Default values: 'threshold=1.4', 'nb_scale=0' and 'cycle_spinning=10'.
15354#@cli : $ image.jpg noise 20 cut 0,255 +denoise_haar[-1] 0.8
15355denoise_haar : check "${1=1.4}>=0 && isint(${2=0}) && $2>=0 && isint(${3=10}) && $3>0"
15356  e[^-1] "Denoise image$? using haar-wavelet thresholding, with threshold $1, "\
15357          ${arg\ 1+($2>0),auto,$2}" scales and $3 spinning cycles."
15358  repeat $! l[$>] nm={0,n}
15359    nb_scales={min(if($2,$2,32),int(log2(min(w,h))-1))}
15360    w={w} h={h} d={d} sigma=${-std_noise}
15361    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
15362    +f 0
15363    repeat $3
15364      dx={round(u(0,{4*$nb_scales}))}
15365      dy={round(u(0,{4*$nb_scales}))}
15366      dz={if($d==1,0,round(u(0,{4*$nb_scales})))}
15367      +shift[0] $dx,$dy,$dz,0,2
15368      haar. $nb_scales
15369      threshold. {$1*$sigma},1
15370      ihaar. $nb_scales
15371      shift. {-$dx},{-$dy},{-$dz},0,2
15372      +[-2,-1]
15373    done
15374    rm[0] / $3 r $w,$h,$d,100%,0
15375  nm $nm endl done
15376
15377#@cli denoise_cnn : _noise_type={ 0=soft | 1=heavy | 2=heavy (faster) | 3=poisson+gaussian },_patch_size>0
15378#@cli : Denoise selected images using a convolutional neural network (CNN).
15379#@cli : Input value range should be [0,255]. Output value range is [0,255].
15380#@cli : Default value: 'patch_size=64'.
15381#@cli : $ image.jpg noise 20 cut 0,255 +denoise_cnn
15382denoise_cnn : skip "${1=0},${2=64}"
15383  N=$!
15384  if isint($1)" && "inrange($1,0,3) check "isint($2) && $2>0" type=$1 else type=0 noarg fi
15385  s0,s1,s2,s3=soft,heavy,heavy,poisson+gaussian
15386  e[^-1] "Denoise image$? using a convolutional neural network (for "${s$type}"-noise), with patch size $2."
15387
15388  # Load denoising network.
15389  if 0${_is_denoise_cnn_$type} ${_denoise_cnn_$type}
15390  else l[]
15391    l[] input_cached gmic_denoise_cnn.gmz
15392    onfail error[0--5] "Command 'denoise_cnn': Unable to load network file 'gmic_denoise_cnn.gmz'."
15393    endl
15394    k[$type] unserialize +store _denoise_cnn_$type _is_denoise_cnn_$type=1
15395  endl fi
15396  ps=$2 replace_str. "nn_layer_input X,32,32,1,3","nn_layer_input X,"$ps","$ps",1,3" # Set patch size
15397  run {t} rm[-2,-1]
15398
15399  # Process images.
15400  repeat $N l[$>,$N--1] to_color[0]
15401    if $type==3 srgb2rgb[0] fi
15402
15403    # Extract overlapping psxps tiles.
15404    nx,ny={0,ceil([w,h]/($ps-6))}
15405    {0,[$ps,$ps,$nx*$ny,s]}
15406    1,1,{d},1,":
15407      ix = z%$nx;
15408      iy = int(z/$nx);
15409      X = $nx<2?-2:round(lerp(-2,w#0 - $ps + 2,ix/($nx - 1)));
15410      Y = $ny<2?-2:round(lerp(-2,h#0 - $ps + 2,iy/($ny - 1)));
15411      V = crop(#0,X,Y,0,0,$ps,$ps,1,3,3);
15412      draw(#-1,V,0,0,z,0,$ps,$ps,1,3)"
15413    rm.
15414
15415    # Denoise tiles.
15416    1,1,{d},1,*${-nn_lib}"X = crop(#-1,0,0,z,0,$ps,$ps,1,3);"${_nn_forward}"draw(#-1,OUT0_out,0,0,z,0,$ps,$ps,1,3);0"
15417    rm. c. 0,255
15418
15419    # Reconstruct image from overlapping tiles.
15420    {0,[w,h,1,4]}
15421    1,1,{-2,d},1,">begin(
15422        mask = convolve(vector(#($ps - 4)*($ps - 4),1),
15423                        $ps - 4,$ps - 4,1,1,
15424                        [1],1,1,1,1,
15425                        0,0,1,
15426                        0,0,0,
15427                        -2,-2,0,$ps - 4 - 1 + 2,$ps - 4 - 1 + 2,0);
15428      );
15429      ix = z%$nx;
15430      iy = int(z/$nx);
15431      X = $nx<2?-2:round(lerp(-2,w#0 - $ps + 2,ix/($nx - 1)));
15432      Y = $ny<2?-2:round(lerp(-2,h#0 - $ps + 2,iy/($ny - 1)));
15433      V = crop(#-2,0,0,z,0,$ps,$ps,1,3);
15434      draw(#-1,V,X,Y,0,0,$ps,$ps,1,3,-1,mask);
15435      draw(#-1,mask,X,Y,0,3,$ps,$ps,1,1,-1,mask)"
15436    rm[-3,-1]
15437    l. s c,-3 max. 1 / if $type==3 rgb2srgb. fi c 0,255 endl
15438    j[0] . rm.
15439
15440  endl done
15441  rm[$N--1]
15442
15443#@cli denoise_patchpca : _strength>=0,_patch_size>0,_lookup_size>0,_spatial_sampling>0
15444#@cli : Denoise selected images using the patch-pca algorithm.
15445#@cli : Default values: 'patch_size=7', 'lookup_size=11', 'details=1.8' and 'spatial_sampling=5'.
15446#@cli : $ image.jpg +noise 20 cut[-1] 0,255 +denoise_patchpca[-1] ,
15447denoise_patchpca : check "${1=1.8} && $1>=0 && isint(${2=7}) && $2>0 && isint(${3=11}) && $3>0 && isint(${4=5}) && $4>0"
15448  e[^-1] "Denoise image$? using patch-pca, with strength $1, patch size $2, lookup size $3 and spatial sampling $4."
15449  repeat $! l[$>] nm={n}
15450    N2={$2*$2} M2={$3*$3} stdnoise=${-std_noise}
15451    100%,100%,1,100% nm. aggreg
15452    100%,100% nm. weights
15453    f[0] "*
15454    begin(
15455      n1 = int($2/2); n2 = $2 - n1 - 1;
15456      m1 = int($3/2); m2 = $3 - m1 - 1;
15457      patch(x,y) = crop(x - n1,y - n1,0,c,$2,$2,1,1,1);
15458      ngauss(x) = exp(-x*x/(2*n1*n1));
15459      ref(vector"$N2"(0),zero);
15460      ref(vector"$N2"(0),mask);
15461      for (l = 0; q = -n1, q<=n2, ++q,
15462        for (p = -n1, p<=n2, ++p, mask[l++] = ngauss(p)*ngauss(q)
15463        )
15464      )
15465    );
15466
15467    if (!(x%$4) && !(y%$4),  # Sub-sampling
15468      ref(patch(x,y),X);
15469
15470      # Build correlation matrix for PCA.
15471      ref(vector"{$N2*$N2}"(0),M);
15472      for (q = -m1, q<=m2, ++q,
15473        for (p = -m1, p<=m2, ++p,
15474          ref(patch(x + p,y + q) - X,Xk);
15475          M += mul(Xk,Xk,"$N2");
15476        )
15477      );
15478      M/="$M2";
15479      eig = eig(M);
15480      lambda = sqrt(abs(eig[0,"$N2"]));
15481
15482      # Determine number of lambdas to keep and project neighboring patches.
15483      for (k = 0, k<size(lambda) && lambda[k]>=$1*"$stdnoise", ++k);
15484      Qt = eig["$N2","{$N2*$N2}"];
15485      Q = transpose(Qt,"$N2");
15486      for (q = -m1, q<=m2, ++q,
15487        for (p = -m1, p<=m2, ++p,
15488          pY = Qt*(patch(x + p,y + q) - X);
15489          copy(pY[k],zero[0],size(pY) - k);
15490          (Y = Q*pY)+=X;
15491          draw(#"$aggreg",Y,x + p - n1,y + q - n1,0,c,$2,$2,1,1,-1,mask);
15492          draw(#"$weights",mask,x + p - n1,y + q - n1,0,c,$2,$2,1,1,-1);
15493        )
15494      );
15495    0);0"
15496    max[weights] 0.01 /[aggreg,weights] k[aggreg] nm $nm
15497  endl done
15498
15499#@cli deriche : std_deviation>=0[%],order={ 0 | 1 | 2 },axis={ x | y | z | c },_boundary_conditions : (+)
15500#@cli : Apply Deriche recursive filter on selected images, along specified axis and with
15501#@cli : specified standard deviation, order and boundary conditions.
15502#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15503#@cli : Default value: 'boundary_conditions=1'.
15504#@cli : $ image.jpg deriche 3,1,x
15505#@cli : $ image.jpg +deriche 30,0,x deriche[-2] 30,0,y add
15506#@cli : $$ https://gmic.eu/oldtutorial/_deriche
15507
15508#@cli dilate : size>=0 : size_x>=0,size_y>=0,size_z>=0 : \
15509# [kernel],_boundary_conditions,_is_real={ 0=binary-mode | 1=real-mode } : (+)
15510#@cli : Dilate selected images by a rectangular or the specified structuring element.
15511#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15512#@cli : Default values: 'size_z=1', 'boundary_conditions=1' and 'is_real=0'.
15513#@cli : $ image.jpg +dilate 10
15514
15515#@cli dilate_circ : _size>=0,_boundary_conditions,_is_normalized={ 0 | 1 }
15516#@cli : Apply circular dilation of selected images by specified size.
15517#@cli : Default values: 'boundary_conditions=1' and 'is_normalized=0'.
15518#@cli : $ image.jpg +dilate_circ 7
15519dilate_circ : check $1>=0 skip ${2=1},${3=0}
15520  e[^-1] "Apply circular dilation of image$? by size $1, boundary conditions $2 and is_normalized $3."
15521  if $1<2 return fi
15522  shape_circle $1 dilate[^-1] .,$2,$3 rm.
15523
15524#@cli dilate_oct : _size>=0,_boundary_conditions,_is_normalized={ 0 | 1 }
15525#@cli : Apply octagonal dilation of selected images by specified size.
15526#@cli : Default values: 'boundary_conditions=1' and 'is_normalized=0'.
15527#@cli : $ image.jpg +dilate_oct 7
15528dilate_oct : check $1>=0 skip ${2=1},${3=0}
15529  e[^-1] "Apply octagonal dilation of image$? by size $1, boundary conditions $2 and is_normalized $3."
15530  if $1<2 return fi
15531  if $1&1 ss={$1} else ss={$1+1} fi
15532  i[0] (0,1,0;1,1,1;0,1,0) i[1] (1,1,1;1,1,1;1,1,1)
15533  repeat $!-2
15534    r={round(($ss-1)*sqrt(2)/(1+sqrt(2))/2)}
15535    q={round(($ss-1)/(1+sqrt(2))/2)}
15536    if $r>0 repeat $r dilate. [0],$2,$3 done fi
15537    if $q>0 repeat $q dilate. [1],$2,$3 done fi
15538  mv. 2 done rm[0,1]
15539
15540_kr_circle :
15541  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
15542  distance. 1 round. 0.5 ir. 0,{$1/2}
15543
15544_jf_circle :
15545  {round($1)},{round($1)}
15546  center={0.5*(w-1)}
15547  f. 'sqrt((x-$center)^2+(y-$center)^2)'
15548  if !(w%2)
15549    round. 0.0001,-1
15550    t1={sqrt(((round($1)-1)/2)^2+0.25)}
15551    t2={sqrt(((round($1)+1)/2)^2+0.25)}
15552    k={$1-round($1)+0.5}
15553    t={$t1+($t2-$t1)*$k}
15554    ir. 0,$t
15555  else ir. 0,{$1/2-0.25}
15556  fi
15557
15558#@cli dilate_threshold : size_x>=1,size_y>=1,size_z>=1,_threshold>=0,_boundary_conditions
15559#@cli : Dilate selected images in the (X,Y,Z,I) space.
15560#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15561#@cli : Default values: 'size_y=size_x', 'size_z=1', 'threshold=255' and 'boundary_conditions=1'.
15562dilate_threshold : check "isint($1) && $1>=1 && isint(${2=$1}) && $2>=1 && isint(${3=1}) && $3>=1 && ${4=255}>=0 &&
15563                          isint(${5=1}) && $5>=0"
15564  e[^-1] "Dilate image$? with mask $1x$2x$3, threshold $4 and "${arg\ $5,dirichlet,neumann}" boundary conditions."
15565  l[]
15566    dx1={int($1/2)} dx2={$1-$dx1-1}
15567    dy1={int($2/2)} dy2={$2-$dy1-1}
15568    dz1={int($3/2)} dz2={$3-$dz1-1}
15569    (-$dx1,$dx1) (-$dy1;$dy1) (-$dz1/$dz1) r $1,$2,$3,1,3 a c round r {w*h*d},3,1,1,-1 transpose.
15570    i.. 1,100%,1,1,254 1,100%,1,1,255 a x
15571    ('{^}') rm.. replace_str "254,","(v=j(" replace_str ",255",",0,0,$5);if(abs(v-i)<=$4,v,-1e20))" list={t}
15572    rm
15573  endl
15574  f 'max($list)'
15575
15576#@cli divergence
15577#@cli : Compute divergence of selected vector fields.
15578#@cli : $ image.jpg luminance +gradient append[-2,-1] c divergence[-1]
15579divergence :
15580  e[^-1] "Compute divergence of vector field$?."
15581  repeat $! l[$>]
15582    if s==1 g x,0
15583    elif s==2 s c g.. x,0 g. y,0 +
15584    elif s==3 s c g... x,0 g.. y,0 g. z,0 +
15585    else error[] "Command '$0': Cannot compute divergence of image ["$>"] (has "{s}">3 channels)."
15586    fi
15587  endl done
15588
15589#@cli dog : _sigma1>=0[%],_sigma2>=0[%]
15590#@cli : Compute difference of gaussian on selected images.
15591#@cli : Default values: 'sigma1=2%' and 'sigma2=3%'.
15592#@cli : $ image.jpg dog 2,3
15593dog : check "${1=2%}>=0 && ${2=3%}>=0"
15594  e[^-1] "Compute difference of gaussian on image$?, with standard deviations $1 and $2."
15595  repeat $! l[$>]
15596    [0] parallel "b[0] $1","b[1] $2" - abs
15597  endl done
15598
15599#@cli diffusiontensors : _sharpness>=0,0<=_anisotropy<=1,_alpha[%],_sigma[%],is_sqrt={ 0 | 1 }
15600#@cli : Compute the diffusion tensors of selected images for edge-preserving smoothing algorithms.
15601#@cli : Default values: 'sharpness=0.7', 'anisotropy=0.3', 'alpha=0.6', 'sigma=1.1' and 'is_sqrt=0'.
15602#@cli : $ image.jpg diffusiontensors 0.8 abs pow 0.2
15603#@cli : $$ https://gmic.eu/oldtutorial/_diffusiontensors
15604diffusiontensors : check "${1=0.7}>=0 && ${2=0.3}>=0 && $2<=1" skip ${3=0.6},${4=1.1},${5=0}
15605  e[^-1] "Compute diffusion tensors for image$?, with sharpness $1, anisotropy $2, alpha $3 and sigma $4."
15606  p1={if($5,0.5,1)*max($1,1e-2)}
15607  p2={$p1/(1e-7+1-$2)}
15608  b $3 n 0,255 structuretensors 0 b $4
15609  repeat $! l[$>]
15610    eigen max.. 0
15611    if s==2 s.. c +[-3,-2] +.. 1 +^.. -$p1 ^... -$p2 a[-3,-1] c                   # 2D
15612    else s.. c +[-4--2] +.. 1 +^.. -$p1 r. 100%,100%,100%,2 ^... -$p2 a[-3,-1] c  # 3D
15613    fi
15614    eigen2tensor
15615  endl done
15616
15617#@cli edges : _threshold[%]>=0
15618#@cli : Estimate contours of selected images.
15619#@cli : Default value: 'edges=15%'
15620#@cli : $ image.jpg +edges 15%
15621edges : skip ${1=15%}
15622  e[^-1] "Estimate image contours of image$?, with threshold $1."
15623  gradient_norm b 0.5 >= $1 distance 0 equalize negate c 30%,70% n 0,1
15624
15625#@cli erode : size>=0 : size_x>=0,size_y>=0,_size_z>=0 : \
15626# [kernel],_boundary_conditions,_is_real={ 0=binary-mode | 1=real-mode } : (+)
15627#@cli : Erode selected images by a rectangular or the specified structuring element.
15628#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15629#@cli : Default values: 'size_z=1', 'boundary_conditions=1' and 'is_real=0'.
15630#@cli : $ image.jpg +erode 10
15631
15632#@cli erode_circ : _size>=0,_boundary_conditions,_is_normalized={ 0 | 1 }
15633#@cli : Apply circular erosion of selected images by specified size.
15634#@cli : Default values: 'boundary_conditions=1' and 'is_normalized=0'.
15635#@cli : $ image.jpg +erode_circ 7
15636erode_circ : check $1>=0 skip ${2=1},${3=0}
15637  e[^-1] "Apply circular erosion of image$? by size $1, boundary conditions $2 and is_normalized $3."
15638  if $1<2 return fi
15639  shape_circle $1 erode[^-1] .,$2,$3 rm.
15640
15641#@cli erode_oct : _size>=0,_boundary_conditions,_is_normalized={ 0 | 1 }
15642#@cli : Apply octagonal erosion of selected images by specified size.
15643#@cli : Default values: 'boundary_conditions=1' and 'is_normalized=0'.
15644#@cli : $ image.jpg +erode_oct 7
15645erode_oct : check $1>=0 skip ${2=1},${3=0}
15646  e[^-1] "Apply octagonal erosion of image$? by size $1, boundary conditions $2 and is_normalized $3."
15647  if $1<2 return fi
15648  if $1&1 ss={$1} else ss={$1+1} fi
15649  i[0] (0,1,0;1,1,1;0,1,0) i[1] (1,1,1;1,1,1;1,1,1)
15650  repeat $!-2
15651    r={round(($ss-1)*sqrt(2)/(1+sqrt(2))/2)}
15652    q={round(($ss-1)/(1+sqrt(2))/2)}
15653    if $r>0 repeat $r erode. [0],$2,$3 done fi
15654    if $q>0 repeat $q erode. [1],$2,$3 done fi
15655  mv. 2 done rm[0,1]
15656
15657#@cli erode_threshold : size_x>=1,size_y>=1,size_z>=1,_threshold>=0,_boundary_conditions
15658#@cli : Erode selected images in the (X,Y,Z,I) space.
15659#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15660#@cli : Default values: 'size_y=size_x', 'size_z=1', 'threshold=255' and 'boundary_conditions=1'.
15661erode_threshold : check "isint($1) && $1>=1 && isint(${2=$1}) && $2>=1 && isint(${3=1}) && $3>=1 && ${4=255}>=0 &&
15662                         isint(${5=1}) && $5>=0"
15663  e[^-1] "Erode image$? with mask $1x$2x$3, threshold $4 and "${arg\ $5,dirichlet,neumann}" boundary conditions."
15664  l[]
15665    dx1={int($1/2)} dx2={$1-$dx1-1}
15666    dy1={int($2/2)} dy2={$2-$dy1-1}
15667    dz1={int($3/2)} dz2={$3-$dz1-1}
15668    (-$dx1,$dx1) (-$dy1;$dy1) (-$dz1/$dz1) r $1,$2,$3,1,3 a c round r {w*h*d},3,1,1,-1 transpose.
15669    i.. 1,100%,1,1,254 1,100%,1,1,255 a x
15670    ('{^}') rm.. replace_str "254,","(v=j(" replace_str ",255",",0,0,$5);if(abs(v-i)<=$4,v,1e20))" list={t}
15671    rm
15672  endl
15673  f 'min($list)'
15674
15675#@cli fft : _{ x | y | z }...{ x | y | z } : (+)
15676#@cli : Compute the direct fourier transform (real and imaginary parts) of selected images,
15677#@cli : optionally along the specified axes only.
15678#@cli : See also: ''ifft''.
15679#@cli : $ image.jpg luminance +fft append[-2,-1] c norm[-1] log[-1] shift[-1] 50%,50%,0,0,2
15680#@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 \
15681# shift -$w2,-$h2,0,0,2 ifft remove[-1]
15682#@cli : $$ https://gmic.eu/oldtutorial/_fft
15683
15684#@cli g : eq. to 'gradient'. : (+)
15685g :
15686  _gmic_s="$?" axes,scheme,boundary,is_noarg=${"_gradient_get_args $*"}
15687  v + _gradient $axes,$scheme,$boundary v -
15688  if $is_noarg noarg fi
15689
15690#@cli gradient : { x | y | z | c }...{ x | y | z | c },_scheme,_boundary_conditions : (no arg)
15691#@cli : Compute the gradient components (first derivatives) of selected images, along specified axes.
15692#@cli : (eq. to 'g').\n
15693#@cli : 'scheme' can be { -1=backward | 0=centered | 1=forward | 2=sobel | 3=rotation-invariant (default) | \
15694# 4=deriche | 5=vanvliet }.
15695#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15696#@cli : (no arg) compute all significant components.
15697#@cli : Default values: 'scheme=0' and 'boundary_conditions=1'.
15698#@cli : $ image.jpg gradient
15699#@cli : $$ https://gmic.eu/oldtutorial/_gradient
15700gradient :
15701  _gmic_s="$?" axes,scheme,boundary,is_noarg=${"_gradient_get_args $*"}
15702  v + _gradient $axes,$scheme,$boundary v -
15703  if $is_noarg noarg fi
15704
15705# Parse and check arguments.
15706_gradient_get_args : skip "${1=},${2=},${3=}"
15707  is_noarg=0
15708  l[]
15709    if ['$1']==0 axes,scheme,boundary=,0,1 is_noarg=1 # No 1st arg
15710    else
15711      is_axes={"s=['$1'];fill(s,k,isin(s[k],'xyzc'));min(s)"}
15712      if !$is_axes axes,scheme,boundary=,0,1 is_noarg=1 # Invalid 1st arg
15713      elif ['$2']==0 axes,scheme,boundary=$1,0,1 # No 2nd arg
15714      else
15715        is_scheme={"isint($2) && inrange($2,-1,5)"}
15716        if !$is_scheme axes,scheme,boundary=,0,1 is_noarg=1 # Invalid 2nd arg
15717        elif ['$3']==0 axes,scheme,boundary=$1,$2,1 # No 3rd arg
15718        else
15719          is_boundary={"isint($3) && inrange($3,0,3)"}
15720          if !$is_boundary axes,scheme,boundary=,0,1 is_noarg=1 # Invalid 3rd arg
15721          else axes,scheme,boundary=${1-3}
15722          fi
15723        fi
15724      fi
15725    fi
15726  onfail axes,scheme,boundary=,0,1 is_noarg=1 # Invalid arguments
15727  endl
15728  u $axes,$scheme,$boundary,$is_noarg
15729
15730# Main command.
15731_gradient :
15732  axes,scheme,boundary=${1=},${2=},${3=}
15733
15734  # Display log message.
15735  if '$axes'!=0 s_axes=" along axes '"$axes"'" fi
15736  s0,s1,s2,s3,s4,s5,s6=backward,centered,forward,sobel,rotation-invariant,deriche,vanvliet
15737  s_scheme=${s{$scheme+1}}
15738  s0,s1,s2,s3=dirichlet,neumann,periodic,mirror
15739  s_boundary=${s$boundary}
15740  e[0--3] "Compute gradient of image"$_gmic_s$s_axes", with "$s_scheme" scheme and "$s_boundary" boundary conditions."
15741  sx,sy,sz,sc=",",;,/,^
15742
15743  # Process images.
15744  repeat $! l[$<] nm={n}
15745    laxes=$axes if '$laxes'==0 if d>1 laxes=xyz else laxes=xy fi fi
15746    ('$laxes')
15747    repeat w
15748      a={`i[#1,$>]`} s=${s$a}
15749      if 0${gradient_$a}>0 [gradient_$a]
15750      elif $scheme==-1 # Backward
15751        (-1${s}1${s}0) +correlate[0] .,$boundary rm..
15752      elif $scheme==0 # Centered
15753        (-0.5${s}0${s}0.5) +correlate[0] .,$boundary rm..
15754      elif $scheme==1 # Forward
15755        (0${s}-1${s}1) +correlate[0] .,$boundary rm..
15756      elif $scheme==2 # Sobel
15757        if _'$a'==_'x' (-1,0,1;-2,0,2;-1,0,1)
15758        elif _'$a'==_'y' (-1,-2,-1;0,0,0;1,2,1)
15759        else (-0.5${s}0${s}0.5)
15760        fi
15761        +correlate[0] .,$boundary rm..
15762      elif $scheme==3 # Rotation-invariant
15763        A,B={[0.25*(2-sqrt(2)),0.5*(sqrt(2)-1)]}
15764        if _'$a'==_'x' (-$A,0,$A;-$B,0,$B;-$A,0,$A)
15765        elif _'$a'==_'y' (-$A,-$B,-$A;0,0,0;$A,$B,$A)
15766        else (-0.5${s}0${s}0.5)
15767        fi
15768        +correlate[0] .,$boundary rm..
15769      else # Deriche/Vanvliet
15770        s4,s5=deriche,vanvliet com=${s$scheme}
15771        if $boundary<2 +$com[0] 0,1,$a,$boundary
15772        else
15773          if _'$a'==_'x' +r[0] {0,[w+2,h,d,s]},0,$boundary,0.5 $com. 0,1,$a shrink_x. 1
15774          elif _'$a'==_'y' +r[0] {0,[w,h+2,d,s]},0,$boundary,0,0.5 $com. 0,1,$a shrink_y. 1
15775          elif _'$a'==_'z' +r[0] {0,[w,h,d+2,s]},0,$boundary,0,0,0.5 $com. 0,1,$a shrink_z. 1
15776          fi
15777        fi
15778      fi
15779      nm. gradient_$a
15780    done
15781    rm[0,1] nm $nm
15782  endl done
15783  u $is_noaarg
15784
15785#@cli gradient_norm
15786#@cli : Compute gradient norm of selected images.
15787#@cli : $ image.jpg gradient_norm equalize
15788#@cli : $$ https://gmic.eu/oldtutorial/_gradient_norm
15789gradient_norm :
15790  e[^-1] "Compute gradient norm of image$?."
15791  repeat $! l[$>] g sqr s c + sqrt endl done
15792
15793#@cli gradient_orientation : _dimension={1,2,3}
15794#@cli : Compute N-d gradient orientation of selected images.
15795#@cli : Default value: 'dimension=3'.
15796#@cli : $ image.jpg +gradient_orientation 2
15797gradient_orientation : check "${1=3}==1 || $1==2 || $1==3"
15798  e[^-1] "Compute $1-d gradient orientation of image$?."
15799  repeat $! l[$<]
15800    if $1==1 g x +abs. +. 1e-8 -/
15801    elif $1==2 g xy +sqr +[-2,-1] +. 1e-8 sqrt. /... . /[-2,-1]
15802    else g xyz +sqr +[-3--1] +. 1e-8 sqrt. /[-4,-3] . /[-2,-1]
15803    fi
15804  endl done
15805
15806#@cli guided : [guide],radius[%]>=0,regularization[%]>=0 : radius[%]>=0,regularization[%]>=0 : (+)
15807#@cli : Blur selected images by guided image filtering.
15808#@cli : If a guide image is provided, it is used to drive the smoothing process.
15809#@cli : A guide image must be of the same xyz-size as the selected images.
15810#@cli : This command implements the filtering algorithm described in:
15811#@cli : He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering",
15812#@cli : IEEE Transactions on Pattern Analysis and Machine Intelligence, vol.35, no.6, pp.1397,1409, June 2013
15813#@cli : $ image.jpg +guided 5,400
15814
15815#@cli haar : scale>0
15816#@cli : Compute the direct haar multiscale wavelet transform of selected images.
15817#@cli : See also: ''ihaar''.
15818#@cli : $$ https://gmic.eu/oldtutorial/_haar
15819haar : check "isint(${1=1}) && $1>=0"
15820  e[^-1] "Compute haar transform of image$? with $1 scales."
15821  repeat $! l[$>]
15822    _haar
15823    repeat $1-1
15824      w={max(0,round(w/2^(1+$>))-1)}
15825      h={max(0,round(h/2^(1+$>))-1)}
15826      d={max(0,round(d/2^(1+$>))-1)}
15827      +z 0,0,0,$w,$h,$d _haar. j.. . rm.
15828    done
15829  endl done
15830
15831_haar : # Mono-scale direct haar transform.
15832  _haar_x _haar_y _haar_z
15833
15834_haar_x : # Direct haar transform along the x-axis.
15835  if w<=1 return fi
15836  if w%2 error[0--6] "Command 'haar': Invalid image width="{w}" (is not even)." fi
15837  +shift -1 r 50% +-[1] [0] +[0,1] / {sqrt(2)} a x
15838
15839_haar_y : # Direct haar transform along the y-axis.
15840  if h<=1 return fi
15841  if h%2 error[0--6] "Command 'haar': Invalid image height="{h}" (is not even)." fi
15842  +shift 0,-1 r 100%,50% +-[1] [0] +[0,1] / {sqrt(2)} a y
15843
15844_haar_z : # Direct haar transform along the z-axis.
15845  if d<=1 return fi
15846  if d%2 error[0--6] "Command 'haar': Invalid image depth="{h}" (is not even)." fi
15847  +shift 0,0,-1 r 100%,100%,50% +-[1] [0] +[0,1] / {sqrt(2)} a z
15848
15849#@cli heat_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 }
15850#@cli : Apply iterations of the heat flow on selected images.
15851#@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'.
15852#@cli : $ image.jpg +heat_flow 20
15853heat_flow : skip ${1=10},${2=30},${3=0}
15854  e[^-1] "Apply $1 iterations of the heat flow on image$?, with time step $2."
15855  pde_flow $1,$2,laplacian,$3
15856
15857#@cli hessian : { xx | xy | xz | yy | yz | zz }...{ xx | xy | xz | yy | yz | zz },_boundary_conditions : (no arg) :
15858#@cli : Compute the hessian components (second derivatives) of selected images along specified axes.
15859#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
15860#@cli : (no arg) compute all significant components.
15861#@cli : Default value: 'boundary_conditions=1'.
15862#@cli : $ image.jpg hessian
15863hessian : skip "${1=},${2=}"
15864
15865  # Parse arguments.
15866  l[]
15867    if ['$1']==0 axes,boundary=,1 noarg # No 1st arg
15868    else
15869      is_axes={"s=['$1'];fill(s,k,isin(s[k],'xyz'));min(s)"}
15870      if !$is_axes axes,boundary=,1 noarg # Invalid 1st arg
15871      elif ['$2']==0 axes,boundary=$1,1 # No 2nd arg
15872      else
15873        is_boundary={"isint($2) && inrange($2,0,3)"}
15874        if !$is_boundary axes,boundary=,1 noarg # Invalid 2nd arg
15875        else axes,boundary=$1,$2
15876        fi
15877      fi
15878    fi
15879  onfail axes,boundary=,1 noarg # Invalid arguments
15880  endl
15881
15882  # Display log message.
15883  if '$axes'!=0 s_axes=" along axes '"$axes"'" fi
15884  s0,s1,s2,s3=dirichlet,neumann,periodic,mirror
15885  s_boundary=${s$boundary}
15886  e[^-1] "Compute hessian of image$?"$s_axes", with "$s_boundary" boundary conditions."
15887
15888  # Process images.
15889  repeat $! l[$<] nm={n}
15890    laxes=$axes if '$laxes'==0 if d>1 laxes=xxxyxzyyyzzz else laxes=xxxyyy fi fi
15891    ('$laxes')
15892    repeat int(w/2)
15893      a1,a2={`i[#1,2*$>]`},{`i[#1,2*$>+1]`}
15894      if _'$a1'>_'$a2' a1,a2=$a2,$a1 fi
15895      if 0${hessian_$a1$a2}>0 [hessian_$a1$a2]
15896      elif _'$a1'==_'x'" && "_'$a2'==_'x' (1,-2,1) +correlate[0] .,$boundary rm..
15897      elif _'$a1'==_'x'" && "_'$a2'==_'y' (0.25,0,-0.25;0,0,0;-0.25,0,0.25) +correlate[0] .,$boundary rm..
15898      elif _'$a1'==_'x'" && "_'$a2'==_'z' (0.25,0,-0.25/0,0,0/-0.25,0,0.25) +correlate[0] .,$boundary rm..
15899      elif _'$a1'==_'y'" && "_'$a2'==_'y' (1;-2;1) +correlate[0] .,$boundary rm..
15900      elif _'$a1'==_'y'" && "_'$a2'==_'z' (0.25;0;-0.25/0;0;0/-0.25;0;0.25) +correlate[0] .,$boundary rm..
15901      elif _'$a1'==_'z'" && "_'$a2'==_'z' (1/-2/1) +correlate[0] .,$boundary rm..
15902      fi
15903      nm. hessian_$a1$a2
15904    done
15905    rm[0,1] nm $nm
15906  endl done
15907  u $is_noaarg
15908
15909#@cli idct : _{ x | y | z }...{ x | y | z } : (no arg)
15910#@cli : Compute the inverse discrete cosine transform of selected images, optionally along the specified axes only.
15911#@cli : Output images are always evenly sized, so this command may change the size of the selected images.
15912#@cli : (dct images obtained with the 'dct' command are evenly sized anyway).
15913#@cli : Default values: (no arg)
15914#@cli : See also: ''dct''.
15915#@cli : $$ https://gmic.eu/oldtutorial/_dct-and-idct
15916idct : skip ${1=0}
15917  ('"$1"')
15918  is_axes={im>=_'x'" && "iM<=_'z'}
15919  if $is_axes
15920    e[0--3] "Compute inverse discrete cosine transform of image$? along axes '$1'."
15921    repeat w
15922      axis={i[$>]}
15923      if $axis==_'x' repeat $!-1 l[$>] if w>1 _idct fi endl done
15924      elif $axis==_'y' repeat $!-1 l[$>] if h>1 permute yxzc _idct permute yxzc fi endl done
15925      elif $axis==_'z' repeat $!-1 l[$>] if d>1 permute zxyc _idct permute yzxc fi endl done
15926      fi
15927    done
15928    rm.
15929  else
15930    rm.
15931    e[0--3] "Compute inverse discrete cosine transform of image$?."
15932    noarg
15933    repeat $! l[$>]
15934      if w>1 _idct fi
15935      if h>1 permute yxzc _idct permute yxzc fi
15936      if d>1 permute zxyc _idct permute yzxc fi
15937    endl done
15938  fi
15939
15940# 1D inverse transform (DCT-III) along the x-axis, for a single image.
15941_idct :
15942  if w%2 r {w+1},100%,100%,100%,0,0 fi
15943  / {sqrt(2/w)} +z[0] 0,0 *. {sqrt(2)} j.. .,0,0,0 rm. # Make the transform orthogonal.
15944  +mirror x shift. 1 *. -1
15945  100%,1,1,1,cos(x*pi/(2*w))
15946  100%,1,1,1,sin(x*pi/(2*w))
15947  +*[0,3] +*[1,2] +[-2,-1]
15948  *[0,2] *[1,2] -[0,1]
15949  ifft x k[0] / 2
15950  s x,2 mirror. x
15951  r[0] 200%,100%,100%,100%,4,0,0
15952  r[1] 200%,100%,100%,100%,4,0,1
15953  +
15954
15955#@cli iee
15956#@cli : Compute gradient-orthogonal-directed 2nd derivative of image(s).
15957#@cli : $ image.jpg iee
15958iee :
15959  e[^-1] "Compute gradient-orthogonal-directed 2nd derivative of image$?."
15960  repeat $! l[$>]
15961    if d==1
15962      +g xy,0 hessian... xxxyyy      # ixx ixy iyy ix iy
15963      *... .. *[-4] . *[-4] -2       # ixx -2iyixy ixiyy ix iy
15964      +[-4,-3] *... ..               # ixx -2ixiyixy+ix^2iyy ix iy
15965      sqr[-2,-1] *[-4] . +[-4,-3]    # iy^2ixx-2ixiyixy+ix^2iyy ix^2 iy^2
15966      +[-2,-1] +. 1e-8 /             # (iy^2ixx+2ixiyixy+ix^2iyy)/(ix^2+iy^2)
15967    else
15968      +inn laplacian.. -
15969    fi
15970  endl done
15971
15972#@cli ifft : _{ x | y | z }...{ x | y | z } : (+)
15973#@cli : Compute the inverse fourier transform (real and imaginary parts) of selected images.
15974#@cli : optionally along the specified axes only.
15975#@cli : See also: ''fft''.
15976#@cli : $$ https://gmic.eu/oldtutorial/_fft
15977
15978#@cli ihaar : scale>0
15979#@cli : Compute the inverse haar multiscale wavelet transform of selected images.
15980#@cli : See also: ''haar''.
15981#@cli : $$ https://gmic.eu/oldtutorial/_haar
15982ihaar : check "isint(${1=1}) && $1>=0"
15983  e[^-1] "Compute inverse haar transform of image$? with $1 scales."
15984  repeat $! l[$>]
15985    repeat $1-1
15986      w={max(0,round(w/2^(1+$<))-1)}
15987      h={max(0,round(h/2^(1+$<))-1)}
15988      d={max(0,round(d/2^(1+$<))-1)}
15989      +z 0,0,0,$w,$h,$d _ihaar. j.. . rm.
15990    done
15991    _ihaar
15992  endl done
15993
15994_ihaar : # Mono-scale inverse haar transform.
15995  _ihaar_x _ihaar_y _ihaar_z
15996
15997_ihaar_x : # Inverse haar transform along the x-axis.
15998  if w<=1 return fi
15999  if w%2 error[0--6] "Command 'ihaar': Invalid image width="{w}" (is not even)." fi
16000  s x,2 r 200% (-1,1) *[-2,-1] + / {sqrt(2)}
16001
16002_ihaar_y : # Inverse haar transform along the y-axis.
16003  if h<=1 return fi
16004  if h%2 error "Command 'ihaar': Invalid image height="{h}" (is not even)." fi
16005  s y,2 r 100%,200% (-1;1) r. {-2,w} *[-2,-1] + / {sqrt(2)}
16006
16007_ihaar_z : # Inverse haar transform along the z-axis.
16008  if d<=1 return fi
16009  if d%2 error "Command 'ihaar': Invalid image depth="{h}" (is not even)." fi
16010  s z,2 r 100%,100%,200% (-1/1) r. {-2,w},{-2,h} *[-2,-1] + / {sqrt(2)}
16011
16012#@cli ilaplacian : { nb_iterations>0 | 0 },_[initial_estimate]
16013#@cli : Invert selected Laplacian images.
16014#@cli : If given 'nb_iterations' is '0', inversion is done in Fourier space (single iteration),
16015#@cli : otherwise, by applying 'nb_iterations' of a Laplacian-inversion PDE flow.
16016#@cli : Note that the resulting inversions are just estimation of possible/approximated solutions.
16017#@cli : Default values: 'nb_iterations=0' and '[initial_estimated]=(undefined)'.
16018#@cli : $ image.jpg +laplacian +ilaplacian[-1] 0
16019ilaplacian : check "${1=0}>=0" skip "${2=}"
16020  is_estimate=${"is_image_arg $2"} nb_iter={round($1)}
16021  if !$nb_iter # Inversion in Fourier space
16022    if $is_estimate
16023      e[0--4] "Invert Laplacian image$? in Fourier space, with initial estimate $2."
16024      pass$2 1 ia=${-average_colors} rm.
16025    else
16026      e[0--4] "Invert Laplacian image$? in Fourier space."
16027      ia=0
16028    fi
16029    repeat $! l[$>]
16030      fft 100%,100%,1,1,"2*(cos(x*2*pi/w) + cos(y*2*pi/h)) - 4" =. 1
16031      /[-3,-2] . rm.
16032      = 0 ifft rm.
16033    endl done + '"begin(S = resize(["$ia"],s,0)); S"'
16034
16035  else # Inversion with PDE-flow
16036    if $is_estimate
16037      e[0--4] "Invert Laplacian image$? using $1 iterations of PDE flow and
16038                   initial estimate $2."
16039      repeat $! pass$2 0 l[$>,-1]
16040        *[0] 5 i[1] (0,5,0;5,0,5;0,5,0)
16041        repeat $1 +convolve. [1] -. [0] +[-2,-1] /. 21 done k. # Optimized semi-implicit scheme, with dt=5
16042      endl done
16043    else
16044      e[0--4] "Invert Laplacian image$? using $1 iterations of PDE flow."
16045      repeat $! l[$>]
16046        * 5 (0,5,0;5,0,5;0,5,0) +f.. 0
16047        repeat $1 +convolve. [1] -. [0] +[-2,-1] /. 21 done k. # Optimized semi-implicit scheme, with dt=5
16048      endl done
16049    fi
16050  fi
16051
16052#@cli inn
16053#@cli : Compute gradient-directed 2nd derivative of image(s).
16054#@cli : $ image.jpg inn
16055inn :
16056  e[^-1] "Compute gradient-directed 2nd derivative of image$?."
16057  repeat $! l[$>]
16058    if d==1
16059      +g xy,0 hessian... xxxyyy  # ixx ixy iyy ix iy
16060      *[-5] .. *[-4] . *[-4] 2   # ixixx 2iyixy iyy ix iy
16061      +[-5,-4] *[-4] ..          # ix^2ixx+2ixiyixy iyy ix iy
16062      sqr[-2,-1] *... . +[-4,-3] # ix^2ixx+2ixiyixy+iy^2iyy ix^2 iy^2
16063      +[-2,-1] +. 1e-8 /         # (ix^2ixx+2ixiyixy+iy^2iyy)/(ix^2+iy^2)
16064    else
16065      +g xyz,0 hessian[-4] xxxyxzyyyzzz          # ixx ixy ixz iyy iyz izz ix iy iz
16066      *[-9] ... *[-8] .. *[-8] 2 *[-7] . *[-7] 2 # ixixx 2iyixy 2izixz iyy iyz izz ix iy iz
16067      +[-9--7] *[-7] ...                         # ix^2ixx+2ixiyixy+2ixizixy iyy iyz izz ix iy iz
16068      *[-6] .. *[-5] . *[-5] 2                   # ix^2ixx+2ixiyixy+2ixizixy iyiyy 2iziyz izz ix iy iz
16069      +[-6,-5] *[-5] .. +[-6,-5]                 # ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz izz ix iy iz
16070      sqr[-3--1] *[-4] . +[-5,-4]                # ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz+iz^2izz ix^2 iy^2 iz^2
16071      +[-3--1] +. 1e-8 /                         # (ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz+iz^2izz)/(ix^2+iy^2+iz^2)
16072    fi
16073  endl done
16074
16075#@cli inpaint : [mask] : [mask],0,_fast_method : \
16076# [mask],_patch_size>=1,_lookup_size>=1,_lookup_factor>=0,_lookup_increment!=0,_blend_size>=0,\
16077# 0<=_blend_threshold<=1,_blend_decay>=0,_blend_scales>=1,_is_blend_outer={ 0 | 1 } : (+)
16078#@cli : Inpaint selected images by specified mask.
16079#@cli : If no patch size (or 0) is specified, inpainting is done using a fast average or median algorithm.
16080#@cli : Otherwise, it used a patch-based reconstruction method, that can be very time consuming.
16081#@cli : 'fast_method' can be { 0=low-connectivity average | 1=high-connectivity average | 2=low-connectivity median | \
16082# 3=high-connectivity median }.
16083#@cli : Default values: 'patch_size=0', 'fast_method=1', 'lookup_size=22', 'lookup_factor=0.5', 'lookup_increment=1', \
16084# 'blend_size=0', 'blend_threshold=0', 'blend_decay=0.05', 'blend_scales=10' and 'is_blend_outer=1'.
16085#@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]
16086#@cli : $ image.jpg 100%,100% circle 30%,30%,30,1,255,0,255 circle 70%,70%,50,1,255,0,255 \
16087# +inpaint[0] [1],5,15,0.5,1,9,0 remove[1]
16088
16089#@cli inpaint_pde : [mask],_nb_scales[%]>=0,_diffusion_type={ 0=isotropic | 1=Delaunay-guided | \
16090# 2=edge-guided | 3=mask-guided },_diffusion_iter>=0
16091#@cli : Inpaint selected images by specified mask using a multiscale transport-diffusion algorithm.
16092#@cli : If 'diffusion type==3', non-zero values of the mask (e.g. a distance function) are used
16093#@cli : to guide the diffusion process.
16094#@cli : Default values: 'nb_scales=75%', 'diffusion_type=1' and 'diffusion_iter=20'.
16095#@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_pde[0] [1]
16096inpaint_pde : check ${is_image_arg\ $1}" && ${2=75%}>=0 && isint(${3=1}) && $3>=0 && $3<=3 && ${4=20}>=0"
16097  s0="isotropic" s1="Delaunay-guided" s2="edge-guided" s3="mask-guided"
16098  e[^-1] "Inpaint image$? by mask $1, using a multiscale diffusion algorithm with $2 scales
16099              and $4 iterations of "${s$3}" diffusion."
16100  repeat $! nm={n} pass$1 l[$>,-1]
16101    nb_scalesM={ceil(log2(max(w,h,d)))}
16102    nb_scales={round(${"is_percent $2"}?$nb_scalesM*$2:$2)}
16103    nb_scales={max(1,min($nb_scales,$nb_scalesM))}
16104    nb_iter={max(5,$4)}
16105
16106    repeat $nb_scales
16107      {0,"S = 2^"$<"; round([ max(1,w/S), max(1,h/S), max(1,d/S), s ])"}
16108      100%,100%,100%
16109      eval[1] "const wl1 = w#-1 - 1;    const hl1 = h#-1 - 1;    const dl1 = d#-1 - 1;
16110               const w1 = max(1,w - 1); const h1 = max(1,h - 1); const d1 = max(1,d - 1);
16111               !i?(
16112                 X = round(x*wl1/w1); Y = round(y*hl1/h1); Z = round(z*dl1/d1);
16113                 I(#-2,X,Y,Z) += I(#0,x,y,z);
16114                 ++i(#-1,X,Y,Z)
16115               );I"
16116      +max. 1 /[-3,-1] !=. 0
16117
16118      if !$> # First scale: Initialize by value propagation
16119        im={-2,im} +-.. {$im-1} *. ..
16120        +distance.. 1 *. -1 watershed.. . rm.
16121        +. {$im-1} mv. -3
16122      fi
16123
16124      if $>>0" || "$nb_scales==1
16125
16126        # Apply diffusion iterations.
16127        ri... ..,3
16128        if $3==0 # Isotropic diffusion
16129          repeat $nb_iter j... ..,0,0,0,0,1,. b... 0.5 done
16130
16131        elif $3==1 # Delaunay-guided
16132          +distance. 1 100%,100%,100%,{d==1?2:3}
16133          eval.. "* # Apply specific gradient scheme for distance function
16134            const boundary = 1;
16135            maxabs(a,b) = (abs(a)>abs(b)?a:b);
16136            ix = maxabs(j(1) - i,i - j(-1));
16137            iy = maxabs(j(0,1) - i,i - j(0,-1));
16138            d>1?(
16139              iz = maxabs(j(0,0,1) - i,i - j(0,0,-1));
16140              copy(I(#-1),[ ix,iy,iz ],3,whd);
16141            ):copy(I(#-1),[ ix,iy ],2,whd)"
16142          rm.. orientation.
16143          repeat $nb_iter
16144            j[-4] ...,0,0,0,0,1,..
16145            +warp[-4] .,1,2,1 *.. -1 warp[-5] ..,1,2,1 +[-5,-1] /[-4] 2
16146          done
16147          rm.
16148
16149        elif $3==2 # Edge-guided
16150          repeat $nb_iter
16151            +diffusiontensors... 0,1,1.5,0.5
16152            j[-4] ...,0,0,0,0,1,..
16153            smooth[-4] .,1,10,0 rm.
16154          done
16155
16156        else # Mask-guided
16157          +r[1] .,2 g. a[-{d==1?2:3}--1] c orientation.
16158          repeat $nb_iter
16159            j[-4] ...,0,0,0,0,1,..
16160            +warp[-4] .,1,2,1 *.. -1 warp[-5] ..,1,2,1 +[-5,-1] /[-4] 2
16161          done rm.
16162        fi
16163        j... ..,0,0,0,0,1,.
16164      fi
16165      rm[-2,-1]
16166    done
16167    nm. $nm rv[0,-1] rm.
16168  endl rm. done
16169
16170#@cli inpaint_flow : [mask],_nb_global_iter>=0,_nb_local_iter>=0,_dt>0,_alpha>=0,_sigma>=0
16171#@cli : Apply iteration of the inpainting flow on selected images.
16172#@cli : Default values: 'nb_global_iter=10', 'nb_local_iter=100', 'dt=5', 'alpha=1' and 'sigma=3'.
16173#@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 inpaint_flow[0] [1]
16174inpaint_flow : check ${is_image_arg\ $1}" && ${2=10}>=0 && ${3=100}>=0 && ${4=5}>0 && ${5=1}>=0 && ${6=3}>=0"
16175  e[^-1] "Apply $2x$3 iterations of the inpainting flow on image$?, with mask $1, time step $4, alpha $5 and sigma $6."
16176  repeat $! pass$1 0 l[$>,-1]
16177    r. [0],[0],[0],1,0 inpaint.. [1]
16178    repeat $2
16179      progress {100*$>/($2-1)}
16180      +diffusiontensors.. 0,1,$5,$6,0 *. .. smooth... .,$3,$4,0 rm.
16181    done
16182    progress 100
16183  endl rm. done
16184
16185#@cli inpaint_holes : maximal_area[%]>=0,_tolerance>=0,_is_high_connectivity={ 0 | 1 }
16186#@cli : Inpaint all connected regions having an area less than specified value.
16187#@cli : Default values: 'maximal_area=4', 'tolerance=0' and 'is_high_connectivity=0'.
16188#@cli : $ image.jpg noise 5%,2 +inpaint_holes 8,40
16189inpaint_holes : check "${1=4}>=0 && ${2=0}>=0" skip ${3=0}
16190  e[^-1] "Inpaint holes with area less than $1 pixels in image$?, with tolerance $2 and "\
16191         ${arg\ 1+!$3,high,low}" connectivity."
16192  repeat $! l[$>]
16193    100%,100%,100%
16194    area={if(${is_percent\ $1},$1*w*h*d,$1)}
16195    repeat s#0 sh[0] $> +area. $2,$3 <=. $1 -|[1,-1] rm. done
16196    if im k[0] whd={w},{h},{d} r 1,1,1,100%,2 r $whd,100%
16197    else inpaint[0] [1],0,{2*!$2+!!$3} k[0] fi
16198  endl done
16199
16200#@cli inpaint_morpho : [mask]
16201#@cli : Inpaint selected images by specified mask using morphological operators.
16202#@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_morpho[0] [1]
16203inpaint_morpho : check ${is_image_arg\ $1}
16204  e[^-1] "Inpaint image$? by mask $1, using morphological operators."
16205  repeat $! pass$1 0 l[$>,-1]
16206    nm={0,n} im={0,im} iM={0,iM} im1={$im-1} iM1={$iM+1}
16207    channels. 0 ==. 0
16208    +f[0] $im1 j. [0],0,0,0,0,1,..
16209    do
16210      +dilate. 3
16211      replace.. $im1,$iM1
16212      erode.. 3
16213      replace.. $iM1,$im1
16214      +[-2,-1] /. 2
16215      j. ...,0,0,0,0,1,..
16216    while im==$im1
16217    k. nm $nm
16218  endl done
16219
16220#@cli inpaint_matchpatch : [mask],_nb_scales={ 0=auto | >0 },_patch_size>0,_nb_iterations_per_scale>0,\
16221# _blend_size>=0,_allow_outer_blending={ 0 | 1 },_is_already_initialized={ 0 | 1 }
16222#@cli : Inpaint selected images by specified binary mask, using a multi-scale matchpatch algorithm.
16223#@cli : Default values: 'nb_scales=0', 'patch_size=9', 'nb_iterations_per_scale=10', 'blend_size=5',\
16224# 'allow_outer_blending=1' and 'is_already_initialized=0'.
16225#@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_matchpatch[0] [1]
16226inpaint_matchpatch : check ${is_image_arg\ $1}"&& ${2=0}>=0 && isint(${3=9}) && $3>0 && isint(${4=10}) && $4>0 &&
16227                           isint(${5=5}) && $5>=0" skip ${6=1},${7=0}
16228  e[^-1] "Inpaint image$? with mask $1, using a multiscale patch-matching algorithm with "\
16229         ${"if $2 u \"$2 \" else u auto- fi"}\
16230         "scales, $3x$3 patches, $4 iterations per scale and blending size $5."
16231  repeat $! pass$1 0 l[$>,-1] nm={0,n}
16232
16233    # Init variables and images.
16234    nm={0,n} nm img,mask
16235    nb_scales={max(1,round(if($2,$2,log2(min(w,h)/16)),1,1))}
16236    visu_size=${fitscreen[]" "{0,[w,h,1]},25%,50%}
16237    slices[img] 0 r[mask] [img],[img],1,1,0 !=[mask] 0
16238    if !$7 inpaint_pde[img] [mask],75% fi # Quick first estimate.
16239    im={img,im} -[img] $im
16240
16241    first_iter=1 iter=0
16242    repeat $nb_scales
16243      scale={100*(0.5^$<)}
16244      e[] "> Process scale "{1+$>}"/"$nb_scales" -> "$scale%
16245      progress {100*$>/max(1,$nb_scales-1)}
16246
16247      # Compute image and mask at current scales.
16248      +r[img,mask] $scale%,$scale%,1,100%,2 nm[-2,-1] scaled_img,scaled_mask
16249      >=[scaled_mask] 0.95
16250      if {scaled_mask,!iM} rm[scaled_img,scaled_mask] continue fi # Skip scale (if too low).
16251      +f[scaled_img] -4096 +j[scaled_img] .,0,0,0,0,1,[scaled_mask] rm.. nm. scaled_reference
16252
16253      coef={0.5^($nb_scales-$iter)}
16254      patch_size={v=round(max(min($3,5),$3*$coef));v+(1-(v%2))}
16255      patch_size={min(w,h,$patch_size)}
16256      blend_size={v=if($5,round(max(3,$5*$coef)));v+(1-(v%2))}
16257      iter+=1
16258      ==[scaled_mask] 0
16259
16260      if $first_iter # First iteration.
16261
16262        # Estimate initial correspondence map.
16263        100%,100%,1,1,x +f. y mv[scaled_mask] $! a[-3--1] c
16264        matchpatch[scaled_img] [scaled_reference],$patch_size,$patch_size,1,4,4,0,0,.
16265        rm[scaled_reference,-1]
16266        nm. correspondence
16267        first_iter=0
16268
16269      else # Standard iteration.
16270
16271        # Upscale correspondence map from previous scale.
16272        *[correspondence] 2 r[correspondence] 200%,200%,1,2 r[correspondence] [scaled_img],[scaled_img],1,2,0,1
16273        100%,100%,1,1,x +f. y a[-2,-1] c
16274        f[scaled_mask] "*if(i,1,
16275                           upc = i(#"$correspondence",x-1,y,0,0); vpc = i(#"$correspondence",x-1,y,0,1);
16276                           ucp = i(#"$correspondence",x,y-1,0,0); vcp = i(#"$correspondence",x,y-1,0,1);
16277                           ucc = i(#"$correspondence",x,y,0,0); vcc = i(#"$correspondence",x,y,0,1);
16278                           i(#-1,x,y,0,0) = (ucc==upc && vcc==vpc)?upc + 1:ucc;
16279                           i(#-1,x,y,0,1) = (ucc==ucp && vcc==vcp)?vcp + 1:vcc;
16280                           0)"
16281        rm[correspondence] nm. correspondence
16282        a[correspondence] [scaled_mask],c
16283
16284        # Refine correspondence map iteratively with matchpatch.
16285        nbs1={max(1,$nb_scales-1)}
16286        nb_iter={round(max(1,$4*(($<+1)/$nbs1)^2))}
16287
16288        repeat $nb_iter
16289          _inpaint_matchpatch[scaled_img] [correspondence],[scaled_mask],$blend_size,$6
16290          +matchpatch[scaled_img] [scaled_reference],$patch_size,$patch_size,1,4,4,0,0,[correspondence]
16291          j[correspondence] . rm.
16292          if {*1} w1[scaled_img] $visu_size,0 fi
16293          if {*2} w2[correspondence] $visu_size,1 fi
16294        done
16295        rm[scaled_img,scaled_mask,scaled_reference] channels[correspondence] 0,1
16296      fi
16297
16298    done
16299    progress 100
16300
16301    # Generate final result.
16302    if $correspondence
16303      ==[mask] 0
16304      _inpaint_matchpatch[img] [correspondence],[mask],$5,$6
16305      rm[correspondence]
16306    fi
16307    +[img] $im
16308    nm[0] $nm
16309  endl rm[mask] done
16310
16311# _inpaint_matchpatch : [correspondence_map],[mask],blend_size>0,_allow_outer_blending={ 0 | 1 }
16312_inpaint_matchpatch :
16313  pass$1 1 pass$2 {!$3" || "!$4}
16314  if !$3
16315    warp[0] [1],0,0,1
16316  else
16317    if $4 erode. $3 fi
16318    f[0] "*begin(
16319      boundary = 1;
16320      const patch_size = $3;
16321      const p2 = int(patch_size/2);
16322      const p1 = patch_size - p2 - 1;
16323      avg = resize([0],s#0);
16324
16325      # Pre-compute gaussian kernel for patch blending.
16326      wpq = resize([0],patch_size^2);
16327      g = 0;
16328      for (q = -p1, q<=p2, ++q,
16329        for (p = -p1, p<=p2, ++p,
16330          wpq[g++] = exp(-(p^2 + q^2)/(2*(0.3*patch_size)^2));
16331        );
16332      );
16333    );
16334    if (i#2,I,
16335      g = 0;
16336      avg = 0;
16337      norm = 0;
16338      for (q = -p1, q<=p2, ++q,
16339        for (p = -p1, p<=p2, ++p,
16340          U = I(#1,x + p,y + q);
16341          w = wpq[g++];
16342          norm+=w;
16343          avg+=w*I(#0,U[0,2] - [p,q]);
16344        );
16345      );
16346      avg/norm)"
16347  fi
16348  k[0]
16349
16350# _inpaint_warping2d : fill-in zero-valued vectors in absolute 2d warping field (works even when 'spectrum>2').
16351_inpaint_warping2d :
16352  repeat $! l[$>]
16353    100%,100%,100%,2,"> begin(const S = s#0; zero0 = vectorS(); zero1 = [0,0]; N = 0); I(#-1)==zero0?zero1:[++N,1]"
16354    s. c distance. 1 *. -1
16355    watershed.. . rm.
16356
16357    # Propagate offsets in each distinct voronoi cell.
16358    repeat 2
16359      f.. ">i?I:( # Forward propagation
16360        nP = vectors();
16361        const sP = size(nP);
16362        r = i(#-1);
16363        (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)):
16364        (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)):
16365        (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)):
16366        (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));
16367        nP)"
16368      f.. "<i?I:( # Backward propagation
16369        nP = vectors();
16370        const sP = size(nP);
16371        r = i(#-1);
16372        (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)):
16373        (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)):
16374        (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)):
16375        (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));
16376        nP)"
16377    done
16378    rm.
16379  endl done
16380
16381#@cli kuwahara : size>0
16382#@cli : Apply Kuwahara filter of specified size on selected images.
16383#@cli : $ image.jpg kuwahara 9
16384kuwahara : check $1>0
16385  e[^-1] "Apply Kuwahara filter of size $1 on image$?."
16386  repeat $! l[$>]
16387    s={s}
16388    +dilate $1 compose_channels. min
16389    +erode[0] $1 compose_channels. max
16390    -[-2,-1]
16391    $1,1,1,1,{1/$1} convolve[0] . transpose. convolve[0] . rm.
16392    p={int($1/2)}
16393    a[-2,-1] c
16394    f "v1=i(x-"$p",y-"$p",0,"$s",0,1); \
16395       v2=i(x+"$p",y-"$p",0,"$s",0,1); \
16396       v3=i(x-"$p",y+"$p",0,"$s",0,1); \
16397       v4=i(x+"$p",y+"$p",0,"$s",0,1); \
16398       vm=min(v1,v2,v3,v4); \
16399       if(c>="$s",i, \
16400         if(vm==v1,i(x-"$p",y-"$p",0,c,0,1),
16401           if(vm==v2,i(x+"$p",y-"$p",0,c,0,1),
16402             if(vm==v3,i(x-"$p",y+"$p",0,c,0,1),
16403               i(x+"$p",y+"$p",0,c,0,1)))))"
16404    channels 0,{s-2}
16405  endl done
16406
16407#@cli laplacian
16408#@cli : Compute Laplacian of selected images.
16409#@cli : $ image.jpg laplacian
16410laplacian :
16411  e[^-1] "Compute Laplacian of image$?."
16412  repeat $! l[$>]
16413    hessian ${arg\ 1+(d==1),xxyyzz,xxyy} +
16414  endl done
16415
16416#@cli lic : _amplitude>0,_channels>0
16417#@cli : Render LIC representation of selected vector fields.
16418#@cli : Default values: 'amplitude=30' and 'channels=1'.
16419#@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
16420lic : skip ${1=30},${2=1}
16421  e[^-1] "Render LIC representation of 2D vector field$?, with amplitude $1 and $2 channel(s)."
16422  repeat $! l[$>] nm={0,n}
16423     channels 0,1 / {max(abs(im),abs(iM))} vector2tensor
16424     100%,100%,100%,$2 rand. 0,255 smooth. ..,$1 rm..
16425     equalize
16426  nm $nm endl done
16427
16428#@cli map_tones : _threshold>=0,_gamma>=0,_smoothness>=0,nb_iter>=0
16429#@cli : Apply tone mapping operator on selected images, based on Poisson equation.
16430#@cli : Default values: 'threshold=0.1', 'gamma=0.8', 'smoothness=0.5' and 'nb_iter=30'.
16431#@cli : $ image.jpg +map_tones ,
16432map_tones : skip ${1=0.1},${2=0.8},${3=0.5},${4=30}
16433  e[^-1] "Apply tone mapping operator on image$?, with threshold $1, gamma $2, smoothness $3 and $4 iterations."
16434  repeat $! l[$>]
16435
16436    # Estimate target divergence for each channel.
16437    +l s c repeat $! l[$>]
16438      g xy,1 a c +norm orientation..
16439      m={im} M={iM} b. $3 n. $m,$M
16440      *. 'alpha=$1*iM;(alpha/(1e-10+i))*(i/(1e-10+alpha))^$2'
16441      * s c g.. x,-1 g. y,-1 +
16442    endl done a c * 0.25 endl
16443
16444   # Start Poisson-PDE iterations
16445    repeat $4 +laplacian.. *. 0.25 +. ... -. .. *. 800 +[-3,-1] /.. 801 c.. 0,255 done rm.
16446
16447  endl done
16448
16449#@cli map_tones_fast : _radius[%]>=0,_power>=0
16450#@cli : Apply fast tone mapping operator on selected images.
16451#@cli : Default values: 'radius=3%' and 'power=0.3'.
16452#@cli : $ image.jpg +map_tones_fast ,
16453map_tones_fast : check "${1=3%}>=0 && ${2=0.3}>=0"
16454  e[^-1] "Apply fast tone mapping operator on image$?, with radius $1 and power $2."
16455  repeat $! l[$>]
16456    +luminance b. $1 n 0,1
16457    +*. 2 -. 1 abs. *. {$2*log(10)} exp.
16458    <=.. 0.5 ri. ...
16459    +*... -1 +. 1 ^. .. *. -1 +. 1 *. ...
16460    ^[-4,-2] ==.. 0 *[-3,-2] +
16461  endl done n 0,255
16462
16463#@cli meancurvature_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 }
16464#@cli : Apply iterations of the mean curvature flow on selected images.
16465#@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'.
16466#@cli : $ image.jpg +meancurvature_flow 20
16467meancurvature_flow : skip ${1=10},${2=30},${3=0}
16468  e[^-1] "Apply $1 iterations of the mean curvature flow on image$?, with time step $2."
16469  pde_flow $1,$2,iee,$3
16470
16471#@cli median : size>=0,_threshold>0 : (+)
16472#@cli : Apply (opt. thresholded) median filter on selected images with structuring element size x size.
16473#@cli : $ image.jpg +median 5
16474
16475#@cli nlmeans : [guide],_patch_radius>0,_spatial_bandwidth>0,_tonal_bandwidth>0,_patch_measure_command : \
16476# _patch_radius>0,_spatial_bandwidth>0,_tonal_bandwidth>0,_patch_measure_command
16477#@cli : Apply non local means denoising of Buades et al, 2005. on selected images.
16478#@cli : The patch is a gaussian function of 'std_patch_radius'.
16479#@cli : The spatial kernel is a rectangle of radius 'spatial_bandwidth'.
16480#@cli : The tonal kernel is exponential (`exp(-d^2/_tonal_bandwidth^2)`)
16481#@cli : with `d` the euclidean distance between image patches.
16482#@cli : Default values: 'patch_radius=4', 'spatial_bandwidth=4', 'tonal_bandwidth=10' and 'patch_measure_command=-norm'.
16483#@cli : $ image.jpg +noise 10 nlmeans[-1] 4,4,{0.6*${-std_noise}}
16484nlmeans:
16485  if ${"is_image_arg $1"}
16486
16487    # Guided-filtering
16488    check "${2=4}>0 && ${3=4}>0 && ${4=10}>0" skip "${5=-norm}"
16489    e[^-1] "Apply non-local means denoising on image$?, with guide $1, patch size $2, spatial bandwidth $3,
16490            tonal bandwidth $4 and patch measure command '$5'."
16491    pass$1 0 l. $5 k[0] endl # [1] preprocessed image used to compute weights.
16492    repeat $!-1 l[$>,-1]
16493      100%,100%,100%,100%,{-1.0/($4*$4)} # [2] compute a scaling.
16494      nlmeans_core[0] [1],[2],$2,$3 rm. # Apply the NLM denoising with image 1 and 2 as parameter.
16495    endl done
16496    rm.
16497
16498  else
16499
16500    # Non-guided filtering
16501    check "${1=4}>0 && ${2=4}>0 && ${3=10}>0" skip "${4=-norm}"
16502    e[^-1] "Apply non-local means denoising on image$?, with patch size $1, spatial bandwidth $2,
16503            tonal bandwidth $3 and patch measure command '$4'."
16504    repeat $! l[$>]
16505      +l $4 k[0] endl # [1] preprocessed image used to compute weights.
16506      100%,100%,100%,100%,{-1.0/($3*$3)} # [2] compute a scaling.
16507      nlmeans_core[0] [1],[2],$1,$2 k[0] # Apply the NLM denoising with image 1 and 2 as parameter.
16508    endl done
16509  fi
16510
16511#@cli nlmeans_core: _reference_image,_scaling_map,_patch_radius>0,_spatial_bandwidth>0
16512#@cli : Apply non local means denoising using a image for weight and a map for scaling
16513nlmeans_core : check ${is_image_arg\ $1}" && "${is_image_arg\ $2}" && $3>0 && $4>0"
16514  e[^-1] "Apply non-local means denoising using weight images $1, scaling map $2, patch size $3 and
16515          spatial bandwidth $4."
16516  pass$1 0 pass$2 0
16517  repeat $!-2 l[$>,-1,-2]
16518    # [0] original, [1] weights, [2] scaling [3] sum(weights * patch), [4] sum(weights), [5] max(weights)
16519    100%,100%,100%,{0,s},0 100%,100%,100%,{1,s},0 100%,100%,100%,{1,s},1e-6
16520    if d#0==1
16521      repeat 2*$4+1 j={$>-$4} repeat 2*$4+1 i={$>-$4}
16522        if $i!=0||$j!=0
16523          # Compute shifted images [6] and weight [7]
16524          +shift[0,1] $i,$j,0,0,2 -[7] [1]
16525          sqr[7] b[7] $3 *[7] [2] exp[7]
16526          # Accumulate weights
16527          *[6] [7] max[5] [7] +[4,7] +[3,6]
16528        fi
16529      done done
16530    else
16531      repeat 2*$4+1 k={$>-$4} repeat 2*$4+1 j={$>-$4} repeat 2*$4+1 i={$>-$4}
16532        if $i!=0||$j!=0||$k!=0
16533          # Compute shifted images [6] and weight [7]
16534          +shift[0,1] $i,$j,0,0,2 -[7] [1]
16535          sqr[7] b[7] $3 *[7] [2] exp[7]
16536          # Accumulate weights
16537          *[6] [7] max[5] [7] +[4,7] +[3,6]
16538        fi
16539      done done done
16540    fi
16541    rm[1,2]
16542    *[0] [3] +[1,0] +[1,2] # Add central patch
16543    / # Normalize
16544  endl done
16545
16546#@cli normalize_local : _amplitude>=0,_radius>0,_n_smooth>=0[%],_a_smooth>=0[%],_is_cut={ 0 | 1 },_min=0,_max=255
16547#@cli : Normalize selected images locally.
16548#@cli : Default values: 'amplitude=3', 'radius=16', 'n_smooth=4%', 'a_smooth=2%', 'is_cut=1', 'min=0' and 'max=255'.
16549#@cli : $ image.jpg normalize_local 8,10
16550normalize_local :
16551  check "${1=3}>=0 && ${2=16}>0 && isbool(${5=1})" skip ${3=4%},${4=2%},${6=0},${7=255}
16552  e[^-1] "Normalize image$? locally, with amplitude $1, radius $2, neighborhood smoothness $3 and
16553          average smoothness $4."
16554  repeat $! l[$>]
16555    +l erode {2*$2+1} s c min endl
16556    +l.. dilate {2*$2+1} s c max endl
16557    +b... $4 b[-3,-2] $3
16558    +-.. ... +. 0.01 -[-5] [-4] /[-5,-1]
16559    *[-3,-2] {$1+1} *. -$1 +... . +[-2,-1]
16560    if $5 max.. $6 min. $7 fi
16561    -. .. *[-3,-1] +
16562    if $5 c $6,$7 fi
16563  endl done
16564
16565#@cli normalized_cross_correlation : [mask]
16566#@cli : Compute normalized cross-correlation of selected images with specified mask.
16567#@cli : $ image.jpg +shift -30,-20 +normalized_cross_correlation[0] [1]
16568normalized_cross_correlation : check ${is_image_arg\ $1}
16569  e[^-1] "Compute normalized cross-correlation of image$? with mask $1."
16570  pass$1 0 norm repeat $!-1 . l[$>,-1]
16571    fft.. fft. [-2,-1] *.. [-5] *. [-6]
16572    -[-2,-1] *[-5,-3] *[-3,-2] +[-3,-2] [-2,-1] a[-2,-1] c norm.
16573    /... . /[-2,-1] ifft rm.
16574  endl done rm.
16575
16576#@cli percentile : [mask],0<=_min_percentile[%]<=100,0<=_max_percentile[%]<=100.
16577#@cli : Apply percentile averaging filter to selected images.
16578#@cli : Default values: 'min_percentile=0' and 'max_percentile=100'.
16579#@cli : $ image.jpg shape_circle 11,11 +percentile[0] [1],25,75
16580percentile : check ${"is_image_arg $1"}" && inrange(${2=0},0,100) && inrange(${3=100},0,100) && $2<=$3"
16581  vmin,vmax={_[${"is_percent $2"}?100*$2:$2,${"is_percent $3"}?100*$3:$3]}
16582  e[^-1] "Apply percentile averaging filter to image$?, with mask $1, "\
16583         "min percentile "$vmin"% and max percentile "$vmax"%."
16584
16585  # Generate code for masking.
16586  pass$1 0 !=. 0 N={is} if !$N rm. return fi 128,$N
16587  eval.. ">
16588    begin(
16589      p = 0;
16590      const w2 = int(w/2);
16591      const h2 = int(h/2);
16592    );
16593    i?(
16594      out = string('N[',p,']=j(',x - w2,',',y - h2,');');
16595      copy(i(#-1,0,p++),out,size(out));
16596    )"
16597  discard. 0 code={t} rm[-2,-1]
16598
16599  # Apply filter.
16600  f "
16601    begin( N = vector"$N"() );
16602    const boundary = 1;
16603    const sS = size(N) - 1;
16604    const s0 = round(sS*"$vmin"%);
16605    const s1 = round(sS*"$vmax"%);
16606    const ds = 1 + s1 - s0;
16607    "$code"
16608    S = sort(N);
16609    res = 0; for (s = s0, s<=s1, ++s, res+=S[s]); res/=ds"
16610
16611#@cli peronamalik_flow : K_factor>0,_nb_iter>=0,_dt,_keep_sequence={ 0 | 1 }
16612#@cli : Apply iterations of the Perona-Malik flow on selected images.
16613#@cli : Default values: 'K_factor=20', 'nb_iter=5', 'dt=5' and 'keep_sequence=0'.
16614#@cli : $ image.jpg +heat_flow 20
16615peronamalik_flow : check "${1=20}>0 && ${2=5}>=0" skip ${3=5},${4=0}
16616  e[^-1] "Apply $2 iterations of the Perona-Malik flow on image$?, with K factor $1 and time step $3."
16617  m "_peronamalik_flow :
16618   +gradient xy,0 a[-2,-1] c norm. b. 0.8 /. $1 sqr. *. -1 exp. a[-2,-1] c
16619   f. '\"s1=s-1;
16620          C=i(x,y,z,s-1);
16621          if(c>=s1,0,
16622           (C+i(x+1,y,z,s-1,0,1))*(j(1,0,0,0,0,1)-i) -
16623           (C+i(x-1,y,z,s-1,0,1))*(i-j(-1,0,0,0,0,1)) +
16624           (C+i(x,y+1,z,s-1,0,1))*(j(0,1,0,0,0,1)-i) -
16625           (C+i(x,y-1,z,s-1,0,1))*(i-j(0,-1,0,0,0,1)))\"'"
16626  pde_flow $2,$3,_peronamalik_flow,$4
16627  um _peronamalik_flow
16628
16629#@cli phase_correlation : [destination]
16630#@cli : Estimate translation vector between selected source images and specified destination.
16631#@cli : $ image.jpg +shift -30,-20 +phase_correlation[0] [1] unroll[-1] y
16632phase_correlation : check ${"is_image_arg $1"}
16633  e[^-1] "Estimate shift between source image$? and destination $1."
16634  pass$1
16635  repeat $!-1
16636    normalized_cross_correlation[$>] .
16637    l[$>] eval "
16638      store([xM>=w/2?xM - w:xM,
16639             yM>=h/2?yM - h:yM,
16640             zM>=d/2?zM - d:zM]*=-1,'res',1,1,1,3)"
16641    endl
16642    $res nm. "[phase correlation]" rv[$>,-1] rm.
16643  done rm.
16644
16645#@cli pde_flow : _nb_iter>=0,_dt,_velocity_command,_keep_sequence={ 0 | 1 }
16646#@cli : Apply iterations of a generic PDE flow on selected images.
16647#@cli : Default values: 'nb_iter=10', 'dt=30', 'velocity_command=laplacian' and 'keep_sequence=0'.
16648#@cli : $ image.jpg +pde_flow 20
16649pde_flow : skip ${1=10},${2=30},${3=laplacian},${4=0}
16650  e[^-1] "Apply $1 iterations of the velocity flow '$3' on image$?, with time step $2."
16651  repeat $! l[$<]
16652    repeat $1
16653      +$3. *. {$2/(0.01+max(abs(im),abs(iM)))}
16654      if $4 +. .. else +[-2,-1] fi
16655    done
16656    if $4 rm[0] fi
16657    a x
16658  endl done
16659  if $4 s x,$1 fi
16660
16661#@cli periodize_poisson
16662#@cli : Periodize selected images using a Poisson solver in Fourier space.
16663#@cli : $ image.jpg +periodize_poisson array 2,2,2
16664periodize_poisson :
16665  e[^-1] "Periodize image$? using Poisson solver in Fourier space."
16666  repeat $! l[$>]
16667    s c repeat $! l[$>]
16668      mM={[im,iM]} sum={0,ia}
16669      laplacian ilaplacian 0 + $sum c $mM
16670    endl done a c
16671  endl done
16672
16673#@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)
16674#@cli : Reconstruct 1D/2D or 3D image from selected sets of keypoints, by RBF-interpolation.
16675#@cli : A set of keypoints is represented by a vector-valued image, where each pixel represents a single keypoint.
16676#@cli : Vector components of a keypoint have the following meaning:
16677#@cli :   - For 1D reconstruction: [ x_k, f1(k),...fN(k) ].
16678#@cli :   - For 2D reconstruction: [ x_k,y_k, f1(k),...,fN(k) ].
16679#@cli :   - For 3D reconstruction: [ x_k,y_k,z_k, f1(k),...,fN(k) ].
16680#@cli : Values 'x_k','y_k' and 'z_k' are the spatial coordinates of keypoint 'k'.
16681#@cli : Values 'f1(k),..,fN(k)' are the 'N' components of the vector value of keypoint 'k'.
16682#@cli : The command reconstructs an image with specified size 'dx'x'dy'x'dz', with 'N' channels.
16683#@cli : Default values: 'x0=y0=z0=0', 'x1=dx-1', 'y1=dy-1', 'z1=dz-1', 'phi(r)=r^2*log(1e-5+r)'.
16684#@cli : $ sp colorful r2dx 400 100%,100% noise_poissondisk. 10 1,{is},1,5 \
16685# 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
16686#@cli : $ 32,1,1,5,u([400,400,255,255,255]) rbf 400,400 c 0,255
16687rbf :
16688  $=a d_phi_r=r^2*log(1e-5+r)
16689  if isin($#,1,3,4) # 1D reconstruction
16690    dx,x0,x1=$a1,{$#>1?[$a2,$a3]:[0,$a1-1]}
16691    phi_r={`$#>3?['$a4']:'$d_phi_r'`}
16692    check $dx>0
16693    e[^-1] "Reconstruct 1D image from keypoint set$?, with size "$dx", "\
16694     "from ("$x0") to ("$x1") and phi(r) = "$phi_r.
16695    repeat $! l[$>] nm={n} if !w $dx elif whd==1 channels. 1,100% r. $dx,1,1,100% else
16696      r 1,{whd},1,100%,-1 permute. cyzx
16697      $dx,1,1,{w-1},"*
16698        begin(
16699          phi(r) = ("$phi_r");
16700          ref(crop(#0,0,0,0,0,1,h#0,1,1),X);
16701          ref(crop(#0,1,0,0,0,s,h#0,1,1,1),F);
16702          ref(vector(#h#0^2),M);
16703          repeat (h#0,k,
16704            for (l = 0, l<=k, ++l,
16705              r = abs(X[k] - X[l]);
16706              M[k*h#0 + l] = M[l*h#0 + k] = phi(r);
16707            )
16708          );
16709          ref(solve(M,F,s),W);
16710          const fx = ("$x1-$x0")/(w-1);
16711        );
16712        ref(vectors(),res); x = (x - "$x0")*fx;
16713        repeat (h#0,k, r = abs(x - X[k]); res+=W[s*k,s]*phi(r))"
16714      k. nm $nm
16715    fi endl done
16716
16717  elif isin($#,2,6,7) # 2D reconstruction
16718    dx,dy,x0,y0,x1,y1=$a1,$a2,{$#>2?[$a3,$a4,$a5,$a6]:[0,0,[$a1,$a2]-1]}
16719    phi_r={`$#>6?['$a7']:'$d_phi_r'`}
16720    check $dx>0" && "$dy>0
16721    e[^-1] "Reconstruct 2D image from keypoint set$?, with size "$dx,$dy", "\
16722     "from ("$x0,$y0") to ("$x1,$y1") and phi(r) = "$phi_r.
16723    repeat $! l[$>] nm={n} if !w $dx,$dy elif whd==1 channels 2,100% r. $dx,$dy,1,100% else
16724      r 1,{whd},1,100%,-1 permute. cyzx
16725      $dx,$dy,1,{w-2},"*
16726        begin(
16727          phi(r) = ("$phi_r");
16728          ref(crop(#0,0,0,0,0,1,h#0,1,1),X);
16729          ref(crop(#0,1,0,0,0,1,h#0,1,1),Y);
16730          ref(crop(#0,2,0,0,0,s,h#0,1,1,1),F);
16731          ref(vector(#h#0^2),M);
16732          repeat (h#0,k,
16733            for (l = 0, l<=k, ++l,
16734              r = norm(X[k] - X[l],Y[k] - Y[l]);
16735              M[k*h#0 + l] = M[l*h#0 + k] = phi(r);
16736            )
16737          );
16738          ref(solve(M,F,s),W);
16739          const fx = ("$x1-$x0")/(w-1);
16740          const fy = ("$y1-$y0")/(h-1);
16741        );
16742        ref(vectors(),res); x = (x - "$x0")*fx; y = (y - "$y0")*fy;
16743        repeat (h#0,k, r = norm(x - X[k], y - Y[k]); res+=W[s*k,s]*phi(r))"
16744      k. nm $nm
16745    fi endl done
16746
16747  elif isin($#,3,9,10) # 3D reconstruction
16748    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]}
16749    phi_r={`$#>9?['$arg10']:'$d_phi_r'`}
16750    check $dx>0" && "$dy>0" && "$dz>0
16751    e[^-1] "Reconstruct 3D image from keypoint set$?, with size "$dx,$dy,$dz", "\
16752     "from ("$x0,$y0,$z0") to ("$x1,$y1,$z1") and phi(r) = "$phi_r.
16753    repeat $! l[$>] nm={n} if !w $dx,$dy,$dz elif whd==1 channels 3,100% r. $dx,$dy,$dz,100% else
16754      r 1,{whd},1,100%,-1 permute. cyzx
16755      $dx,$dy,$dz,{w-3},"*
16756        begin(
16757          phi(r) = ("$phi_r");
16758          ref(crop(#0,0,0,0,0,1,h#0,1,1),X);
16759          ref(crop(#0,1,0,0,0,1,h#0,1,1),Y);
16760          ref(crop(#0,2,0,0,0,1,h#0,1,1),Z);
16761          ref(crop(#0,3,0,0,0,s,h#0,1,1,1),F);
16762          ref(vector(#h#0^2),M);
16763          repeat (h#0,k,
16764            for (l = 0, l<=k, ++l,
16765              r = norm(X[k] - X[l],Y[k] - Y[l],Z[k] - Z[l]);
16766              M[k*h#0 + l] = M[l*h#0 + k] = phi(r);
16767            )
16768          );
16769          ref(solve(M,F,s),W);
16770          const fx = ("$x1-$x0")/(w - 1);
16771          const fy = ("$y1-$y0")/(h - 1);
16772          const fz = ("$z1-$z0")/(d - 1);
16773        );
16774        ref(vectors(),res); x = (x - "$x0")*fx; y = (y - "$y0")*fy; z = (z - "$z0")*fz;
16775        repeat (h#0,k, r = norm(x - X[k], y - Y[k], z - Z[k]); res+=W[s*k,s]*phi(r))"
16776      k. nm $nm
16777    fi endl done
16778
16779  else error[0--2] "Command 'rbf': invalid arguments '$*'."
16780  fi
16781
16782#@cli red_eye : 0<=_threshold<=100,_smoothness>=0,0<=attenuation<=1
16783#@cli : Attenuate red-eye effect in selected images.
16784#@cli : Default values: 'threshold=75', 'smoothness=3.5' and 'attenuation=0.1'.
16785#@cli : $ image.jpg +red_eye ,
16786red_eye : skip ${1=75},${2=3.5},${3=0.1}
16787  e[^-1] "Attenuate red-eye effect in image$?, with threshold $1, smoothness $2 and attenuation $3."
16788  to_rgb rgb2ycbcr repeat $! l[$>]
16789    s c -. 128 +>=. $1% b. $2 sqrt. *. -1 +. 1
16790    n. $3,1 *[-2,-1] +. 128 a c ycbcr2rgb
16791  endl done
16792
16793#@cli remove_hotpixels : _mask_size>0, _threshold[%]>0
16794#@cli : Remove hot pixels in selected images.
16795#@cli : Default values: 'mask_size=3' and 'threshold=10%'.
16796#@cli : $ image.jpg noise 10,2 +remove_hotpixels ,
16797remove_hotpixels : check ${1=3}>0 skip ${2=10%}
16798  e[^-1] "Remove hot pixels in image$?, with mask size $1 and threshold $2."
16799  repeat $! l[$>]
16800    +median $1 +- abs. >=. $2
16801    *.. . ==. 0 *[-3,-1] +
16802  endl done
16803
16804#@cli remove_pixels : number_of_pixels[%]>=0
16805#@cli : Remove specified number of pixels (i.e. set them to 0) from the set of non-zero pixels in selected images.
16806#@cli : $ image.jpg +remove_pixels 50%
16807remove_pixels : check "$1>=0"
16808  e[^-1] "Remove $1 of the non-zero pixels in image$?."
16809  repeat $! l[$>]
16810    +norm !=. 0
16811    N={is}                                      # Number of non-zero pixels.
16812    n={round(if(${"is_percent $1"},$N*$1,$1))} # Number of pixels to remove.
16813    if $n<=0 rm.        # No pixels to remove.
16814    elif $n>=$N rm. f 0 # All pixels to remove.
16815    elif $n>int($N/2)   # More pixels to remove than to keep.
16816      remove_pixels. {$N-$n} ==. 0 *
16817    else                       # Less pixels to remove than to keep.
16818      d={d} r 100%,{d*h},1,100%,-1  # Force image to be in 2D.
16819
16820      # Retrieve coordinates of all non-zero pixels.
16821      100%,1,1,1,x 1,{-2,h},1,1,y +[-2,-1] 1 r[-2,-1] ..,.
16822      *[-2,-1] ... rm...
16823      y[-2,-1] a[-2,-1] x discard. y,0
16824
16825      # Generate a 1xN vector with at least n non-zero pixels.
16826      do
16827        1,100%,1,1 rand. 0,{h} <=. {$n*1.25}
16828        if is>=$n break else rm. fi
16829      while 1
16830
16831      # Generate a 1xn vector of coordinates to 'remove'.
16832      r. 2 *[-2,-1] discard. y,0
16833      i.. 1,100% rand.. 0,1 a[-2,-1] x sort. +,y
16834      rows. 0,{$n-1} -. 1 z. 1,3
16835
16836      # Set those pixels to 0 using a 3D object.
16837      i.. ({'CImg3d'},{h},{h})
16838      1,100%,1,1,1 1,100%,1,1,y a[-2,-1] x
16839      3,100% 1,100%,1,1,1 y[-5--1] a[-5--1] y
16840      if s#0<=3 j3d.. .,0,0,0,1,0,0,0,0
16841      else [0],[0],1,1,1 j3d. ..,0,0,0,1,0,0,0,0 *[0,-1]
16842      fi
16843      rm.
16844
16845      r 100%,{h/$d},$d,100%,-1  # Resize to original dimension (eventually 3D).
16846    fi
16847  endl done
16848
16849#@cli rolling_guidance : std_deviation_s[%]>=0,std_deviation_r[%]>=0,_precision>=0
16850#@cli : Apply the rolling guidance filter on selected image.
16851#@cli : Rolling guidance filter is a fast image abstraction filter, described in:
16852#@cli : "Rolling Guidance Filter", Qi Zhang Xiaoyong, Shen Li, Xu Jiaya Jia, ECCV'2014.
16853#@cli : Default values: 'std_deviation_s=4', 'std_deviation_r=10' and 'precision=0.5'.
16854#@cli : $ image.jpg +rolling_guidance , +-
16855rolling_guidance : check "${1=4}>=0 && ${2=10}>=0 && ${3=0.5}>=0"
16856  e[^-1] "Apply rolling guidance filter on image$?, with standard deviations ($1,$2) and precision $3."
16857  precision={2^-$3}
16858  repeat $! l[$>]
16859    +b $1
16860    repeat 100
16861      if c>1 +norm. +bilateral... .,$1,$2 rm..
16862      else +bilateral.. .,$1,$2
16863      fi
16864      -.. . std={-2,sqrt(iv)} rm..
16865      if $std<$precision break fi
16866    done
16867    k.
16868  endl done
16869
16870#@cli sharpen : amplitude>=0 : amplitude>=0,edge>=0,_alpha[%],_sigma[%]
16871#@cli : Sharpen selected images by inverse diffusion or shock filters methods.
16872#@cli : 'edge' must be specified to enable shock-filter method.
16873#@cli : Default values: 'edge=0', 'alpha=0' and 'sigma=0'.
16874#@cli : $ image.jpg sharpen 300
16875#@cli : $ image.jpg blur 5 sharpen 300,1
16876sharpen : check "$1>=0 && ${2=0}>=0 && ${3=0}>=0 && ${4=0}>=0"
16877  if $2>0 # Shock filters
16878    e[0--3] "Sharpen image$? with shock filters, amplitude $1, edge $2, alpha $3 and sigma $4."
16879    repeat $! l[$>]
16880      im,iM={[im,iM]}
16881      +b $3 structuretensors. 0 b. $4 eigen. l.. max 0 s c + + 1 ^ {-0.5*$2} *. -1 +. 1 endl
16882      if {0,d>1} # 3D
16883        +f[0] "const boundary = 1;
16884          minmod(a,b) = (a*b<=0?0:minabs(a,b));
16885          u = i(#-1,x,y,z,0);
16886          v = i(#-1,x,y,z,1);
16887          w = i(#-1,x,y,z,2);
16888          amp = i(#-2,x,y,z,0);
16889          Ippp = j(-1,-1,-1); Icpp = j(0,-1,-1); Inpp = j(1,-1,-1);
16890          Ipcp = j(-1,0,-1); Iccp = j(0,0,-1); Incp = j(1,0,-1);
16891          Ipnp = j(-1,1,-1); Icnp = j(0,1,-1); Innp = j(1,1,-1);
16892          Ippc = j(-1,-1,0); Icpc = j(0,-1,0); Inpc = j(1,-1,0);
16893          Ipcc = j(-1,0,0); Iccc = i; Incc = j(1,0,0);
16894          Ipnc = j(-1,1,0); Icnc = j(0,1,0); Innc = j(1,1,0);
16895          Ippn = j(-1,-1,1); Icpn = j(0,-1,1); Inpn = j(1,-1,1);
16896          Ipcn = j(-1,0,1); Iccn = j(0,0,1); Incn = j(1,0,1);
16897          Ipnn = j(-1,1,1); Icnn = j(0,1,1); Innn = j(1,1,1);
16898          ixx = Incc + Ipcc - 2*Iccc;
16899          ixy = 0.25*(Innc + Ippc - Inpc - Ipnc);
16900          ixz = 0.25*(Incn + Ipcp - Incp - Ipcn);
16901          iyy = Icnc + Icpc - 2*Iccc;
16902          iyz = 0.25*(Icnn + Icpp - Icnp - Icpn);
16903          izz = Iccn + Iccp - 2*Iccc;
16904          ixf = Incc - Iccc;
16905          ixb = Iccc - Ipcc;
16906          iyf = Icnc - Iccc;
16907          iyb = Iccc - Icpc;
16908          izf = Iccn - Iccc;
16909          izb = Iccc - Iccp;
16910          itt = u^2*ixx + v^2*iyy + w^2*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz;
16911          it = u*minmod(ixf,ixb) + v*minmod(iyf,iyb) + w*minmod(izf,izb);
16912          amp*sign(itt)*abs(it)"
16913
16914      else # 2D
16915        +f[0] "const boundary = 1;
16916          minmod(a,b) = (a*b<=0?0:minabs(a,b));
16917          u = i(#-1,x,y,z,0);
16918          v = i(#-1,x,y,z,1);
16919          amp = i(#-2,x,y,z,0);
16920          Ipp = j(-1,-1); Icp = j(0,-1); Inp = j(1,-1);
16921          Ipc = j(-1,0); Icc = i; Inc = j(1,0);
16922          Ipn = j(-1,1); Icn = j(0,1); Inn = j(1,1);
16923          ixx = Inc + Ipc - 2*Icc;
16924          ixy = 0.25*(Ipp + Inn - Ipn - Inp);
16925          iyy = Icn + Icp - 2*Icc;
16926          ixf = Inc - Icc;
16927          iyf = Icn - Icc;
16928          ixb = Icc - Ipc;
16929          iyb = Icc - Icp;
16930          itt = u^2*ixx + v^2*iyy + 2*u*v*ixy;
16931          it = y*minmod(ixf,ixb) + v*minmod(iyf,iyb);
16932          amp*sign(itt)*abs(it)"
16933      fi
16934      *. {$1/abs(maxabs(im,iM))} rm[-3,-2] - c $im,$iM
16935    endl done
16936  else # Inverse diffusion
16937    e[0--3] "Sharpen image$? with inverse diffusion and amplitude $1."
16938    repeat $! l[$>]
16939      im,iM={[im,iM]} +laplacian *. {$1/abs(maxabs(im,iM))} - c $im,$iM
16940    endl done
16941  fi
16942
16943#@cli smooth : amplitude[%]>=0,_sharpness>=0,0<=_anisotropy<=1,_alpha[%],_sigma[%],_dl>0,_da>0,\
16944# _precision>0,_interpolation,_fast_approx={ 0 | 1 } : \
16945# nb_iterations>=0,_sharpness>=0,_anisotropy,_alpha,_sigma,_dt>0,0 : [tensor_field],_amplitude>=0,_dl>0,_da>0,\
16946# _precision>0,_interpolation,_fast_approx={ 0 | 1 } : \
16947# [tensor_field],_nb_iters>=0,_dt>0,0 : (+)
16948#@cli : Smooth selected images anisotropically using diffusion PDE's, with specified field of
16949#@cli : diffusion tensors.
16950#@cli : 'interpolation' can be { 0=nearest | 1=linear | 2=runge-kutta }.
16951#@cli : Default values: 'sharpness=0.7', 'anisotropy=0.3', 'alpha=0.6', 'sigma=1.1', 'dl=0.8', 'da=30', \
16952# 'precision=2', 'interpolation=0' and 'fast_approx=1'.
16953#@cli : $ image.jpg repeat 3 smooth 40,0,1,1,2 done
16954#@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
16955#@cli : $$ https://gmic.eu/oldtutorial/_smooth
16956
16957#@cli split_freq : smoothness>0[%]
16958#@cli : Split selected images into low and high frequency parts.
16959#@cli : $ image.jpg split_freq 2%
16960split_freq :
16961  e[^-1] "Split image$? into low and high frequency parts, with smoothness $1."
16962  repeat $! l[$>] +b $1 -[0] [1] rv endl done
16963
16964#@cli solve_poisson : "laplacian_command",_nb_iterations>=0,_time_step>0,_nb_scales>=0
16965#@cli : Solve Poisson equation so that applying 'laplacian[n]' is close to the result of 'laplacian_command[n]'.
16966#@cli : Solving is performed using a multi-scale gradient descent algorithm.
16967#@cli : If 'nb_scales=0', the number of scales is automatically determined.
16968#@cli : Default values: 'nb_iterations=60', 'dt=5' and 'nb_scales=0'.
16969#@cli : $ image.jpg command "foo : gradient x" +solve_poisson foo +foo[0] +laplacian[1]
16970solve_poisson : check "${2=60}>=0 && ${3=5}>0 && ${4=0}>=0"
16971  e[^-1] "Solve Poisson equation for image$?, for laplacian command '$1', with $2 iterations, time step $3 and "\
16972         ${arg\ 1+($4==0),$4,auto}" scales."
16973  repeat $! l[$>]
16974    [0]
16975    repeat if($4,$4,int(max(log2(max(w,h))-1,1)))
16976      f={2^$<}
16977      r[1] {0,max(1,w/$f)},{0,max(1,h/$f)},1,100%,3
16978      +ri[0] [1],2 l. -$1 k[0] endl
16979      repeat $2 +laplacian.. -. .. *. {$3/max(1e-8,abs(im),abs(iM))} +[-3,-1] done
16980      rm.
16981    done
16982    rm[0]
16983  endl done
16984
16985#@cli split_details : _nb_scales>0,_base_scale[%]>=0,_detail_scale[%]>=0
16986#@cli : Split selected images into 'nb_scales' detail scales.
16987#@cli : If 'base_scale'=='detail_scale'==0, the image decomposition is done with 'a trous' wavelets.
16988#@cli : Otherwise, it uses laplacian pyramids with linear standard deviations.
16989#@cli : Default values: 'nb_scales=4', 'base_scale=0' and 'detail_scale=0'.
16990#@cli : $ image.jpg split_details ,
16991split_details : check "isint(${1=4}) && $1>0 && ${2=0}>=0 && ${3=0}>=0"
16992  if ($2)==0" && "($3)==0
16993    e[^-1] "Split image$? using $1 spatial scales and 'a trous' wavelets."
16994    repeat $! l[$<]
16995      repeat $1-1
16996        +f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2);
16997              i(x - d2) + i(x + d2) + 4*i(x - d) + 4*i(x + d) + 6*i;"
16998        /. 16
16999        if h>1
17000          f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2);
17001               i(x,y - d2) + i(x,y + d2) + 4*i(x,y - d) + 4*i(x,y + d) + 6*i;"
17002          /. 16
17003        fi
17004        if d>1
17005          f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2);
17006               i(x,y,z - d2) + i(x,y,z + d2) + 4*i(x,y,z - d) + 4*i(x,y,z + d) + 6*i;"
17007          /. 16
17008        fi
17009        -.. .
17010      done rv
17011    endl done
17012  else
17013    e[^-1] "Split image$? using $1 spatial scales with base scale $2 and detail scale $3."
17014    repeat $! l[$<]
17015      ss={max(0.3,if(${is_percent\ $2},$2*max(w,h),$2))}
17016      se={max(0.3,if(${is_percent\ $3},$3*max(w,h),$3))}
17017      ds={$se-$ss}
17018      repeat $1-1 +b. {$ss+$>*$ds/max(1,$1-2)} -.. . rv[-2,-1] done
17019    endl done
17020  fi
17021
17022#@cli structuretensors : _scheme={ 0=centered | 1=forward/backward } : (+)
17023#@cli : Compute the structure tensor field of selected images.
17024#@cli : Default value: 'scheme=1'.
17025#@cli : $ image.jpg structuretensors abs pow 0.2
17026#@cli : $$ https://gmic.eu/oldtutorial/_structuretensors
17027
17028#@cli solidify : _smoothness[%]>=0,_diffusion_type={ 0=isotropic | 1=Delaunay-guided | 2=edge-oriented },\
17029# _diffusion_iter>=0
17030#@cli : Solidify selected transparent images.
17031#@cli : Default values: 'smoothness=75%', 'diffusion_type=1' and 'diffusion_iter=20'.
17032#@cli : $ image.jpg 100%,100% circle[-1] 50%,50%,25%,1,255 append c +solidify , display_rgba
17033solidify : check "${1=75%}>=0 && isint(${2=1}) && $2>=0 && $2<=2 && ${3=20}>=0"
17034  s0="isotropic" s1="Delaunay-guided" s2="edge-oriented"
17035  e[^-1] "Solidify transparent image$? with smoothness $1 and $3 iterations of "${s$2}" diffusion."
17036  repeat $! l[$>] split_opacity
17037    if $!>1 <=. 128 inpaint_pde.. [1],${1-3} rm. c 0,255 fi
17038  endl done
17039
17040#@cli syntexturize : _width[%]>0,_height[%]>0
17041#@cli : Resynthetize 'width'x'height' versions of selected micro-textures by phase randomization.
17042#@cli : The texture synthesis algorithm is a straightforward implementation of the method described in :
17043#@cli : <http://www.ipol.im/pub/art/2011/ggm_rpn/>.
17044#@cli : Default values: 'width=height=100%'.
17045#@cli : $ image.jpg crop 2,282,50,328 +syntexturize 320,320
17046syntexturize : check "${1=100%}>0 && ${2=$1}>0"
17047  e[^-1] "Resynthetize $1x$2 versions of texture$? by phase randomization."
17048  repeat $! l[$>]
17049
17050    # Prepare input image data.
17051    mM={[im,iM]} repeat s sh. $> sum$>={is} var$>={iv} rm. done  # Retrieve some stats for post-normalization.
17052    nw={if(${is_percent\ $1},$1*w,$1)}
17053    nh={if(${is_percent\ $2},$2*h,$2)}
17054    repeat s sum$>*={$nw*$nh/(w*h)} done # Re-estimate output (0,0) frequency.
17055
17056    if $nw>w||$nh>h # Spot extension required when rendering on bigger image.
17057      periodize_poisson
17058      100%,100% rectangle. 5,5,{w-6},{h-6},1,1 b. 2 n. 0,1
17059      $nw,$nh,1,{-2,s} fc. ${average_colors...}
17060      j. ...,{(w-{-2,w})/2},{(h-{-2,h})/2},0,0,1,..
17061      rm[-3,-2]
17062    else
17063      r $nw,$nh,1,100%,0,0,0.5,0.5
17064      periodize_poisson
17065    fi
17066    fft
17067
17068    # Compute coherent random phase.
17069    100%,100% rand. {-pi},{pi}
17070    =. 0
17071    if !(w%2) =. {(u<0.5)*pi},{int(w/2)} fi
17072    if !(h%2) =. {(u<0.5)*pi},0,{int(h/2)} fi
17073    if !(h%2)&&!(h%2) =. {(u<0.5)*pi},{int(w/2)},{int(h/2)} fi
17074
17075    # Add random phase to fft of input image.
17076    +sin. cos..
17077    +*[-4,-1] +*[-4,-3] +[-2,-1]
17078    *[-5,-3] *[-3,-2] -[-3,-2]
17079
17080    # Get synthetized result and normalize it.
17081    repeat s =.. ${sum$>},0,0,0,$> =. 0,0,0,0,$> done
17082    ifft rm.
17083    repeat s sh. $> avg={ia} -. $avg *. {sqrt(${var$>}/if(iv,iv,1))} +. $avg rm. done
17084    c $mM
17085
17086  endl done
17087
17088#@cli syntexturize_matchpatch : _width[%]>0,_height[%]>0,_nb_scales>=0,_patch_size>0,_blending_size>=0,_precision>=0
17089#@cli : Resynthetize 'width'x'height' versions of selected micro-textures using a patch-matching algorithm.
17090#@cli : If 'nbscales==0', the number of scales used is estimated from the image size.
17091#@cli : Default values: 'width=height=100%', 'nb_scales=0', 'patch_size=7', 'blending_size=5' and 'precision=1'.
17092#@cli : $ image.jpg crop 25%,25%,75%,75% syntexturize_matchpatch 512,512
17093syntexturize_matchpatch : check "${1=100%}>0 && ${2=$1}>0 && isint(${3=0}) && $3>=0 && isint(${4=7}) && $4>0 &&
17094                                 ${5=5}>=0 && ${6=1}>=0"
17095  e[^-1] "Resynthetize $1x$2 version(s) of texture$? using a patch-matching algorithm with "\
17096         ${"if $3 u \"$3 \" else u auto- fi"}"scales, $4x$4 patches, blending size $5 and precision $6."
17097  repeat $! l[$>]
17098    nb_scales={round(if($3,$3,log2(min(w,h)/16)),1,1)}
17099    width={if(${"is_percent $1"},round(w*$1,1,1),$1)}
17100    height={if(${"is_percent $2"},round(h*$2,1,1),$2)}
17101
17102    repeat $nb_scales
17103      scale={100*(0.5^$<)}
17104      +r[0] $scale%,$scale%,1,3,2
17105
17106      if !$>
17107
17108        # Initialization.
17109        {1+round(w*$width/{0,w},1,1)},{1+round(h*$height/{0,h},1,1)},1,1
17110
17111        noise. 0.2,2 ==. 1 +distance. 1 *. -1
17112        label_fg.. 0 watershed.. . rm.
17113
17114        100%,100%,1,1,x +f. y a[-2,-1] c channels. 0,2
17115        +blend. ..,shapeaverage -.. . rm.
17116        channels. 0,1
17117
17118        {-2,iM+1} rand. 0,{-4,w} +rand. 0,{-4,h} a[-2,-1] c
17119        map... . rm. +[-2,-1] round.
17120        s. c %.. {-3,w} %. {-3,h} a[-2,-1] c
17121
17122      else
17123
17124        # Upscale.
17125        rv[-2,-1] channels. 0,1
17126        *. 2 r. 200%,200%,1,2,1
17127        f. "*upc = i(x - 1,y,0,0);
17128             vpc = i(x - 1,y,0,1);
17129             ucp = i(x,y - 1,0,0);
17130             vcp = i(x,y - 1,0,1);
17131             ucc = i(x,y,0,0);
17132             vcc = i(x,y,0,1);
17133             if (ucc==upc && vcc==vpc && c==0, upc + 1,
17134             if (ucc==ucp && vcc==vcp && c==1, vcp + 1,i))"
17135      fi
17136
17137      # Synthesis.
17138      psize={-2,min(w,h,$4)}
17139      repeat 1+$6*$<
17140        psynth={int(max(3,$5*$scale%))}
17141        +warp_patch.. .,$psynth
17142        matchpatch. ...,$psize,$psize,1,4,4,0,0,.. rm..
17143      done
17144      rm..
17145
17146    done
17147    warp_patch.. .,$5
17148    rm. r $width,$height,1,100%,0,0,0.5,0.5
17149  endl done
17150
17151# _syntexturize_matchpatch : [correspondence_map],blend_size>0
17152_syntexturize_matchpatch : check ${is_image_arg\ $1}" && isint(${2=3}) && $2>=0"
17153  if $2<=1 pass$1 warp[^-1] .,0 rm.
17154  else repeat $! pass$1 l[$>,-1]
17155    [1],[1],1,[0]
17156    f. "*begin(
17157          boundary = 1;
17158          const patch_size = $2;
17159          const p2 = int(patch_size/2);
17160          const p1 = patch_size - p2 - 1;
17161          avg = resize([0],s#0);
17162
17163          # Pre-compute gaussian kernel.
17164          wpq = resize([0],patch_size^2);
17165          g = 0;
17166          for (q = -p1, q<=p2, ++q,
17167            for (p = -p1, p<=p2, ++p,
17168              wpq[g++] = exp(-(p^2 + q^2)/(2*(0.3*patch_size)^2));
17169            );
17170          );
17171        );
17172        g = 0;
17173        avg = 0;
17174        norm = 0;
17175        for (q = -p1, q<=p2, ++q,
17176          for (p = -p1, p<=p2, ++p,
17177            U = I(#1,x + p,y + q);
17178            w = wpq[g++];
17179            avg+=w*I(#0,U - [p,q]);
17180            norm+=w;
17181          );
17182        );
17183        avg/norm"
17184    k.
17185  endl done fi
17186
17187#@cli tv_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 }
17188#@cli : Apply iterations of the total variation flow on selected images.
17189#@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'.
17190#@cli : $ image.jpg +tv_flow 40
17191tv_flow : skip ${1=10},${2=30},${3=0}
17192  e[^-1] "Apply $1 iterations of the total variation flow on image$?, with time step $2."
17193  pde_flow $1,$2,curvature,$3
17194
17195#@cli unsharp : radius[%]>=0,_amount>=0,_threshold[%]>=0
17196#@cli : Apply unsharp mask on selected images.
17197#@cli : Default values: 'amount=2' and 'threshold=0'.
17198#@cli : $ image.jpg blur 3 +unsharp 1.5,15 cut 0,255
17199unsharp : check "${2=2}>=0" skip ${3=0}
17200  e[^-1] "Apply unsharp mask on image$?, with radius $1, amount $2 and threshold $3."
17201  repeat $!
17202    +b[$>] $1 -. [$>]
17203    if $3 +norm. >=. $3 *[-2,-1] fi
17204    *. $2 -[$>,-1]
17205  done
17206
17207#@cli unsharp_octave : _nb_scales>0,_radius[%]>=0,_amount>=0,threshold[%]>=0
17208#@cli : Apply octave sharpening on selected images.
17209#@cli : Default values: 'nb_scales=4', 'radius=1', 'amount=2' and 'threshold=0'.
17210#@cli : $ image.jpg blur 3 +unsharp_octave 4,5,15 cut 0,255
17211unsharp_octave : check "${1=4}>0 && ${3=2}>=0" skip ${2=1},${4=0}
17212  e[^-1] "Apply octave sharpening on image$?, with $1 scales, radius $2, amount $3 and threshold $4."
17213  repeat $! l[$>] nm={0,n}
17214    +f 0 weight=0
17215    repeat $1
17216      +unsharp[0] {$2*2^-$<},$3,$4 *. {2^-$>}
17217      weight+={2^-$>}
17218      +[1,-1]
17219    done
17220    rm[0] / $weight
17221  nm $nm endl done
17222
17223#@cli vanvliet : std_deviation>=0[%],order={ 0 | 1 | 2 | 3 },axis={ x | y | z | c },_boundary_conditions : (+)
17224#@cli : Apply Vanvliet recursive filter on selected images, along specified axis and with
17225#@cli : specified standard deviation, order and boundary conditions.
17226#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
17227#@cli : Default value: 'boundary_conditions=1'.
17228#@cli : $ image.jpg +vanvliet 3,1,x
17229#@cli : $ image.jpg +vanvliet 30,0,x vanvliet[-2] 30,0,y add
17230
17231#@cli voronoi
17232#@cli : Compute the discrete Voronoi diagram of non-zero pixels in selected images.
17233#@cli : $ 400,400 noise 0.2,2 eq 1 +label_fg 0 voronoi[-1] +gradient[-1] xy,1 append[-2,-1] c \
17234# norm[-1] ==[-1] 0 map[-2] 2,2 mul[-2,-1] normalize[-2] 0,255 dilate_circ[-2] 4 reverse max
17235voronoi :
17236  e[^-1] "Compute the discrete Voronoi diagram of non-zero pixels in image$?."
17237  repeat $! l[$>] s c repeat $! l[$>]
17238    +!=. 0 distance. 1 *. -1
17239    watershed.. . rm.
17240  endl done a c endl done
17241
17242#@cli watermark_fourier : text,_size>0
17243#@cli : Add a textual watermark in the frequency domain of selected images.
17244#@cli : Default value: 'size=33'.
17245#@cli : $ image.jpg +watermark_fourier "Watermarked!" +display_fft remove[-3,-1] normalize 0,255 \
17246# append[-4,-2] y append[-2,-1] y
17247watermark_fourier : check ${2=33}>0
17248  e[^-1] "Add textual watermark '$1' with size $2 in the frequency domain of image$?."
17249  i[0] 0 t[0] "$1",0,0,$2,1,1 >=[0] 0.5 autocrop[0] 0
17250  repeat $!-1 w2={int(w/2)} h2={int(h/2)}
17251    fft.
17252    shift[-2,-1] $w2,$h2,0,0,2
17253    [0],[0],1,{s}
17254    j[-3,-2] .,3,3,0,0,1,[0]
17255    mirror[0] x
17256    j[-3,-2] .,{{-2,w}-2-{0,w}},3,0,0,1,[0]
17257    mirror[0] y
17258    j[-3,-2] .,{{-2,w}-2-{0,w}},{{-2,h}-2-{0,h}},0,0,1,[0]
17259    mirror[0] x
17260    j[-3,-2] .,3,{{-2,h}-2-{0,h}},0,0,1,[0]
17261    mirror[0] y
17262    rm.
17263    shift[-2,-1] -$w2,-$h2,0,0,2
17264    ifft[-2,-1] rm.
17265  mv. 1 done
17266  rm[0]
17267
17268#@cli watershed : [priority_image],_is_high_connectivity={ 0 | 1 } : (+)
17269#@cli : Compute the watershed transform of selected images.
17270#@cli : Default value: 'is_high_connectivity=1'.
17271#@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
17272
17273#---------------------------------
17274#
17275#@cli :: Features Extraction
17276#
17277#---------------------------------
17278
17279#@cli area : tolerance>=0,is_high_connectivity={ 0 | 1 }
17280#@cli : Compute area of connected components in selected images.
17281#@cli : Default values: 'is_high_connectivity=0'.
17282#@cli : $ image.jpg luminance stencil[-1] 1 +area 0
17283#@cli : $$ https://gmic.eu/oldtutorial/_area
17284area : check "$1>=0" skip ${2=0}
17285  e[^-1] "Compute area of connected components in image$?, with tolerance $1 and "\
17286         ${arg\ 1+!$2,high,low}" connectivity."
17287  repeat $! l[$>] s c
17288    repeat $! label[$>] $1,$2 nb={$>,1+iM} +histogram[$>] $nb,0,{$nb-1} map[$>] . rm. done
17289  a c endl done
17290
17291#@cli area_fg : tolerance>=0,is_high_connectivity={ 0 | 1 }
17292#@cli : Compute area of connected components for non-zero values in selected images.
17293#@cli : Similar to 'area' except that 0-valued pixels are not considered.
17294#@cli : Default values: 'is_high_connectivity=0'.
17295#@cli : $ image.jpg luminance stencil[-1] 1 +area_fg 0
17296area_fg : check "$1>=0" skip ${2=0}
17297  e[^-1] "Compute area of foreground connected components in image$?, with tolerance $1 and "\
17298         ${arg\ 1+!$2,high,low}" connectivity."
17299  repeat $! l[$>] s c
17300    repeat $! label_fg[$>] $1,$2 nb={$>,1+iM} +histogram[$>] $nb,0,{$nb-1} =. 0 map[$>] . rm. done
17301  a c endl done
17302
17303#@cli at_line : x0[%],y0[%],z0[%],x1[%],y1[%],z1[%]
17304#@cli : Retrieve pixels of the selected images belonging to the specified line (x0,y0,z0)-(x1,y1,z1).
17305#@cli : $ image.jpg +at_line 0,0,0,100%,100%,0 line[0] 0,0,100%,100%,1,0xFF00FF00,255,0,0
17306at_line : check ${7=100%}>=0
17307  e[^-1] "Retrieve pixels of image$?, belonging to line ($1,$2,$3)-($4,$5,$6)."
17308  repeat $! l[$>]
17309    x0={if(${is_percent\ $1},(w-1)*$1,$1)}
17310    y0={if(${is_percent\ $2},(h-1)*$2,$2)}
17311    z0={if(${is_percent\ $3},(d-1)*$3,$3)}
17312    x1={if(${is_percent\ $4},(w-1)*$4,$4)}
17313    y1={if(${is_percent\ $5},(h-1)*$5,$5)}
17314    z1={if(${is_percent\ $6},(d-1)*$6,$6)}
17315    ($x0,$x1^$y0,$y1^$z0,$z1)
17316    r. {1+max(abs($x1-$x0),abs($y1-$y0),abs($z1-$z0))},1,1,3,3
17317    round. 1 warp[0] .,0,0,0 rm.
17318  endl done
17319
17320#@cli at_quadrangle : x0[%],y0[%],x1[%],y1[%],x2[%],y2[%],x3[%],y3[%],_interpolation,_boundary_conditions : \
17321# x0[%],y0[%],z0[%],x1[%],y1[%],z1[%],x2[%],y2[%],z2[%],x3[%],y3[%],z3[%],_interpolation,_boundary_conditions
17322#@cli : Retrieve pixels of the selected images belonging to the specified 2D or 3D quadrangle.
17323#@cli : 'interpolation' can be { 0=nearest-neighbor | 1=linear | 2=cubic }.
17324#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
17325#@cli : $ image.jpg params=5%,5%,95%,5%,60%,95%,40%,95% +at_quadrangle $params polygon.. 4,$params,0.5,255
17326at_quadrangle : check "$#>=8 && $#<=14 && $#!=11"
17327  _at_quadrangle{$#<12?2:3} $*
17328
17329_at_quadrangle2 : check "${9=1}>=0 && $9<=2 && ${10=0}>=0 && $10<=3"
17330  repeat $! l[$>]
17331    x0={round(${"is_percent $1"}?(w-1)*$1:$1)}
17332    y0={round(${"is_percent $2"}?(h-1)*$2:$2)}
17333    x1={round(${"is_percent $3"}?(w-1)*$3:$3)}
17334    y1={round(${"is_percent $4"}?(h-1)*$4:$4)}
17335    x2={round(${"is_percent $5"}?(w-1)*$5:$5)}
17336    y2={round(${"is_percent $6"}?(h-1)*$6:$6)}
17337    x3={round(${"is_percent $7"}?(w-1)*$7:$7)}
17338    y3={round(${"is_percent $8"}?(h-1)*$8:$8)}
17339    ($x0,$x1;$x3,$x2^$y0,$y1;$y3,$y2)
17340    r. {P0=[$x0,$y0];P1=[$x1,$y1];P2=[$x2,$y2];P3=[$x3,$y3];\
17341        1+round([max(norm(P1-P0),norm(P3-P2)),max(norm(P3-P0),norm(P2-P1))])},1,2,3
17342    warp.. .,0,$9,$10 rm.
17343  endl done
17344
17345_at_quadrangle3 : check "${13=1}>=0 && $13<=2 && ${14=0}>=0 && $14<=3"
17346  repeat $! l[$>]
17347    x0={round(${"is_percent $1"}?(w-1)*$1:$1)}
17348    y0={round(${"is_percent $2"}?(h-1)*$2:$2)}
17349    z0={round(${"is_percent $3"}?(h-1)*$3:$3)}
17350    x1={round(${"is_percent $4"}?(w-1)*$4:$4)}
17351    y1={round(${"is_percent $5"}?(h-1)*$5:$5)}
17352    z1={round(${"is_percent $6"}?(h-1)*$6:$6)}
17353    x2={round(${"is_percent $7"}?(w-1)*$7:$7)}
17354    y2={round(${"is_percent $8"}?(h-1)*$8:$8)}
17355    z2={round(${"is_percent $9"}?(h-1)*$9:$9)}
17356    x3={round(${"is_percent $10"}?(w-1)*$10:$10)}
17357    y3={round(${"is_percent $11"}?(h-1)*$11:$11)}
17358    z3={round(${"is_percent $12"}?(h-1)*$12:$12)}
17359    ($x0,$x1;$x3,$x2^$y0,$y1;$y3,$y2^$z0,$z1;$z3,$z2)
17360    r. {P0=[$x0,$y0,$z0];P1=[$x1,$y1,$z1];P2=[$x2,$y2,$z2];P3=[$x3,$y3,$z2];\
17361        1+round([max(norm(P1-P0),norm(P3-P2)),max(norm(P3-P0),norm(P2-P1))])},1,3,3
17362    warp.. .,0,$13,$14 rm.
17363  endl done
17364
17365#@cli barycenter
17366#@cli : Compute the barycenter vector of pixel values.
17367#@cli : $ 256,256 ellipse 50%,50%,20%,20%,0,1,1 deform 20 +barycenter +ellipse[-2] {@0,1},5,5,0,10
17368barycenter :
17369  e[^-1] "Compute the barycenter vector of pixel values of image$?."
17370  norm repeat $! l[$>] nm={0,b}
17371    sum={is}
17372    if $sum>0
17373      if d>1 +* 'z' z={is} rm. else z=0 fi
17374      if h>1 +* 'y' y={is} rm. else y=0 fi
17375      * 'x' x={is} rm.
17376      ({$x/$sum};{$y/$sum};{$z/$sum})
17377    else ({w/2},{h/2},{d/2}) rm..
17378    fi
17379  nm "[barycenter of '"$nm"']" endl done
17380
17381#@cli delaunay : _output_type={ 0=image | 1=coordinates/triangles }
17382#@cli : Generate discrete 2D Delaunay triangulation of non-zero pixels in selected images.
17383#@cli : Input images must be scalar.
17384#@cli : Each pixel of the output image is a triplet (a,b,c) meaning the pixel belongs to
17385#@cli : the Delaunay triangle 'ABC' where 'a','b','c' are the labels of the pixels 'A','B','C'.
17386#@cli : $ 400,400 rand 32,255 100%,100% noise. 0.4,2 eq. 1 mul +delaunay
17387#@cli : $ image.jpg 100%,100% noise. 2,2 eq. 1 delaunay. +blend shapeaverage0
17388delaunay : skip "${1=0}"
17389  mode=0 if s=['"$1"'];s=='0'||s=='1' mode=$1 else mode=0 noarg fi
17390  s0,s1=image,coordinates
17391  e[^-1] "Generate discrete 2D Delaunay triangulation of non-zero pixels in image$?, in "${s$mode}" mode."
17392  repeat $! l[$<] bnm={b} nm={n}
17393
17394    # Extract and label non-zero pixels.
17395    1,64,1,2 =. 1,0,100%
17396    f.. "begin(N = 0); I!=0?(da_push([x,y]);++N):0; end(resize(#-1,1,da_size(),1,2,0))"
17397
17398    # Construct discrete voronoi diagram.
17399    +neq.. 0 distance. 1 *. -1 watershed... . rm.
17400
17401    # Extract Delaunay triangles from discrete voronoi.
17402    1,64,1,3
17403    eval... "
17404      V = crop(x,y,2,2);
17405      min(V)?(
17406        V[1]==V[0] && V[2]!=V[0] && V[3]!=V[0] && V[3]!=V[2]?( # [ a,a,b,c ]
17407          da_push([ V[0],V[2],V[3] ]);
17408        ):(V[2]==V[0] && V[1]!=V[0] && V[3]!=V[0] && V[3]!=V[1]) || # [ a,b,a,c ]
17409          (V[1]==V[2] && V[0]!=V[1] && V[3]!=V[0] && V[3]!=V[1])?(  # [ a,b,b,c ]
17410          da_push([ V[0],V[1],V[3] ]);
17411        ):(V[3]==V[0] && V[1]!=V[0] && V[2]!=V[0] && V[2]!=V[1]) || # [ a,b,c,a ]
17412          (V[1]==V[3] && V[0]!=V[1] && V[2]!=V[0] && V[2]!=V[1]) || # [ a,b,c,b ]
17413          (V[2]==V[3] && V[0]!=V[1] && V[0]!=V[2] && V[1]!=V[2]) ?( # [ a,b,c,c ]
17414          da_push([ V[0],V[1],V[2] ]);
17415        ):V[0]!=V[1] && V[0]!=V[2] && V[0]!=V[3] && V[1]!=V[2] && V[1]!=V[3] && V[2]!=V[3]?( # [ a,b,c,d ]
17416          da_push([ V[0],V[1],V[2] ]);
17417          da_push([ V[1],V[3],V[2] ]);
17418        );
17419        end(resize(#-1,1,da_size(),1,3,0));
17420      )"
17421
17422    # Return output in expected mode.
17423    if $mode k[-2,-1] nm.. ${bnm}_points nm. ${bnm}_faces
17424    else {0,[w,h,1,3]} eval.. "polygon(#-1,3,I[#1,i0],I[#1,i1],I[#1,i2],1,i0,i1,i2)" k. nm $nm fi
17425  endl done
17426
17427#@cli detect_skin : 0<=tolerance<=1,_skin_x,_skin_y,_skin_radius>=0
17428#@cli : Detect skin in selected color images and output an appartenance probability map.
17429#@cli : Detection is performed using CbCr chromaticity data of skin pixels.
17430#@cli : If arguments 'skin_x', 'skin_y' and 'skin_radius' are provided, skin pixels are learnt
17431#@cli : from the sample pixels inside the circle located at ('skin_x','skin_y') with radius 'skin_radius'.
17432#@cli : Default value: 'tolerance=0.5' and 'skin_x=skiny=radius=-1'.
17433detect_skin : check "${1=0.5}>=0 && $1<=1" skip ${2=-1},${3=-1},${4=-1}
17434  if $2<0||$3<=0||$4<=0
17435    e[0--3] "Detect skin in image$?, using tolerance $1."
17436    m0=120.9292108800069
17437    m1=142.5745272918084
17438    A=0.09749985486268997
17439    B=0.06388871371746063
17440    C=0.05250053107738495
17441    to_rgb srgb2rgb rgb2ycbcr channels 1,2
17442    repeat $! l[$>]
17443      whd={w},{h},{d} r {w*h*d},2,1,1,-1
17444      s y -[0] $m0 -[1] $m1 a y
17445      i[0] ($A,$B;$B,$C) +m* rm[0]
17446      * s y + *. {$1-1} exp.
17447      r $whd,1,-1
17448    endl done
17449  else
17450    e[0--3] "Detect skin in image$?, using tolerance $1 and target circle at ($2,$3) with radius $4."
17451    to_rgb srgb2rgb rgb2ycbcr channels 1,2
17452    repeat $! l[$>]
17453      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
17454      +warp[0] [1],0,0 rm[1]
17455      s[1] c
17456      m0={1,ia} -[1] $m0
17457      m1={2,ia} -[2] $m1
17458      M={h} a[1,2] x +transpose[1] rv[1,2] m*[1,2] /[1] $M invert[1]
17459      rv whd={w},{h},{d} r[1] {w*h*d},2,1,1,-1
17460      s[1] y -[1] $m0 -[2] $m1 a[1,2] y +m* rm[0]
17461      * s y + *. {$1-1} exp.
17462      r $whd,1,-1
17463    endl done
17464  fi
17465
17466#@cli displacement : [source_image],_smoothness,_precision>=0,_nb_scales>=0,_iteration_max>=0,is_backward={ 0 | 1 },\
17467# _[guide] : (+)
17468#@cli : Estimate displacement field between specified source and selected target images.
17469#@cli : If 'smoothness>=0', regularization type is set to isotropic, else to anisotropic.
17470#@cli : If 'nbscales==0', the number of scales used is estimated from the image size.
17471#@cli : Default values: 'smoothness=0.1', 'precision=5', 'nb_scales=0', 'iteration_max=10000', 'is_backward=1' \
17472# and '[guide]=(unused)'.
17473#@cli : $ image.jpg +rotate 3,1,0,50%,50% +displacement[-1] [-2] quiver[-1] [-1],15,1,1,1,{1.5*iM}
17474
17475#@cli distance : isovalue[%],_metric : isovalue[%],[metric],_method : (+)
17476#@cli : Compute the unsigned distance function to specified isovalue, opt. according to a custom metric.
17477#@cli : 'metric' can be { 0=chebyshev | 1=manhattan | 2=euclidean | 3=squared-euclidean }.
17478#@cli : 'method' can be { 0=fast-marching | 1=low-connectivity dijkstra | 2=high-connectivity dijkstra | \
17479# 3=1+return path | 4=2+return path }.
17480#@cli : Default value: 'metric=2' and 'method=0'.
17481#@cli : $ image.jpg threshold 20% distance 0 pow 0.3
17482#@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
17483#@cli : $$ https://gmic.eu/oldtutorial/_distance
17484
17485#@cli fftpolar
17486#@cli : Compute fourier transform of selected images, as centered magnitude/phase images.
17487#@cli : $ image.jpg fftpolar ellipse 50%,50%,10,10,0,1,0 ifftpolar
17488fftpolar :
17489  e[^-1] "Compute fourier transform of image$?, as centered magnitude/phase images."
17490  repeat $! l[$<]
17491    fft complex2polar shift {-round(w/2)},{-round(h/2)},{-round(d/2)},0,2
17492  endl done
17493
17494#@cli histogram : nb_levels>0[%],_min_value[%],_max_value[%] : (+)
17495#@cli : Compute the histogram of selected images.
17496#@cli : If value range is set, the histogram is estimated only for pixels in the specified
17497#@cli : value range. Argument 'max_value' must be specified if 'min_value' is set.
17498#@cli : Default values: 'min_value=0%' and 'max_value=100%'.
17499#@cli : $ image.jpg +histogram 64 display_graph[-1] 400,300,3
17500
17501#@cli histogram_nd : nb_levels>0[%],_value0[%],_value1[%]
17502#@cli : Compute the 1D,2D or 3D histogram of selected multi-channels images (having 1,2 or 3 channels).
17503#@cli : If value range is set, the histogram is estimated only for pixels in the specified
17504#@cli : value range.
17505#@cli : Default values: 'value0=0%' and 'value1=100%'.
17506#@cli : $ image.jpg channels 0,1 +histogram_nd 256
17507histogram_nd : check $1>0 skip ${2=0%},${3=100%}
17508  e[^-1] "Compute histogram of multi-channels image$?, using $1 levels in range [$1,$2]."
17509  percent_nblevels=${"is_percent $1"}
17510  percent_min=${"is_percent $2"}
17511  percent_max=${"is_percent $3"}
17512  repeat $! l[$>] s={s}
17513    r {w*h*d},{min(3,s)},1,1,-1
17514    vmin=$2 vmax=$3
17515    if $percent_min||$percent_max
17516      im={im} iM={iM}
17517      vmin={if($percent_min,$im+($iM-$im)*$2,$2)}
17518      vmax={if($percent_max,$im+($iM-$im)*$3,$3)}
17519    fi
17520    dv={$vmax-$vmin}
17521    nb_levels={max(1,round(if($percent_nblevels,$1*(1+$vmax-$vmin),$1)))}
17522    f 'if(i>=$vmin&&i<=$vmax,if(i==$vmax,$nb_levels-1,int((i-$vmin)*$nb_levels/($vmax-$vmin))),-1)'
17523    pointcloud 1,$nb_levels,{if($s>1,$nb_levels,1)},{if($s>2,$nb_levels,1)}
17524  endl done
17525
17526#@cli histogram_cumul : _nb_levels>0,_is_normalized={ 0 | 1 },_val0[%],_val1[%]
17527#@cli : Compute cumulative histogram of selected images.
17528#@cli : Default values: 'nb_levels=256', 'is_normalized=0', 'val0=0%' and 'val1=100%'.
17529#@cli : $ image.jpg +histogram_cumul 256 histogram[0] 256 display_graph 400,300,3
17530histogram_cumul : check ${1=256}>0 skip ${2=0},${3=0%},${4=100%}
17531  arg 1+!$2,"normalized ",""
17532  e[^-1] "Compute "${}"cumulative histogram of image$?, using $1 levels."
17533  histogram $1,$3,$4 cumulate if $2 repeat $! /[$>] {$>,iM} done fi
17534
17535#@cli histogram_pointwise : nb_levels>0[%],_value0[%],_value1[%]
17536#@cli : Compute the histogram of each vector-valued point of selected images.
17537#@cli : If value range is set, the histogram is estimated only for values in the specified
17538#@cli : value range.
17539#@cli : Default values: 'value0=0%' and 'value1=100%'.
17540histogram_pointwise : skip ${2=0%},${3=100%}
17541  e[^-1] "Compute the pointwise histogram of vector-valued points in image$?, with $1 levels."
17542  repeat $! l[$>] nm={0,n}
17543    nb_levels={round(if(${is_percent\ $1},(iM-im)*$1,$1))}
17544    value0={if(${is_percent\ $2},im+(iM-im)*$2,$2)}
17545    value1={if(${is_percent\ $3},im+(iM-im)*$3,$3)}
17546    - $value0 * {$nb_levels/max(1,abs($value1-$value0))} c 0,{$nb_levels-1} round
17547    w={w} h={h} d={d} r {w*h*d},{s},1,1,-1
17548    i.. (0,{w-1}) r.. .,.,1,1,3 round..
17549    r[-2,-1] 300%,100%,1,1,4 shift. 1 +[-2,-1] y.
17550    i.. ({'CImg3d'},{h/3},{h/3})
17551    (1,0;1,{h/3-1}) r. 2,{-2,h/3},1,1,3 round.
17552    3,100%,1,1,1 1,100%,1,1,-1 y[-5,-3,-2] a[-5--1] y
17553    {$w*$h*$d},$nb_levels j3d. ..,0,0,0,1,0,0,0 rm..
17554    r $w,$h,$d,$nb_levels,-1
17555  nm $nm endl done
17556
17557#@cli hough : _width>0,_height>0,gradient_norm_voting={ 0 | 1 }
17558#@cli : Compute hough transform (theta,rho) of selected images.
17559#@cli : Default values: 'width=512', 'height=width' and 'gradient_norm_voting=1'.
17560#@cli : $ image.jpg +blur 1.5 hough[-1] 400,400 blur[-1] 0.5 add[-1] 1 log[-1]
17561hough : check "${1=512}>0 && ${2=$1}>0" skip ${3=1}
17562  e[^-1] "Compute $1x$2 hough transform of image$?, "${arg\ 1+!$3,with,without}" gradient norm voting."
17563  slices 50% luminance repeat $! l[$>] nm={0,n}
17564    rhomax={sqrt(w^2+h^2)/2}
17565    g (0,{w-1}) (0;{{-2,h}-1}) r[-2,-1] {-3,w},{-3,h},1,1,3 -.. {w/2} -. {h/2}
17566    complex2polar[-4--1] -. ... polar2complex[-2,-1] rm.
17567    +<. 0 *. {pi} +[-3,-1] abs. %.. {2*pi}
17568    *. {$2/$rhomax} *.. {0.5*$1/pi}
17569    y[-3--1] x {w} mv[-4] $! if !$3 f. 1 fi
17570    a y pointcloud 1 r $1,$2,1,1,0
17571  nm $nm endl done
17572
17573#@cli ifftpolar
17574#@cli : Compute inverse fourier transform of selected images, from centered magnitude/phase images.
17575ifftpolar :
17576  e[^-1] "Compute inverse fourier transform of image$?, from centered magnitude/phase images."
17577  repeat int($!/2) l[$>,{$>+1}]
17578    shift {round(w/2)},{round(h/2)},{round(d/2)},0,2 polar2complex ifft rm.
17579  endl done
17580
17581#@cli isophotes : _nb_levels>0
17582#@cli : Render isophotes of selected images on a transparent background.
17583#@cli : Default value: 'nb_levels=64'
17584#@cli : $ image.jpg blur 2 isophotes 6 dilate_circ 5 display_rgba
17585isophotes : skip ${1=64}
17586  e[^-1] "Render isophote maps from images$?, with $1 levels."
17587  to_rgba repeat $! l[$>]
17588    +luminance repeat $1 +isoline3d[1] {$>*255/($1-1)} done rm[1] +3d[^0] col3d. 1
17589    [0],[0] j3d. ..,0,0,0,1,0,0,0 rm.. *
17590  endl done
17591
17592#@cli label : _tolerance>=0,is_high_connectivity={ 0 | 1 },_is_L2_norm={ 0 | 1 } : (+)
17593#@cli : Label connected components in selected images.
17594#@cli : Default values: 'tolerance=0', 'is_high_connectivity=0' and 'is_L2_norm=1'.
17595#@cli : $ image.jpg luminance threshold 60% label normalize 0,255 map 0
17596#@cli : $ 400,400 set 1,50%,50% distance 1 mod 16 threshold 8 label mod 255 map 2
17597#@cli : $$ https://gmic.eu/oldtutorial/_label
17598
17599#@cli label_fg : tolerance>=0,is_high_connectivity={ 0 | 1 }
17600#@cli : Label connected components for non-zero values (foreground) in selected images.
17601#@cli : Similar to 'label' except that 0-valued pixels are not labeled.
17602#@cli : Default value: 'is_high_connectivity=0'.
17603label_fg : check "$1>=0" skip ${2=0}
17604  e[^-1] "Label foreground connected components on image [1], with tolerance $1 and "\
17605         ${arg\ 1+!$2,high,low}" connectivity."
17606  repeat $! l[$>]
17607    if d>1 +z -1,-1,-1,100%,100%,100% label. $1,$2 z. 1,1,1,100%,100%,100%
17608    else +z -1,-1,100%,100% label. $1,$2 z. 1,1,100%,100%
17609    fi
17610    norm.. !=.. 0 * +histogram {1+iM} =. 0
17611    >. 0 cumulate. map.. . rm.
17612  endl done
17613
17614#@cli laar
17615#@cli : Extract the largest axis-aligned rectangle in non-zero areas of selected images.
17616#@cli : Rectangle coordinates are returned in status, as a sequence of numbers x0,y0,x1,y1.
17617#@cli : $ shape_cupid 256 coords=${-laar} normalize 0,255 to_rgb rectangle $coords,0.5,0,128,0
17618laar :
17619  e[^-1] "Extract the largest axis-aligned rectangle in non-zero areas of image$?."
17620  res= sep=
17621  repeat $! l[$>]
17622    +channels 0 gt. 0 nm. shape
17623    +cumulate[shape] xy nm. cumul
17624    val={i[-1,2]}
17625    if !$val res.=-1,-1,-1,-1        # All black
17626    elif $val==wh res.=0,0,{[w,h]-1} # All white
17627    else
17628
17629      # Step 1: Compute largest square.
17630      Rin,Rout=0,{min(w,h)+1}
17631      P0,P=
17632      do
17633        Rmid={int(($Rin+$Rout)/2)}
17634        Q=${_laar\ $Rmid,$Rmid,$P}
17635        if narg($Q) Rin=$Rmid P0=$Q P=$Q else Rout=$Rmid P= fi
17636      while $Rin!=$Rout-1
17637      if $Rin==1 P=${_laar\ 1,1} fi
17638
17639      # Step 2: Compute largest rectangle, for W>H.
17640      maxA,maxW,maxH=0 maxcoords=
17641      P=$P0 W,H=$Rin
17642      for $H>0
17643        A={$W*$H} if $A>$maxA maxA,maxW,maxH=$A,$W,$H maxP={[$P][0,2]} fi
17644        nW={$W+1}
17645        Q=${_laar\ $nW,$H,$P}
17646        if narg($Q) W=$nW P=$Q
17647        elif $H>1
17648          pH={$H-1}
17649          Q=${_laar\ $nW,$pH}
17650          if narg($Q) W,H=$nW,$pH P=$Q
17651          else H-=2 P=
17652          fi
17653        else break
17654        fi
17655      done
17656
17657      # Step 3: Compute largest rectangle, for H>W.
17658      P=$P0 W,H=$Rin
17659      for $W>0
17660        A={$W*$H} if $A>$maxA maxA,maxW,maxH=$A,$W,$H maxP={[$P][0,2]} fi
17661        nH={$H+1}
17662        Q=${_laar\ $W,$nH,$P}
17663        if narg($Q) H=$nH P=$Q
17664        elif $W>1
17665          pW={$W-1}
17666          Q=${_laar\ $pW,$nH}
17667          if narg($Q) W,H=$pW,$nH P=$Q
17668          else W-=2 P=
17669          fi
17670        else break
17671        fi
17672      done
17673
17674      res.=$sep$maxP,{[$maxP]+[$maxW,$maxH]-1} sep=,
17675    fi
17676    rm[shape,cumul]
17677  endl done u $res
17678
17679# $1,$2: Rectangle width and height.
17680# ${3--1}: Coordinates of candidate locations. If empty, full-search is done.
17681# Return the list of upper-left rectangle coordinates that fit.
17682# Images [shape] and [cumul] must be defined.
17683_laar : skip "${3=}"
17684  fn="cumul(x0,y0,x1,y1) = (
17685        _px0 = x0 - 1;
17686        _py0 = y0 - 1;
17687        i(#"$cumul,"x1,y1) + i(#"$cumul",_px0,_py0) - i(#"$cumul",x1,_py0) - i(#"$cumul",_px0,y1);
17688      );"
17689  res=
17690  if narg($3) # Test suggested locations
17691    (${3--1}) r. 2,{w/2},1,1,-1 s. x a[-2,-1] c
17692    f. ${fn}"x1 = i0 + $1 - 1; y1 = i1 + $2 - 1;
17693       i(#"$shape",i0,i1) && x1<w#"$shape" && y1<h#"$shape" && cumul(i0,i1,x1,y1)==$1*$2?I:[-1,-1]"
17694    l. discard y,-1 if w s c a x res={^} fi rm endl
17695  else # Test all shape points
17696    +f[cumul] *${fn}"x1 = x + $1 - 1; y1 = y + $2 - 1;
17697              i(#"$shape",x,y) && x1<w#"$shape" && y1<h#"$shape" && cumul(x,y,x1,y1)==$1*$2"
17698    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
17699    rm.
17700  fi
17701  u $res
17702
17703#@cli max_patch : _patch_size>=1
17704#@cli : Return locations of maximal values in local patch-based neighborhood of given size for selected images.
17705#@cli : Default value: 'patch_size=16'.
17706#@cli : $ image.jpg norm +max_patch 16
17707max_patch : check "isint(${1=16}) && $1>=1"
17708  e[^-1] "Return locations of maximal values in local patch neighborhood of size $1, in image$?."
17709  repeat $! +dilate[$>] $1 ==[$>,-1] done
17710
17711#@cli min_patch : _patch_size>=1
17712#@cli : Return locations of minimal values in local patch-based neighborhood of given size for selected images.
17713#@cli : Default value: 'patch_size=16'.
17714#@cli : $ image.jpg norm +min_patch 16
17715min_patch : check "isint(${1=16}) && $1>=1"
17716  e[^-1] "Return locations of minimal values in local patch neighborhood of size $1, in image$?."
17717  repeat $! +erode[$>] $1 ==[$>,-1] done
17718
17719#@cli minimal_path : x0[%]>=0,y0[%]>=0,z0[%]>=0,x1[%]>=0,y1[%]>=0,z1[%]>=0,_is_high_connectivity={ 0 | 1 }
17720#@cli : Compute minimal path between two points on selected potential maps.
17721#@cli : Default value: 'is_high_connectivity=0'.
17722#@cli : $ image.jpg +gradient_norm fill[-1] 1/(1+i) minimal_path[-1] 0,0,0,100%,100%,0 pointcloud[-1] 0 *[-1] 280 \
17723# to_rgb[-1] ri[-1] [-2],0 or
17724minimal_path : check "$1>=0 && $2>=0 && $3>=0" skip ${7=0}
17725  e[^-1] "Compute minimal path between points ($1,$2,$3) and ($4,$5,$6) for potential map$?, with "\
17726         ${arg\ 1+$7,low,high}" connectivity."
17727  repeat $! l[$>] nm={0,n}
17728    - {im} + {iM/100}
17729    100%,100% = 1,${4-6} distance. 1,[0],{if($7,4,3)} k. channels. 1
17730    i[0] 0
17731    eval "
17732      is_percent(str) = (unref(_is_pct); _is_pct=['#str']; _is_pct[size(_is_pct) - 1]==_'%');
17733      x = round(is_percent($1)?$1*(w - 1):$1);
17734      y = round(is_percent($2)?$2*(h - 1):$2);
17735      z = round(is_percent($3)?$3*(d - 1):$3);
17736      da_push(#0,[x,y,z]);
17737      do (
17738        p = i(x,y,z);
17739        p&1?--x:p&2?++x;
17740        p&4?--y:p&8?++y;
17741        p&16?--z:p&32?++z;
17742        da_push(#0,[x,y,z]),
17743      _(while) p);
17744      resize(#0,1,da_size(#0),1,3,0)"
17745    rm. nm $nm endl done
17746
17747#@cli mse
17748#@cli : Compute MSE (Mean-Squared Error) matrix between selected images.
17749#@cli : $ image.jpg +noise 30 +noise[0] 35 +noise[0] 38 cut. 0,255 mse
17750mse :
17751  e[^-1] "Compute the "$!x$!" matrix of MSE values, for image$?."
17752  +mse k.
17753
17754+mse :
17755  e[^-1] "Compute the "$!x$!" matrix of MSE values, for image$?."
17756  N=$! $N,$N
17757  repeat $N,q
17758    repeat $q,p
17759      if "const p = "$p"; const q = "$q"; [w#p,h#p,d#p,s#p]!=[w#q,h#q,d#q,s#q]"
17760        error[0--4] "Image ["$p"] = '"{$p,n}"' ("{$p,[w,h,d,s]}") and ["$q"] = '"{$q,n}"' ("{$q,[w,h,d,s]}") "\
17761                    "have different sizes."
17762      fi
17763      +-[$p,$q] sqr. mse={ia} rm.
17764      =. $mse,$p,$q =. $mse,$q,$p
17765    done
17766  done
17767
17768#@cli patches : patch_width>0,patch_height>0,patch_depth>0,x0,y0,z0,_x1,_y1,_z1,...,_xN,_yN,_zN
17769#@cli : Extract N+1 patches from selected images, centered at specified locations.
17770#@cli : $ image.jpg +patches 64,64,1,153,124,0,184,240,0,217,126,0,275,38,0
17771patches : check "isint($1) && $1>0 && isint($2) && $2>0 && isint($3) && $3>0"
17772  e[^-1] "Extract $1x$2x$3 patches from image$?, at locations (${4--1})."
17773  (${4--1}) r. 3,{w/3},1,1,-1 permute. yzcx N={w}
17774  H={int(sqrt(w))} W={round(w/$H,1,1)} r. {$W*$H},1,1,3,0 r. $W,$H,1,3,-1
17775  r. {w*$1},{h*$2},{d*$3}
17776  $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]
17777  repeat $!-1 warp[$>] .,0,0,0 done rm.
17778  repeat $! l[$<] s y,$H s x,$W k[0-{$N-1}] endl done
17779
17780#@cli matchpatch : [patch_image],patch_width>=1,_patch_height>=1,_patch_depth>=1,_nb_iterations>=0,\
17781# _nb_randoms>=0,_patch_penalization,_output_score={ 0 | 1 },_[guide] : (+)
17782#@cli : Estimate correspondence map between selected images and specified patch image, using
17783#@cli : a patch-matching algorithm.
17784#@cli : Each pixel of the returned correspondence map gives the location (p,q) of the closest patch in
17785#@cli : the specified patch image. If 'output_score=1', the third channel also gives the corresponding
17786#@cli : matching score for each patch as well.
17787#@cli : If 'patch_penalization' is >=0, SSD is penalized with patch occurrences.
17788#@cli : If 'patch_penalization' is <0, SSD is inf-penalized when distance between patches are less \
17789# than '-patch_penalization'.
17790#@cli : Default values: 'patch_height=patch_width', 'patch_depth=1', 'nb_iterations=5', 'nb_randoms=5', \
17791# 'patch_penalization=0', 'output_score=0' and 'guide=(undefined)'.
17792#@cli : $ image.jpg sample colorful +matchpatch[0] [1],3 +warp[-2] [-1],0
17793
17794#@cli plot2value
17795#@cli : Retrieve values from selected 2D graph plots.
17796#@cli : $ 400,300,1,1,'if(y>300*abs(cos(x/10+2*u)),1,0)' +plot2value +display_graph[-1] 400,300
17797plot2value :
17798  e[^-1] "Retrieve values from 2D graph plot$?."
17799  repeat $! l[$>]
17800    s c >= 50%
17801    repeat $! l[$>] (1,{w}) ri[1] [0],3 * histogram {w},1,{w} endl done
17802    a c
17803  endl done
17804
17805#@cli pointcloud : _type = { -X=-X-opacity | 0=binary | 1=cumulative | 2=label | 3=retrieve coordinates },\
17806# _width,_height>0,_depth>0
17807#@cli : Render a set of point coordinates, as a point cloud in a 1D/2D or 3D binary image
17808#@cli : (or do the reverse, i.e. retrieve coordinates of non-zero points from a rendered point cloud).
17809#@cli : Input point coordinates can be a NxMx1x1, Nx1x1xM or 1xNx1xM image, where 'N' is the number of points,
17810#@cli : and M the point coordinates.
17811#@cli : If 'M'>3, the 3-to-M components sets the (M-3)-dimensional color at each point.
17812#@cli : Parameters 'width','height' and 'depth' are related to the size of the final image :
17813#@cli :   - If set to 0, the size is automatically set along the specified axis.
17814#@cli :   - If set to N>0, the size along the specified axis is N.
17815#@cli :   - If set to N<0, the size along the specified axis is at most N.
17816#@cli : Points with coordinates that are negative or higher than specified ('width','height','depth')
17817#@cli : are not plotted.
17818#@cli : Default values: 'type=0' and 'max_width=max_height=max_depth=0'.
17819#@cli : $ 3000,2 rand 0,400 +pointcloud 0 dilate[-1] 3
17820#@cli : $ 3000,2 rand 0,400 {w} {w},3 rand[-1] 0,255 append y +pointcloud 0 dilate[-1] 3
17821pointcloud : check "${1=0}<=3 && ${2=0}>=0 && ${3=0}>=0 && ${4=0}>=0"
17822  e[^-1] "Convert image$? to point clouds, in "${arg\ 2+($1>=0)*$1-($1<0),{-$1}-opacity,binary,cumulative,labeling}\
17823         " mode, with dimensions $2x$3x$4."
17824  repeat $! l[$>] nm={0,n}
17825    if $1!=3 # Render point cloud image from set of point coordinates
17826
17827      # Force input data to be of size Nx1x1xM.
17828      if "d>1 || (w>1 && h>1 && s>1)"
17829         error "Command '$0': Invalid input image "{w}x{h}x{d}x{s}". Should be NxMx1x1, Nx1x1xM or 1xNx1xM."
17830      fi
17831      if "w>1 && h>1 && s==1" r 100%,1,1,{h},-1  # NxMx1x1 -> Nx1x1xM
17832      elif "w==1 && h>1 && s>1" r {h},1,1,{s},-1 # 1xNx1xM -> Nx1x1xM
17833      fi
17834
17835      # Retrieve coordinates and color info.
17836      if s<3 channels 0,2 fi
17837      if s<4 100%,1,1,1,1 a[-2,-1] c fi
17838      sh. 0   round. siz_x={!$2?iM+1:$2}
17839      sh.. 1  round. siz_y={!$3?iM+1:$3}
17840      sh... 2 round. siz_z={!$4?iM+1:$4}
17841      rm[-3--1]
17842
17843      # Draw point cloud.
17844      $siz_x,$siz_y,$siz_z,{$1!=2?s-3:1}
17845      if $1<0 # -X-opacity
17846        f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = (1+$1)*I(#-1,P) - $1*C; V"
17847      elif $1==0 # Binary
17848        f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = C; V"
17849      elif $1==1 # Cumulative
17850        f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) += C; V"
17851      else # Label
17852        f.. ">begin(l = 0); V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = ++l; V"
17853      fi
17854
17855    else # Retrieve set of point coordinates from rendered point cloud image
17856      16,1,1,{s+3}
17857      f.. ">
17858        begin(N = 0; zero = vectors(0));
17859        I!=zero?I[#-1,N++] = [ x,y,z,I ];
17860        N>=w(#-1)?resize(#-1,1.5*w(#-1),1,1,s#-1,0);
17861        end(resize(#-1,N,1,1,s#-1,0));
17862        I"
17863    fi
17864  k. nm $nm endl done
17865
17866#@cli psnr : _max_value
17867#@cli : Compute PSNR (Peak Signal-to-Noise Ratio) matrix between selected images.
17868#@cli : Default value: 'max_value=255'.
17869#@cli : $ image.jpg +noise 30 +noise[0] 35 +noise[0] 38 cut[-1] 0,255 psnr 255 replace_inf 0
17870+psnr : skip "${1=}"
17871  if isnum("$1")
17872    e[0--3] "Compute the "$!x$!" matrix of PSNR values, from image$? with maximum value $1."
17873    if $! +mse f. ">x==y?inf:y>x?i(y,x):10*log10(($1)^2/i)" else 0 fi
17874  else
17875    e[0--3] "Compute the "$!x$!" matrix of PSNR values, from image$?."
17876    noarg
17877    if $! $!,1,1,1,iM#x +mse[^-1] f. ">x==y?inf:y>x?i(y,x):10*log10(max(i[#-2,x],i[#-2,y])^2/i)" rm.. else 0 fi
17878  fi
17879  nm. [PSNR]
17880
17881psnr : skip "${1=}"
17882  if isnum("$1")
17883    e[0--3] "Compute the "$!x$!" matrix of PSNR values, from image$? with maximum value $1."
17884    +psnr $1
17885  else
17886    e[0--3] "Compute the "$!x$!" matrix of PSNR values, from image$?."
17887    noarg +psnr
17888  fi
17889  k.
17890
17891#@cli segment_watershed : _threshold>=0
17892#@cli : Apply watershed segmentation on selected images.
17893#@cli : Default values: 'threshold=2'.
17894#@cli : $ image.jpg segment_watershed 2
17895segment_watershed : check "${1=2}>=0"
17896  e[^-1] "Apply watershed segmentation on image$?, with edge threshold $1."
17897  repeat $! l[$>]
17898    min={im}
17899    + {1+$min} +gradient_norm
17900    +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))"
17901    *[-3,-1] *. -1 watershed.. . rm.
17902    - {1+$min}
17903  endl done
17904
17905#@cli shape2bump : _resolution>=0,0<=_weight_avg_max_avg<=1,_dilation,_smoothness>=0
17906#@cli : Estimate bumpmap from binary shape in selected images.
17907#@cli : Default value: 'resolution=256', 'weight_avg_max=0.75', 'dilation=0' and 'smoothness=100'.
17908shape2bump : check "isint(${1=256}) && $1>=0 && ${2=0.75}>=0 && $2<=1 && isnum(${3=0}) && ${4=100}>=0"
17909  e[^-1] "Estimate bumpmap from binary shape in image$?, using "\
17910         ${"if $1 u \"resolution $1\" else u \"full resolution\" fi"}", avg/max weight $2, dilation $3
17911          and smoothness $4."
17912  repeat $!
17913    +l[$>]
17914      norm > 0
17915      siz={[w,h]}
17916
17917      # Generate skeleton.
17918      distance 0 + $3
17919      +f. "const boundary = 1;
17920           (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))"
17921
17922      # Downsize for faster computation.
17923      is_resized=0
17924      if $1" && "max(w,h)>$1 rr2d $1,$1,0,2 gt. 0 thinning. 1 *.. {$1/max($siz)} is_resized=1 fi
17925
17926      # Generate z-map.
17927      +f. 0 .x2
17928      f[1] "*if (i,
17929        r = i(#0,x,y);
17930        ir = floor(r);
17931        r2 = r^2;
17932        for (q = -ir, q<=ir, ++q,
17933          Y = y + q;
17934          for (p = -ir, p<=ir, ++p,
17935            X = x + p;
17936            dist = norm(p,q);
17937            if (dist<r,
17938              elev = sqrt(r2 - dist^2);
17939              i(#2,X,Y) = max(i(#2,X,Y),elev);
17940              if ($2<1, i(#3,X,Y) += elev; ++i(#4,X,Y));
17941            );
17942          )
17943        );
17944      ); i"
17945
17946      if $2<1 M={-3,iM} max. 1 /[-2,-1] n. 0,$M j. ..,0,0,0,0,$2
17947      else rm[-2,-1]
17948      fi
17949      k.
17950      if $is_resized *. {max([$siz]/[w,h])} r $siz,1,1,5 c 0,100% fi
17951    endl
17952    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
17953    k.
17954  done
17955
17956#@cli skeleton : _boundary_conditions={ 0=dirichlet | 1=neumann }
17957#@cli : Compute skeleton of binary shapes using distance transform and constrained thinning.
17958#@cli : Default value: 'boundary_conditions=1'.
17959#@cli : $ shape_cupid 320 +skeleton 0
17960skeleton : check "!isnum(${1=1}) || ($1>=0 && $1<=1)"
17961  if isnum($1) bc=$1 else bc=1 noarg fi
17962  e[^-1] "Compute skeleton of binary image$? with "${"arg 1+"$bc",dirichlet,neumann"}" boundary conditions."
17963  repeat $! l[$>] s c repeat $! l[$>]
17964                    # [0] = 2D binary shape
17965    1,16,1,2        # [1] = List of boundary pixels
17966    1,16,1,2        # [2] = List of boundary pixels (next iteration)
17967
17968    # [3] = Pixels on median axis.
17969    +distance[0] 0
17970    f. "const boundary = 1;
17971        (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))"
17972
17973    # Extract boundary pixels.
17974    f[0] ">const boundary = "$bc";
17975          i && (!j(-1) || !j(1) || !j(0,-1) || !j(0,1))?da_push(#1,[x,y]); i;"
17976
17977    # Run thinning algorithm.
17978    eval "
17979      const boundary = "$bc";
17980
17981      # Lookup tables for detecting the simple points.
17982      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,
17983                       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,
17984                       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,
17985                       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,
17986                       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,
17987                       1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,0 ];
17988      dotm = [ 128,64,32,16,0,8,4,2,1 ];
17989      is_removed = 1;
17990
17991      # Start thinning iterations.
17992      while (is_removed,
17993        is_removed = 0;
17994        N = da_size(#1);
17995        repeat (N,n,
17996          xc = i(#1,0,n,0,0);
17997          yc = i(#1,0,n,0,1);
17998          icc = i(#0,xc,yc);
17999          (icc && !i(#3,xc,yc))?(
18000            xp = xc - 1; yp = yc - 1;
18001            xn = xc + 1; yn = yc + 1;
18002            ref(crop(#0,xp,yp,3,3),V);
18003            val = dot(dotm,V>0);
18004            is_removable[val]?(
18005              i(#0,xc,yc) = 0;
18006              is_removed = 1;
18007              V[3]==1?(da_push(#2,[xp,yc]); i(#0,xp,yc) = 2);
18008              V[5]==1?(da_push(#2,[xn,yc]); i(#0,xn,yc) = 2);
18009              V[1]==1?(da_push(#2,[xc,yp]); i(#0,xc,yp) = 2);
18010              V[7]==1?(da_push(#2,[xc,yn]); i(#0,xc,yn) = 2);
18011            ):(da_push(#2,[xc,yc]); i(#0,xc,yc) = 2);
18012          )
18013        );
18014        resize(#1,1,h(#2),1,2,0);
18015        copy(i(#1),i(#2),2*h(#2));
18016        i[#2,h(#2)-1] = 0;
18017      );"
18018    k[0] > 0 thinning
18019  endl done a c endl done
18020
18021skeleton_v236 : check ${1=0}>=0
18022  e[^-1] "Compute skeleton of binary image$?."
18023  distance 0 b $1 sharpen 1e10 >= 100%
18024  repeat $! +erode[$>] 2 -[$>,-1] done
18025
18026#@cli slic : size>0,_regularity>=0,_nb_iterations>0
18027#@cli : Segment selected 2D images with superpixels, using the SLIC algorithm (Simple Linear Iterative Clustering).
18028#@cli : Scalar images of increasingly labeled pixels are returned.
18029#@cli : Reference paper: Achanta, R., Shaji, A., Smith, K., Lucchi, A., Fua, P., & Susstrunk, S. (2010). \
18030# SLIC Superpixels (No. EPFL-REPORT-149300).
18031#@cli : Default values: 'size=16', 'regularity=10' and 'nb_iterations=10'.
18032#@cli : $ image.jpg +srgb2lab slic[-1] 16 +blend shapeaverage f[-2] "j(1,0)==i && j(0,1)==i" *[-1] [-2]
18033slic : check "${1=16}>0 && ${2=10}>=0 && ${3=10}>0"
18034  e[^-1] "Segment image$? using SLIC superpixels, with size $1, regularity $2 and $3 iterations."
18035  S,m,nb_iter=${1-3}
18036  repeat $! l[$>] slices 50%
18037
18038    # Initialize superpixel centers Ck.
18039    {[max(1,round(w/$S)),max(1,round(h/$S))]},1,2,"round(([x,y]+=0.5)*="$S")"
18040
18041    # Perturb the Ck towards low gradient positions.
18042    if $S>=3
18043      +b[0] 0.7 g. xy,1 a[-2,-1] c norm. # Gradient norm with forward differences
18044      f.. "
18045        const n = round("$S"/3);
18046        const n1 = int(n/2);
18047        pos = argmin(crop(#-1,i0 - n1,i1 - n1,n,n,1));
18048        dxy = [pos%n,int(pos/n)] - n1;
18049        [ cut(i0 + dxy[0],0,w#0-1), cut(i1 + dxy[1],0,h#0-1) ]"
18050      rm.
18051    fi
18052    r. {wh},1,1,2,-1
18053    100%,1,1,{0,s},"I(#0,I#1)" a[-2,-1] c # Add superpixels colors
18054    [0],[0],1,2 eval.. "I(#-1,i0,i1) = [ x + 1,1 ]; I" s. c distance. 1 *. -1 watershed.. . rm. channels. 0,1
18055
18056    # Start iteration loop.
18057    repeat $nb_iter
18058
18059      # Assign best superpixel to each pixel.
18060      sh[2] 1 f. inf rm.
18061      eval[1] "
18062        const m = "$m";
18063        const S = "$S";
18064        k = x;
18065        xk = i0;
18066        yk = i1;
18067        Ik = (I)[2,s#0];
18068        x0 = max(xk - S,0);
18069        x1 = min(xk + S,w#0 - 1);
18070        y0 = max(yk - S,0);
18071        y1 = min(yk + S,h#0 - 1);
18072        for (y = y0, y<=y1, ++y,
18073          for (x = x0, x<=x1, ++x,
18074            delta_c = norm(I(#0,x,y) - Ik);
18075            delta_s = norm(x - xk, y - yk);
18076            delta = delta_c + m/S*delta_s;
18077            if (delta<i(#-1,x,y,0,1),
18078              I(#-1,x,y) = [ k,delta ];
18079            );
18080          )
18081        );
18082      I"
18083
18084      # Update superpixels.
18085      f[1] 0 channels[1] 0,{-2,s}
18086      eval[2] "I[#1,i0]+=[ x,y,I#0,1 ];I"
18087      s[1] c,-{1,s-1} max[2] 1 /[1,2]
18088
18089    done
18090
18091    # Remove small and isolated regions (surrounded by pixels all having the same label).
18092    k[2] channels 0 label. 0,0
18093    +area 0,0 <. {$S^2/8}
18094    {0,iM+1},1,1,1,x
18095    eval[0] ">
18096      const boundary = 1;
18097      if (i[#-1,i]>=0,
18098        N = [ j(-1,0),j(0,-1),j(1,0),j(0,1) ];
18099        repeat (size(N),k,
18100          if (N[k]!=i,
18101            i[#-1,i] = i[#-1,i]==i || i[#-1,i]==N[k]?N[k]:-1
18102          );
18103        );
18104      );
18105    i"
18106    f. "i<0?0:1" +map[0] . rm.. or[-2,-1]
18107
18108    +[0] 1 100%,100% j[0] .,0,0,0,0,1,.. rm.
18109    distance. 0 *. -1 watershed.. . rm.
18110    label. 0,0
18111  endl done
18112
18113#@cli ssd_patch : [patch],_use_fourier={ 0 | 1 },_boundary_conditions
18114#@cli : Compute fields of SSD between selected images and specified patch.
18115#@cli : Argument 'boundary_conditions' is valid only when 'use_fourier=0'.
18116#@cli : 'boundary_conditions' can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
18117#@cli : Default value: 'use_fourier=0' and 'boundary_conditions=0'.
18118#@cli : $ image.jpg +crop 20%,20%,35%,35% +ssd_patch[0] [1],0,0
18119ssd_patch : check ${is_image_arg\ $1} skip ${2=0},${3=0}
18120  e[^-1] "Compute field of SSD between image$? and patch $1 using "${arg\ 1+!$2,fourier,spatial}" mode."
18121  repeat $! pass$1 0 l[$>,-1]
18122    r 100%,100%,100%,${-max_s} s c
18123    repeat $!/2 l[$>,{-1-$<}]
18124      +sqr[1] val={is} rm. # Sum J(p,q)^2
18125      +sqr[0] +f[1] 1
18126      if $2
18127        convolve_fft.. . rm. # Sum I(x+p,y+q)^2
18128        mirror[1] xyz convolve_fft[0] [1] rm[1]  # Sum I(x+p,y+q).J(p,q)
18129      else
18130        correlate.. .,$3 rm. # Sum I(x+p,y+q)^2
18131        correlate[0] [1],$3 rm[1] # Sum I(x+p,y+q).J(p,q)
18132      fi
18133      *[0] -2 +[0,1] + $val
18134    endl done +
18135  endl done
18136
18137#@cli thinning : _boundary_conditions={ 0=dirichlet | 1=neumann }
18138#@cli : Compute skeleton of binary shapes using morphological thinning
18139#@cli : (beware, this is a quite slow iterative process)
18140#@cli : Default value: 'boundary_conditions=1'.
18141#@cli : $ shape_cupid 320 +thinning
18142thinning : check "!isnum(${1=1}) || ($1>=0 && $1<=1)"
18143  if isnum($1) bc=$1 else bc=1 noarg fi
18144  e[^-1] "Apply morphological thinning to binary image$? with "\
18145         ${"arg 1+"$bc",dirichlet,neumann"}" boundary conditions."
18146  repeat $! l[$>] s c repeat $! l[$>]
18147                    # [0] = 2D binary shape (current iteration)
18148    1,16,1,2        # [1] = List of boundary pixels (current iteration)
18149    1,16,1,2        # [2] = List of pixels to remove (current iteration)
18150    1,16,1,2        # [3] = List of boundary pixels (for next iteration)
18151
18152    # Extract boundary pixels.
18153    f[0] ">const boundary = "$bc";
18154          i && (!j(-1) || !j(1) || !j(0,-1) || !j(0,1))?da_push(#1,[x,y]); i;"
18155
18156    # Run thinning algorithm.
18157    eval "
18158      const boundary = "$bc";
18159
18160      # Lookup tables for detecting the 8 3x3 hit&miss.
18161      hm_and = [ 231,189,231,189,122,91,94,218 ];
18162      hm_eq = [ 7,148,224,41,18,80,72,10 ];
18163
18164      # Start thinning iterations.
18165      dotm = [ 128,64,32,16,0,8,4,2,1 ];
18166      is_removed = vector8(0);
18167      ind = 1;
18168      nind = 3;
18169      it = 0;
18170      do (
18171        N = da_size(#ind);
18172        it8 = it%8;
18173        is_removed[it8] = 0;
18174
18175        # Find removable contour points.
18176        repeat (N,n,
18177          xc = i(#ind,0,n,0,0);
18178          yc = i(#ind,0,n,0,1);
18179          icc = i(#0,xc,yc);
18180          icc?(
18181            xp = xc - 1; yp = yc - 1;
18182            xn = xc + 1; yn = yc + 1;
18183            ref(crop(#0,xp,yp,3,3),V);
18184            val = dot(dotm,V>0);
18185            (val & hm_and[it8])==hm_eq[it8]?(
18186              da_push(#2,[xc,yc]);
18187              is_removed[it8] = 1;
18188              V[3]==1?(da_push(#nind,[xp,yc]); i(#0,xp,yc) = 2);
18189              V[5]==1?(da_push(#nind,[xn,yc]); i(#0,xn,yc) = 2);
18190              V[1]==1?(da_push(#nind,[xc,yp]); i(#0,xc,yp) = 2);
18191              V[7]==1?(da_push(#nind,[xc,yn]); i(#0,xc,yn) = 2);
18192            ):(da_push(#nind,[xc,yc]); i(#0,xc,yc) = 2);
18193          );
18194        );
18195
18196        # Re-assign value '1' to contour points.
18197        N = da_size(#nind);
18198        repeat (N,n, i(#0,i[#nind,n],i[#nind,n + h(#nind)]) = 1);
18199        _tmp = ind; ind = nind; nind = _tmp;
18200        i[#nind,h(#nind)-1] = 0;
18201
18202        # Remove matching contour points.
18203        N = da_size(#2);
18204        repeat (N,n, i(#0,I[#2,n]) = 0);
18205        i[#2,h(#2)-1] = 0;
18206        ++it;
18207        _(while), max(is_removed)
18208      );"
18209    k[0]
18210  endl done a c endl done
18211
18212#@cli tones : N>0
18213#@cli : Get N tones masks from selected images.
18214#@cli : $ image.jpg +tones 3
18215tones : check $1>0
18216  e[^-1] "Get $1 tones masks from image$?."
18217  norm n 0,{$1-1} round 1 repeat $! l[$<]
18218    repeat $1-1 +==[0] {1+$>} done ==[0] 0
18219  endl done
18220
18221#@cli topographic_map : _nb_levels>0,_smoothness
18222#@cli : Render selected images as topographic maps.
18223#@cli : Default values: 'nb_levels=16' and 'smoothness=2'.
18224#@cli : $ image.jpg topographic_map 10
18225topographic_map : check "isint(${1=16}) && $1>0" skip ${2=2}
18226  e[^-1] "Render topographic maps from image$?, with $1 levels and smoothness $2."
18227  repeat $! l[$>]
18228    +b $2 isophotes. $1 compose_channels. + ==. 0 blend shapeaverage0
18229  endl done
18230
18231#@cli tsp : _precision>=0
18232#@cli : Try to solve the 'travelling salesman' problem, using a combination of greedy search and 2-opt algorithms.
18233#@cli : Selected images must have dimensions Nx1x1xC to represent N cities each with C-dimensional coordinates.
18234#@cli : This command re-order the selected data along the x-axis so that the point sequence becomes a shortest path.
18235#@cli : Default values: 'precision=256'.
18236#@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 \
18237# line[-1] {0,boundary=2;[I[$>],I[$>+1]]},1,255,128,0 done keep[-1]
18238tsp : check "${1=256}>=0"
18239  e[^-1] "Try to solve the 'travelling salesman' problem for pointcloud$?, with precision $1."
18240  repeat $! l[$>] n={n}
18241    if h>1" || "d>1 error[0--4] "Selected image '"{n}"' has invalid dimensions ("{[w,h,d,s]}")." fi
18242
18243    # Find initial estimate, using greedy nearest neighbor algorithm.
18244    eval "
18245      is_used = vectorw(0);
18246      next = vectorw(-1);
18247      n_initial = n_current = round(u(0,w-1));
18248      do (
18249        is_used[n_current] = 1;
18250        P_current = I[n_current];
18251        n_next = -1; dmin = inf;
18252        repeat (w,n,
18253          if (!is_used[n],
18254            d = norm(I[n] - P_current);
18255            if (d<dmin, dmin = d; n_next = n);
18256          );
18257        );
18258        if (n_next<0, next[n_current] = n_initial; break());
18259        next[n_current] = n_next;
18260        n_current = n_next;
18261        _(while), 1
18262      );
18263      resize(#-1,w,1,1,s+1,0);
18264      copy(i(0,0,0,s),next,w)"
18265    100%,1,1,{s-1},">begin(ind = 0); val = I[#0,ind]; ind = val[s]; val" rm..
18266
18267    # Improving initial estimate iteratively by 2-opt algorithm.
18268    eval "
18269      is_improved = 1;
18270      while (is_improved,
18271        is_improved = 0;
18272        nb_try = $1*w;
18273        repeat (nb_try,try,
18274          r = round(max(8,0.5*w*(try/nb_try)^0.25));
18275          i = round(u(0,w-1));
18276          ni = (i+1)%w;
18277          pi = (i-1)%w;
18278          do (j = (i + round(u(-r,r)))%w; _(while), j==i || j==ni || j==pi);
18279          nj = (j+1)%w;
18280          P_i = I[i];
18281          P_ni = I[ni];
18282          P_j = I[j];
18283          P_nj = I[nj];
18284          dist_ini = norm(P_ni - P_i);
18285          dist_jnj = norm(P_nj - P_j);
18286          dist_ij = norm(P_j - P_i);
18287          dist_ninj = norm(P_nj - P_ni);
18288          if (dist_ij + dist_ninj<dist_ini + dist_jnj,
18289            mi = (min(i,j) + 1)%w;
18290            mj = max(i,j);
18291            oi = min(mi,mj);
18292            oj = max(mi,mj);
18293            delta = oj - oi + 1;
18294            repeat (s,c, copy(i(oi,0,0,c),i(oj,0,0,c),delta,1,-1));  # Reverse path between nodes.
18295            is_improved = 1;
18296          );
18297        );
18298      )"
18299    nm $n
18300  endl done
18301
18302#@cli variance_patch : _patch_size>=1
18303#@cli : Compute variance of each images patch centered at (x,y), in selected images.
18304#@cli : Default value: 'patch_size=16'
18305#@cli : $ image.jpg +variance_patch
18306variance_patch : check "isint(${1=16}) && $1>=1"
18307  e[^-1] "Compute variance of image patches in image$?, with patch size $1."
18308  $1,$1,1,1,1 normalize_sum.
18309  repeat $!-1 l[$>,-1]
18310    +sqr[0] convolve[0,2] [1]
18311    sqr[0] rv[0,2] -[0,2] max[0] 0
18312  endl done rm.
18313
18314#---------------------------------
18315#
18316#@cli :: Image Drawing
18317#
18318#---------------------------------
18319
18320#@cli arrow : x0[%],y0[%],x1[%],y1[%],_thickness[%]>=0,_head_length[%]>=0,_head_thickness[%]>=0,_opacity,\
18321# _pattern,_color1,...
18322#@cli : Draw specified arrow on selected images.
18323#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
18324#@cli : even if a color is specified. If a pattern is specified, the arrow is
18325#@cli : drawn outlined instead of filled.
18326#@cli : Default values: 'thickness=1%', 'head_length=10%', 'head_thickness=3%', 'opacity=1', 'pattern=(undefined)' \
18327# and 'color1=0'.
18328#@cli : $ 400,400,1,3 repeat 100 arrow 50%,50%,{u(100)}%,{u(100)}%,3,20,10,0.3,${-rgb} done
18329arrow : check "${5=1%}>=0 && ${6=10%}>=0 && ${7=3%}" skip ${8=1}
18330  e[^-1] "Draw arrow in image$?, from ($1,$2) to ($3,$4), with thickness $5, head length $6,
18331          head_thickness $7 and opacity $8."
18332  repeat $! l[$>]
18333    polygon. 7,{"
18334      x0 = "${"is_percent $1"}"?(w-1)*$1:$1;
18335      y0 = "${"is_percent $2"}"?(h-1)*$2:$2;
18336      x1 = "${"is_percent $3"}"?(w-1)*$3:$3;
18337      y1 = "${"is_percent $4"}"?(h-1)*$4:$4;
18338      p0 = [x0,y0];
18339      dp = [x1,y1]-=p0;
18340      l = norm2(dp);                       # arrow length
18341      t = "${"is_percent $5"}"?l*$5:$5;   # thickness
18342      hl = "${"is_percent $6"}"?l*$6:$6;  # head length
18343      ht = "${"is_percent $7"}"?l*$7:$7;  # head thickness
18344      lmhl = l - hl;
18345      X = mul([0,-t,lmhl,-t,lmhl,-ht,l,0,lmhl,ht,lmhl,t,0,t],rot(-atan2(dp[1],dp[0])),2);
18346      X+=[p0,p0,p0,p0,p0,p0,p0]"},${8--1}
18347  endl done
18348
18349#@cli axes : x0,x1,y0,y1,_font_height>=0,_opacity,_pattern,_color1,...
18350#@cli : Draw xy-axes on selected images.
18351#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
18352#@cli : even if a color is specified.
18353#@cli : To draw only one x-axis at row Y, set both 'y0' and 'y1' to Y.
18354#@cli : To draw only one y-axis at column X, set both 'x0' and 'x1' to X.
18355#@cli : Default values: 'font_height=14', 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
18356#@cli : $ 400,400,1,3,255 axes -1,1,1,-1
18357axes : check "isint(${5=14}) && $5>=0 && ${6=1}>=0" skip ${7=0},${8=0}
18358  if ${"is_pattern \"$7\""}
18359    e[0--3] "Draw xy-axes on image$?, with x-range ($1,$2), y-range ($3,$4), font height $5, opacity $6,
18360             pattern $7 and color (${8--1})."
18361    pattern=$7 color=${8--1}
18362  else
18363    e[0--3] "Draw xy-axes on image$?, with x-range ($1,$2), y-range ($3,$4), font height $5, opacity $6
18364             and color (${7--1})."
18365    pattern=0xFFFFFFFF color=${7--1}
18366  fi
18367  if !$5" || "!$6 return fi
18368  mx={min($1,$2)} Mx={max($1,$2)}
18369  my={min($3,$4)} My={max($3,$4)}
18370
18371  # Start drawing axes on selected images.
18372  repeat $! l[$>]
18373    w1={0,w-1} h1={0,h-1}
18374
18375    # Determine number of axes tick marks.
18376    if $1!=$2 u=${"_axes[] $1,$2,{0.3*w/$5}"} offx={arg(1,$u)} deltax={arg(2,$u)} fi
18377    if $3!=$4 u=${"_axes[] $3,$4,{0.3*h/$5}"} offy={arg(1,$u)} deltay={arg(2,$u)} fi
18378
18379    # Draw x-axis.
18380    is_0x=0
18381    if $3==$4 y0=$3 else y0={v=-($my)*$h1/($My-$my);if($4>=$3,v,$h1-v)} fi
18382    sty={if($y0>$h1-$5,-1,1)}
18383
18384    if $1!=$2" && "$y0>=0" && "$y0<=$h1
18385      line 0,$y0,$w1,$y0,$6,$pattern,$color
18386      4,4,1,1,x<=y +mirror. y rows. 1,3 a[-2,-1] y .,.,1,[0] fc. $color
18387      if $2>=$1 j[0] .,{$w1-3},{$y0-3},0,0,$6,..
18388      else mirror.. x j[0] .,0,{$y0-3},0,0,$6,..
18389      fi
18390      rm[-2,-1]
18391
18392      i=0 do
18393        val={_$offx+$i*$deltax} i+=1
18394        if $val>=$mx" && "$val<=$Mx
18395          x={v=($val-$mx)*$w1/($Mx-$mx);if($2>=$1,v,$w1-v)}
18396          line $x,{$y0-1},$x,{$y0+1},$6,$pattern,$color
18397          if $val
18398            0 t. $val,0,0,$5,1,1 100%,100%,1,[0] fc. $color
18399            j[0] .,{max(0,min($w1-w,$x-w/2))},{if($sty>0,$y0+3,$y0-h-3)},0,0,$6,.. rm[-2,-1]
18400          else is_0x=1
18401          fi
18402        fi
18403      while $val<$Mx
18404    fi
18405
18406    # Draw y-axis.
18407    is_0y=0
18408    if $1==$2 x0=$1 else x0={v=-($mx)*$w1/($Mx-$mx);if($2>=$1,v,$w1-v)} fi
18409    stx={if($x0>$w1-$5,-1,1)}
18410
18411    if $3!=$4" && "$x0>=0" && "$x0<=$w1
18412      line $x0,0,$x0,$h1,$6,$pattern,$color
18413      4,4,1,1,x>=y +mirror. x z. 1,3 a[-2,-1] x .,.,1,[0] fc. $color
18414      if $4>=$3 j[0] .,{$x0-3},{$h1-3},0,0,$6,..
18415      else mirror.. y j[0] .,{$x0-3},0,0,0,$6,..
18416      fi
18417      rm[-2,-1]
18418
18419      i=0 do
18420        val={_$offy+$i*$deltay} i+=1
18421        if $val>=$my" && "$val<=$My
18422          y={v=($val-$my)*$h1/($My-$my);if($4>=$3,v,$h1-v)}
18423          line {$x0-1},$y,{$x0+1},$y,$6,$pattern,$color
18424          if $val
18425            0 t. $val,0,0,$5,1,1 100%,100%,1,[0] fc. $color
18426            j[0] .,{if($stx>0,$x0+6,$x0-w-6)},{max(0,min($h1-h,$y-h/2))},0,0,$6,.. rm[-2,-1]
18427          else is_0y=1
18428          fi
18429        fi
18430      while $val<$My
18431    fi
18432
18433    # Draw origin, if necessary.
18434    if $is_0x" || "$is_0y
18435      0 t. 0,0,0,$5,1,1 100%,100%,1,[0] fc. $color
18436      j[0] .,{if($stx>0,$x0+6,$x0-w-6)},{if($sty>0,$y0+3,$y0-h-3)},0,0,$6,.. rm[-2,-1]
18437    fi
18438
18439  endl done
18440
18441# Return optimal (offset0,scale) to display input (min,max) values.
18442# $1 = min value, $2 = max value, $3 = max number of tags(>=1).
18443_axes :
18444  n={max(1,round($3))}
18445  d={abs($2-$1)/($n-1)}
18446  s={10^round(log10($d))}
18447  m={round(min($1,$2),$s,-1)}
18448  M={round(max($1,$2),$s,1)}
18449  do N={1+round(($M-$m)/$s,1,1)} s={2*$s} while $N>$n
18450  u $m,{$s/2}
18451
18452#@cli ball : _size>0, _R,_G,_B,0<=_specular_light<=8,0<=_specular_size<=8,_shadow>=0
18453#@cli : Input a 2D RGBA colored ball sprite.
18454#@cli : Default values: 'size=64', 'R=255', 'G=R', 'B=R', 'specular_light=0.8', 'specular_size=1' and 'shading=1.5'.
18455#@cli : $ repeat 9 ball {1.5^($>+2)},${-rgb} done append x
18456+ball : 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}
18457  e[^-1] "Input $1x$1 ball with color (${2-4}), specular light $5, specular size $6 and shadow factor $7."
18458  l[]
18459  {2*$1},{2*$1} = 1,65%,30% distance 1 * -1
18460  +n 0,1 ^[1] $7 *[1] 1.4 +*[1] $3 +*[1] $4 *[1] $2 a[^0] c
18461  >=[0] {100-10*$6}% b[0] {3*$6}% n[0] 0,{$5*255} rv + c 0,255
18462  100%,100% circle[1] 50%,50%,34%,1,1 *[0] [1] *. 255 a c
18463  r $1,$1,1,4,2
18464  endl
18465
18466# chromeball64x64 : Output a 64x64 GreyA chrome ball sprite.
18467# ${1-3} = R,G,B
18468chromeball64x64 :
18469  base642img[] \
18470"MiB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgNjQgMSAyICMzMzY0CnicxZl5VJNnvsfT0mM7ntPp9N7paefe6ZzOmWlnemfGmTpzer0dO1W"\
18471"RnQCBQPZ9TwhhDQkYwyKQAAlbEjZBthgRRBahFamAggp1KS7QIgKyk7DIpoCQd54XtComyPLH/f7xvn/k/Xyf3/N7n+X35EUgLOqN9z/DHcqtaW69cq"\
18472"EmK8z7s3csP2ZZO/7ILGzpHhwaHBzoHxgA9/vt55M8fvPaxuh37NOu9AyNjBpNY+PjE+OmkYGuOzfb73XURu1589X0zx2PXu8ZMo0Dbnh41DQxMT5mH"\
18473"O7vam/v7O5sUH35Cgebvyiaf+g3jRuHB/oGjeMPpqamHkyMDvb3g3iG+7tvnjm8a71e/Af+5PXOYdPo4MCwaWr24cO5ubnZ2ekHY6PDgwNDRtNQT9u3"\
18474"mRirqXztt5Laa10jo0N9fcaZ+UWghflHwGBudnpywjTUNzjcd/faN3kBH1vGX98VWdd6b2B4oKt3fH7JbF5+vDC/ADQPm0xPTRr779/vuNZUdUzyT0t"\
18475"9eON/5Webb3f8eOdqW9/Eo2XI/Gh6auYxbAMMHsIO48OD/TcvnCvNltjbvIz/I/RMXVPTlaa6xrauwbG55eU5wAObpZmJialpoJnpSZCZ63WV+rRgxz"\
18476"fW9n0Xr/RsZVX1mdOV9a237o3OLT+amJw3Q9DS3IRxdNQ4NmYcn4YN7l8+U5ybIDq4pgsf4fPrjhcWFhScqL54vWt0Zn5++sHcotkMLc1OjptGR4yjw"\
18477"6MPZqbBu+yoLyvURfjueQF/z1V98YRGo8vWV128PTTzeGlpeWkZtA5B5sVHczNTD4CmZ+Zmp8b6Oy7XFB9NDOV+8hz+1j7Jt9+kKVIyC0433DItmiHz"\
18478"CvtEZvPSit/j+bnpsYGbF6tL8tIiRfRn4+C1PzGKWwuVCRm5xWevDS5CVmRemp0Y6m1rqCrOT1eKBS4/peA/XZKu1mkVKVn5ZQ0/TJmt8dDSeO8PNy6"\
18479"fK9fnZqllIu4fnuA2u4VnO0rU8WmZheUN9x5ZxaHFwatN9bVnSgtzstKOBAkIO1b595007W25CQkpmUUwb7V981T3pXM1VWXFBUcztcpQIWfX6rj9G6"\
18480"nqXotGmZCUDvjO2cWFhWXL/OTdK9/WlJ8yFORkalXhIt5qAL/4l/hSz8UUhVKlKyiv7xibmRibsxzDo77vL56tKDXk52TqUuQiAetTmP/USXm1pzFZo"\
18481"VADvuHOkGlocGzJIr881tlaV1kC+KwMXZS/gI0Er+DNL7zUV+9d1CgUqvTC8gsdIyO9vabHljMwP3S7sbr0eN7R7KN5sf4CLu2XIHv7MXFX7rZmg/jT"\
18482"CysutBuNvT2j81Yy+OBea22ZIe9YblFpPOAZuxGIXQd9ws/fbTMoFIm6/NPfXu8Z6e8bsZIAEMCtxqoSvf54WbUK5pEIm332nsJTP7RXqRSJaTkl3zS"\
18483"13e3pG5mxnABoebKrpe5MWXllXXUc4JnEn73tZo+kpH9/u+FofGJSpr6ytvlGR49VHloytrfW19bVXy6RifgcJuXXv8E6OKIP1d9oOa1JUGtyT5yuvX"\
18484"yza8gqDz0e6/zuUvOVCxkhfjwOk/znv1Id7d0Z+ZcvNeiTEsEELK6qb7kzMG2Vh5ZMP95ovXIqOsCXx2aQ93zFdbZz8ZKX19bX5CSqtbmG01+f/+6u0"\
18485"eokBKNo4PZ3X6cG+/G5LDrZ1t3fzdbBjZqgLz97KkOdmlVUUl7deKN7fNHqNFgauX0+O9zfl8dh0chIfCjazs4ZxY07WlxWlKXRZBcYSmsab9wzPlxc"\
18486"smzxeKhFHx0oFHDZTBoJxZARHA46ItG86IxCg/5Yhi47/3hpdX1L293eQdP0/Mse5ocdlUlhAQKQPTqV6MmNYSDt7J3d0dzIjAKDoSA3J6/wRGlV9df"\
18487"nGi5dvdU1MDa9ZjbO99bnxAGey2bQyHiUUCVEO9k5unp4sw9r8/XHiwoLi/T6E8XFJWUV1bUXWm/c6TbNLS4/DWN5prvpZDrMc1gMKgmHZGskZHdHey"\
18488"ekhxctVJVTdBzIcBy20RtOlFR8Xdf43Z2eQePk9KPHy0vzk73f158u0MaGBwhA78lErAMxK5qHQdo7OiM90ARfeVoewE+A5oEMer3hZFnVuebrt37s6"\
18489"ukb6Ou80VRbebIgKzkmPIDPolNJeMy/XHJVEgba2cEJGHhhqP6RqcdAwydLTpVXlJWcMBiKSyrONjRdam48X1dzSl+QBy8d8VHSAB6LRiHhvP/+xVHN"\
18490"kQCKm5PjioEPjuYXrtTmFukNJWWnSk8CA8PJU2WlhsLcrAxtilqVmJAQHxd9ONSfx6BRyVj0xx+mZKkihBgXJydnFzcPT28snswSSaMTkjOOFR4HPSg"\
18491"qKsjNTE2Ii4mOlB+WyQ7LwfWQNNiPS6fRKViv99+JyNaqZCwvV8C7url7on2weBKFwRUGS+VH4hRKpTI2SiYVBwXCCgoODgkJDg4KFAk4DCabhkHtQH"\
18492"DS0zPigyhebq6urkg3d5SXNwZHIJKpNPAAl8fj8/krFz5fIPAV+MISCAQ8LpvN51N99iMQX4J9QxslJPt4uiNh3tMLhIDDE0kUKpVGpzOYLBaLvSIOE"\
18493"HxnsZhMFs8vwI/s80cE4r9CFGk6VbiAjPP2RHl4oFBeXmhvDHAgEElkMmxCg22eiQ7E5PmFSPwIaHgPJUmOJCVGifl08Dp90F5eIABvHwwWi3tqQQEm"\
18494"K6I9EZ3FFYllMi7uILz+/40TEBkXJfXns8GIIOJxGB9v72cGRBKRBExgARtgRaMzuQJ/SUS0mIb9aGXzxzOE4TIJWE/4YErCrxW4AAEcTyAQSLBWcAq"\
18495"VQgX54Pj6B4VHxcoFBORqFfQ5nswPDPL35cLpFfBXXOg0Gtzac4JRFofL9w2UhMuiFTEhVPyTQvAtNAbDEAj5bI6vn5/IPyAwQOQH+/B4XM5q5tnwKw"\
18496"Ao+DU4TB4dHaOIk7II7k+LsD97uLmTWWwGnSMQ+geGhIaKxWCYgMESFBTgD8tPKBSKwA8SqVQep1QqYqOlXCLuw6f1h42DowMSR6WQaWy+UBQUKpWAB"\
18497"8ElFHYSiyVhEnCXyCIjo6JiVMlqRbRczCUTv3pWP/3C1fagiw/INoXJEwiD4KclUglMAwupTA4GvTwyVhGfqErVpSnB7GGTSd4/e64A+/jg/v0O7j44"\
18498"YMDhCQNDACgGAx0oRCyVRUYdiYlVqlN1uvRMTZxUxKURiPj/fqEA3PPVflsndx8Chc7h8EXB0jAgqXjFAPBH4hLUKdrMLF1aYmQwj0HCk6l/ebH+tLH"\
18499"be8DBHUOiMUCeRSHhh+UR0dERMqBDkXHxquRUjS5dl5oYLRawqAQsjbMXsUav79t7wBlNoIM3xfUNORyjUCYmJ6mTU5ISkmBUk5qckhgVyqdRiBgMU2"\
18500"T7cgX/+sEvv3JGE+nwdBNKY1O06ZnpWm16SoIqJS01Me5IpCyYSyHgfbywfMna6nlV/2e31xlLptEoZDpfHJWoyy3ILyjIzdKoosOCgvxYVCyYFyhPi"\
18501"lj+hSUa6BOXvUgKk4rxROMYAWFH1FpNSnJ8ZFiwL5OC9/ZCodAoT4JYEfuJFRyBeNfDwZnEpHq7ubhj6Twhn8nkMYk+aA83Dx8Sm0OlsMK12ohPreJA"\
18502"n5FcPMg0vJu9vbMXDuPh5unuam9r6+BJF/n5R6Tk5aei3loPRyB22vliUHgqztV23wEHuwOObo62SAyJzpfEZxflpBI/WJ+G9ZZdmIxL9EG5OhzYb4c"\
18503"kEJnCUHmMUp2uVXj/6tX0iv6Hmp6nUYaHHkrUKmLjlOq0rOwU6u4dG6RX9ME+ukydW1FZXl6kCafs+91mWKCdu7Hqijvj8JY9dadajf3w1cgzvUutWV"\
18504"hbdnQl/XOD9J9y5iwXTp2itzdAF1s+PKxoTPoKh7eTXwr8RZlo6+H7e9anYTX82hptE7FO6M80ibKM7yzfCA1kllvC32veIA5U+PL5/722jeMWDHY2b"\
18505"gaHoGNr+JLN4RAU/gIeulkcMiOfwz9/xaixJNOzcfDmzc3jEHTmJ/7QVnAI8n6CfzC7Nb7vyZ9pmVvDIUi4OnK2kLwnAaysiJFbxSEInsw2A1vnLwF+"\
18506"39ZxCALLcvp2eAkCsY3wIagJ8fvt4NDCTuy2eGifent8YMX2eN2mlq2XdXZwe/w2m4d6/r/5DW1Z6/DbDKClZXt8RfH2eHXE9nim2/b43e9tC596HbG"\
18507"lveepKhGIbSWACgq2beAL74IF+PrW+TJ4/2BtnbeH+R19W8VbVvfPgK3yzk/Kh+6t4c1P6wfUlvCF3T8VIKVb4ROe1U+/7N88fu3549CXm64hJv+AeF"\
18508"6CTeILTmsK0NhN4WYmYq02Y2DmvIQjEHLrH37WBk+1gCMQRCtfHdZq0s4ijkD8tXMj+P2PrODg+JXx6u3kzLpH8C+urU93e69HA9ng2q3TfbwNfIFGO"\
18509"Fda7sUF0kZP8L8S1K05Ay80hP52g/CqdnzOTa1o6QFqqdb57dlp7bl/A5UFagkxIDQ1IDEgMSAjNTIKeJxz941i0M/Iz03VTykpTs4ozS3Qd0ktzi7J"\
18510"L9BPzigCiscXF2SkFqXqFeSlMwAAcSYQbQ=="
18511  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]
18512  c.. 0,255 a[-2,-1] c
18513
18514#@cli chessboard : size1>0,_size2>0,_offset1,_offset2,_angle,_opacity,_color1,...,_color2,...
18515#@cli : Draw chessboard on selected images.
18516#@cli : Default values: 'size2=size1', 'offset1=offset2=0', 'angle=0', 'opacity=1', 'color1=0' and 'color2=255'.
18517#@cli : $ image.jpg chessboard 32,32,0,0,25,0.3,255,128,0,0,128,255
18518chessboard : check "$1>0 && ${2=$1}>0" skip ${3=0},${4=0},${5=0},${6=1},${7=0},${8=255}
18519  e[^-1] "Draw chessboard on image$?, with sizes ($1,$2), offsets ($3,$4), angle $5 deg., opacity $6 and
18520          colors (${7--1})."
18521  i[0] (${7--1}) r[0] {{0,w}/2},1,1,2,-1 permute[0] cyzx
18522  repeat $!-1
18523    w={w} h={h} theta={$5*pi/180}
18524    ($3,{$3+$w-1};$3,{$3+$w-1}^$4,$4;{$4+$h-1},{$4+$h-1}) r. $w,$h,1,2,3
18525    r. {$w*$h},2,1,1,-1
18526    i.. ({cos($theta)},{-sin($theta)};{sin($theta)},{cos($theta)}) m*[-2,-1]
18527    r. $w,$h,1,2,-1
18528    %. {$1+$2} >=. $1 s. c xor[-2,-1] map. [0] r. 100%,100%,1,..
18529    j.. .,0,0,0,0,$6 rm.
18530  mv. 1 done rm[0]
18531
18532#@cli cie1931
18533#@cli : Draw CIE-1931 chromaticity diagram on selected images.
18534#@cli : $ 500,400,1,3 cie1931
18535cie1931 :
18536  e[^-1] "Draw CIE-1931 chromaticity diagram on image$?."
18537
18538  # Generate convex hull of visible colors, as a 3D object.
18539  (67.5;73.5;109.5;103.5;51.5;100.5;37;36)  # Header
18540  (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.
18541  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;\
18542  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;\
18543  441,279,0;475,313,0;509,347,0;731,568,0)
18544  xM=731 yM=829
18545  2,{h-1},1,1,3,0 1,{h},1,1,'y' ++. 1 %. {h} +[-2,-1] 1 a[-3--1] x  # Primitives.
18546  3,{h},1,1,160 1,{h},1,1,1  # Colors + opacities.
18547  y[-4--2] a[-5--1] y mv. 0
18548
18549  # Generate RGB triangle of displayable colors.
18550  xR=636 yR=504 xG=297 yG=234 xB=147 yB=774
18551  512,512,1,3 triangle_shade. 0,0,{w-1},0,0,{h-1},""255,0,0,""0,255,0,""0,0,255 rgb2srgb.
18552  +compose_channels. max +. 1e-8 /[-2,-1] *. 255
18553  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;\
18554       9;0;1;2;0;0;511;0;0;511;-128;512;512;3)
18555  y. (1) a[-3--1] y mv. 1
18556
18557  # Draw chroma diagram.
18558  repeat $!-2
18559    to_rgb. fc. 255,255,255 grid. 10%,10%,0,0,0.3,0xCCCCCCCC,1,0
18560    100%,100%,1,3
18561    +*3d[0,1] {(w-8)/$xM},{(h-32)/$yM}
18562    j3d... .,2,30,0,1,2
18563    +!=... 0 distance. 1 *. -1 watershed[-4] . rm. /... 1.5
18564    j3d... .,2,30,0,1,2
18565    p3d. 1 p3d. 2 col3d. 128 j3d... .,2,30,0,1,1 rm.
18566    {-2,w},{-2,h} j3d. ..,2,30,0,1,2 rm..
18567    +erode. 4 -. .. ==. 0 *[-3,-1]
18568    a[-2,-1] c blend[-2,-1] alpha
18569    100%,100%,1,1,255 axes. 0,0.75,0.85,0,14,1 +erode. 3 negate. to_rgb..
18570    j... ..,0,0,0,0,1,.,400 rm[-2,-1]
18571  mv. 2 done rm[0,1]
18572
18573#@cli circle : x[%],y[%],R[%],_opacity,_pattern,_color1,...
18574#@cli : Draw specified colored circle on selected images.
18575#@cli : A radius of '100%' stands for 'sqrt(width^2+height^2)'.
18576#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
18577#@cli : even if a color is specified. If a pattern is specified, the circle is
18578#@cli : drawn outlined instead of filled.
18579#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
18580#@cli : $ image.jpg repeat 300 circle {u(100)}%,{u(100)}%,{u(30)},0.3,${-rgb} done circle 50%,50%,100,0.7,255
18581circle : skip ${4=1},${5=0},${6=0}
18582  if ${"is_pattern \"$5\""}
18583    e[0--3] "Draw outlined circle at ($1,$2) with radius $3 on image$?, with opacity $4, pattern $5 and
18584             color (${6--1})."
18585  else
18586    e[0--3] "Draw filled circle at ($1,$2) with radius $3 on image$?, with opacity $4 and color (${5--1})."
18587  fi
18588  ellipse $1,$2,$3,$3,0,${4--1}
18589
18590#@cli close_binary : 0<=_endpoint_rate<=100,_endpoint_connectivity>=0,_spline_distmax>=0,_segment_distmax>=0,\
18591# 0<=_spline_anglemax<=180,_spline_roundness>=0,_area_min>=0,_allow_self_intersection={ 0 | 1 }
18592#@cli : Automatically close open shapes in binary images (defining white strokes on black background).
18593#@cli : Default values: 'endpoint_rate=75', 'endpoint_connectivity=2', 'spline_distmax=80', 'segment_distmax=20', \
18594# 'spline_anglemax=90', 'spline_roundness=1','area_min=100', 'allow_self_intersection=1'.
18595close_binary :
18596  check "${1=75}>=0 && $1<=100 && ${2=2}>=0 && ${3=80}>=0 && ${4=20}>=0 && ${5=90}>=0 && $5<=180 && ${6=1}>=0 &&
18597         ${7=100}>=0 && isnum(${8=1})"
18598  e[^-1] "Close open shapes in binary image$?, with endpoint rate $1, endpoint connectivity $2, spline max distance $3,
18599          segment max distance $4, spline max angle $5, spline roundness $6, area min $7 and self intersections "\
18600          ${"arg 1+!$8,allowed,\"not allowed\""}"."
18601
18602  # Set algorithm parameters.
18603  endpoint_threshold={100-$1}       # in [0,100]
18604  endpoint_connectivity={round($2)}
18605  spline_distmax=$3                 # in px
18606  segment_distmax=$4                # in px
18607  spline_anglemax=$5                # in deg
18608  spline_roundness=$6
18609  area_min=$7                       # in px
18610  allow_self_intersections={!!$8}   # 0 or 1
18611
18612  # Define useful functions to navigate through edgels.
18613  _edgel_lib="
18614    begin(
18615      pn = [ 0,1,1,1,-1,0,-1,1,0,-1,-1,-1,1,0,1,-1 ];
18616      pp = [ 0,-1,1,-1,1,0,1,1,0,1,-1,1,-1,0,-1,-1 ];
18617    );
18618
18619    next(img,p) = ( # Used when shape is made of '!=0' (in 8-connexity)
18620      p0 = p#[0,2] + pn[4*p#[2],2];
18621      p1 = p#[0,2] + pn[4*p#[2] + 2,2];
18622      case = !!i(#img,p0[0],p0[1],0,0) + 2*!!i(#img,p1[0],p1[1],0,0);
18623      !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 ];
18624    );
18625
18626    previous(img,p) = ( # Used when shape is made of '!=0' (in 8-connexity)
18627      p0 = p#[0,2] + pp[4*p#[2],2];
18628      p1 = p#[0,2] + pp[4*p#[2] + 2,2];
18629      case = !!i(#img,p0[0],p0[1],0,0) + 2*!!i(#img,p1[0],p1[1],0,0);
18630      !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 ];
18631    );
18632
18633    next2(img,p) = ( # Used when shape is made of '{ 0 | 4 }' (in 4-connexity)
18634      p0 = p#[0,2] + pn[4*p#[2],2];
18635      p1 = p#[0,2] + pn[4*p#[2] + 2,2];
18636      case = !(i(#img,p0[0],p0[1],0,0)&3) + 2*!(i(#img,p1[0],p1[1],0,0)&3);
18637      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 ];
18638    );
18639
18640    next3(img,p) = ( # Used when shape is made of '{ 1 | 2 }' (in 4-connexity)
18641      p0 = p#[0,2] + pn[4*p#[2],2];
18642      p1 = p#[0,2] + pn[4*p#[2] + 2,2];
18643      val1 = i(#img,p0[0],p0[1],0,0);
18644      val2 = i(#img,p1[0],p1[1],0,0);
18645      case = (val1==1 || val1==2) + 2*(val2==1 || val2==2);
18646      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 ];
18647    );
18648
18649    vec(p) = (
18650      ang = i(p[0],p[1],0,p[2])*pi/180;
18651      [ cos(ang), sin(ang) ];
18652    );"
18653
18654  repeat $! l[$>] nm={n}
18655
18656    # Ensure input is a 2D binary image.
18657    slices 50% channels 0 > 0
18658    nm. strokes
18659
18660    # Extract primary edgel normals.
18661    100%,100%,1,4,"
18662      ref(crop(#"$strokes",x - 1,y - 1,0,0,3,3,1,1,1),N);
18663      N[4]?[ N[5]?-2:0, N[7]?-2:90, N[3]?-2:180, N[1]?-2:270 ]:[-2,-2,-2,-2];
18664    " nm. e_normals
18665
18666    # Smooth edgel normals.
18667    f[e_normals] $_edgel_lib"
18668      const boundary = 1;
18669      i<-1?-2:(
18670        ppos = npos = pos = [ x,y,c ];
18671        u = vec(pos);
18672        for (t = 1, t<=5, ++t,
18673          ppos = previous(#"$strokes",ppos);
18674          npos = next(#"$strokes",npos);
18675          if (ppos==npos, break());
18676          w = exp(-t^2/30);
18677          u+=w*vec(ppos);
18678          u+=w*vec(npos);
18679        );
18680        ang = (atan2(u[1],u[0])*180/pi)%360;
18681      )"
18682
18683    # Estimate edgel curvatures.
18684    +f[e_normals] $_edgel_lib"
18685      const boundary = 1;
18686      i<-1?-2:(
18687        pos = [ x,y,c ];
18688        ppos = previous(#"$strokes",pos);
18689        npos = next(#"$strokes",pos);
18690        pu = vec(ppos);
18691        nu = vec(npos);
18692        du = (nu - pu)/2;
18693        cr = cross([vec(pos),0],[du,0]);
18694        norm(du)*sign(cr[2]);
18695      )"
18696    nm. e_curvatures
18697
18698    # Smooth edgel curvatures.
18699    +f[e_curvatures] $_edgel_lib"
18700      const boundary = 1;
18701      i<-1?-2:(
18702        ppos = npos = pos = [ x,y,c ];
18703        val = i; sumw = 1;
18704        for (t = 1, t<=5, ++t,
18705          ppos = previous(#"$strokes",ppos);
18706          npos = next(#"$strokes",npos);
18707          if (ppos==npos, break());
18708          w = exp(-t^2/30);
18709          val+=w*i(ppos[0],ppos[1],0,ppos[2]);
18710          val+=w*i(npos[0],npos[1],0,npos[2]);
18711          sumw+=2*w;
18712        );
18713        val/sumw;
18714      )"
18715    nm. e_smooth_curvatures
18716
18717    # Estimate stroke radius.
18718    +distance[strokes] 0
18719    f. "
18720      i<1-0.01||i>1+0.01?0:(
18721        p = x; q = y; d = 1;
18722        for (is_better = 1, is_better && d<32,
18723          next_p = p;
18724          next_q = q;
18725          is_better = 0;
18726          pp = p - 1;
18727          np = p + 1;
18728          pq = q - 1;
18729          nq = q + 1;
18730          (nd = i(pp,pq))>d?(d = nd; next_p = pp; next_q = pq; is_better = 1);
18731          (nd = i(p, pq))>d?(d = nd; next_p = p;  next_q = pq; is_better = 1);
18732          (nd = i(np,pq))>d?(d = nd; next_p = np; next_q = pq; is_better = 1);
18733          (nd = i(pp,q))>d?(d = nd; next_p = pp; next_q = q; is_better = 1);
18734          (nd = i(np,q))>d?(d = nd; next_p = np; next_q = q; is_better = 1);
18735          (nd = i(pp,nq))>d?(d = nd; next_p = pp; next_q = nq; is_better = 1);
18736          (nd = i(p, nq))>d?(d = nd; next_p = p;  next_q = nq; is_better = 1);
18737          (nd = i(np,nq))>d?(d = nd; next_p = np; next_q = nq; is_better = 1);
18738          p = next_p;
18739          q = next_q;
18740        );
18741        d)"
18742    nm. stroke_radii
18743
18744    # Detect end points.
18745    compose_channels[e_smooth_curvatures] max nm[e_smooth_curvatures] smooth_curvatures
18746    +compose_channels[e_curvatures] max nm. curvatures
18747
18748    f. "i(#"$smooth_curvatures")>=("$endpoint_threshold"%)/(max(1,i(#"$stroke_radii"))) ||
18749        i>=max(0.25,"$endpoint_threshold"%)"
18750    label_fg. 0,1 nm. keypoints
18751    if iM>0
18752      {iM},1,1,3,-1 nm. keycoords
18753      f[keypoints] "> # Keep only a single point on connected regions of high curvatures.
18754        ret = 0;
18755        if (i,
18756          j = i - 1;
18757          old_max = I[#"$keycoords",j];
18758          kappa = i(#"$smooth_curvatures");
18759          if (kappa>old_max[2],
18760            I[#"$keycoords",j] = [ x,y,kappa ];
18761            i(#"$keypoints",old_max[0],old_max[1]) = 0;
18762            ret = 1;
18763          );
18764        ); ret"
18765      channels[keycoords] 0,3 # Channels 2,3 = number of connexions and normal angle for each keypoint.
18766    fi
18767    rm[smooth_curvatures,stroke_radii,keypoints]
18768
18769    # Estimate point normals.
18770    if !narg($keycoords)
18771      rm[e_normals,e_curvatures] [strokes] nm. new_strokes
18772    else
18773      f[keycoords] "
18774        P = (I)[0,2];
18775        angles = I(#"$e_normals",P);
18776        U = [ 0,0 ];
18777        repeat (size(angles),k,
18778          if (angles[k]>=0,
18779            w = max(1e-8,i(#"$e_curvatures",P[0],P[1],0,k))^2;
18780            ang = angles[k]*pi/180;
18781            U += w*[ cos(ang),sin(ang) ];
18782          );
18783        );
18784        [ P,0,(atan2(U[1],U[0])*180/pi)%360 ]"
18785      rm[e_normals,e_curvatures]
18786
18787      # Detect pairs of keypoints preserving the spline constraints.
18788      256,1,1,3 nm. keypairs
18789      f[keycoords] ">
18790        begin(ind = 0);
18791        for (nx = x + 1, nx<w, ++nx,
18792          const cosmin = cos("$spline_anglemax"*pi/180);
18793          Ic = I[x];
18794          In = I[nx];
18795          S = Ic[0,2];
18796          T = In[0,2];
18797          angS = Ic[3]*pi/180;
18798          angT = In[3]*pi/180;
18799          NS = [ cos(angS),sin(angS) ];
18800          NT = [ cos(angT),sin(angT) ];
18801          ST = T - S;
18802          dist = norm(ST);
18803          cosN = dot(NS,-NT);
18804          angN = acos(cosN)*180/pi;
18805          quality = max(0,1 - dist/"$spline_distmax") * max(0,dot(NS,ST) - dot(NT,ST))/dist * max(0,cosN - cosmin);
18806          if (quality>0,
18807            if (ind>=w(#"$keypairs"), resize(#"$keypairs",2*ind,1,1,3,0));
18808            I[#"$keypairs",ind++] = [ quality,x,nx ];
18809          );
18810        );
18811        end(resize(#"$keypairs",ind,1,1,3,0));
18812        I"
18813      if {keypairs,w} sort[keypairs] -,x
18814      else rm[keypairs]
18815      fi
18816
18817      # Find and connect splines.
18818      [strokes] nm. new_strokes
18819      ind_strokes={$allow_self_intersections?$strokes:$new_strokes}
18820
18821      if narg($keypairs)
18822        f[keypairs] ">"$_edgel_lib"
18823          for_spline(code) = for (t = 0, t<=1, t+=dt,
18824            t3 = t*(t2 = t*t);
18825            P = round(mul([t3,t2,t,1],C,2));
18826            code#;
18827            dP = abs(mul([3*t2,2*t,1,0],C,2)) + 1e-8;
18828            dt = min(dtmin,0.75/max(dP));
18829          );
18830
18831          Ic = I;
18832          indS = Ic[1];
18833          indT = Ic[2];
18834          if (i(#"$keycoords",indS,0,0,2)<"$endpoint_connectivity" &&
18835              i(#"$keycoords",indT,0,0,2)<"$endpoint_connectivity",
18836            S = I(#"$keycoords",indS)[0,2];
18837            T = I(#"$keycoords",indT)[0,2];
18838            angS = i(#"$keycoords",indS,0,0,3)*pi/180;
18839            angT = i(#"$keycoords",indT,0,0,3)*pi/180;
18840            ST = T - S;
18841            dist = "$spline_roundness"*norm(ST);
18842            NS = [ cos(angS),sin(angS) ];
18843            NT = [ cos(angT),sin(angT) ];
18844            tmax = max(abs(ST));
18845            is_cond = 1;
18846            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);
18847            dt = dtmin = 1/max(abs(T - S));
18848            current_val = 1;
18849            nb_switches = 0;
18850            for_spline(
18851              if (i(#"$ind_strokes",P,0,0)!=current_val, ++nb_switches; current_val=!current_val);
18852              if (nb_switches>2, is_cond = 0; break())
18853            );
18854            if (is_cond,
18855
18856              # Check that the new spline did not create small closed regions.
18857              const area_max_threshold = "$area_min";
18858              const area_min_threshold = 5;
18859              const max_edgels = 2*(area_max_threshold + 1);
18860
18861              if (area_max_threshold<=0,
18862                for_spline(i(#"$new_strokes",P)=1);
18863              , #else
18864                for_spline(i(#"$new_strokes",P)|=2);
18865                for_spline(
18866                  if (i(#"$new_strokes",P[0] + 1,P[1])==0,
18867                    edgels = area = 0; Q0 = Q = [ P[0] + 1,P[1],2 ];
18868                    do (
18869                      i(#"$new_strokes",Q[0],Q[1]) = 4;
18870                      area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
18871                      Q = next2(#"$new_strokes",Q),
18872                    Q!=Q0 && ++edgels<=max_edgels);
18873                    if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
18874                      is_cond = 0; break()
18875                    );
18876                  );
18877                  if (i(#"$new_strokes",P[0],P[1] + 1)==0,
18878                    edgels = area = 0; Q0 = Q = [ P[0],P[1] + 1,3 ];
18879                    do (
18880                      i(#"$new_strokes",Q[0],Q[1]) = 4;
18881                      area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
18882                      Q = next2(#"$new_strokes",Q),
18883                    Q!=Q0 && ++edgels<=max_edgels);
18884                    if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
18885                      is_cond = 0; break()
18886                    );
18887                  );
18888                  if (i(#"$new_strokes",P[0] - 1,P[1])==0,
18889                    edgels = area = 0; Q0 = Q = [ P[0] - 1,P[1],0 ];
18890                    do (
18891                      i(#"$new_strokes",Q[0],Q[1]) = 4;
18892                      area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
18893                      Q = next2(#"$new_strokes",Q),
18894                    Q!=Q0 && ++edgels<=max_edgels);
18895                    if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
18896                      is_cond = 0; break()
18897                    );
18898                  );
18899                  if (i(#"$new_strokes",P[0],P[1] - 1)==0,
18900                    edgels = area = 0; Q0 = Q = [ P[0],P[1] - 1,1 ];
18901                    do (
18902                      i(#"$new_strokes",Q[0],Q[1]) = 4;
18903                      area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
18904                      Q = next2(#"$new_strokes",Q),
18905                    Q!=Q0 && ++edgels<=max_edgels);
18906                    if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
18907                      is_cond = 0; break()
18908                    );
18909                  );
18910                0);
18911
18912                # Set or unset spline, and clean temp points used for local area estimations.
18913                for_spline(
18914                  if (i(#"$new_strokes",P[0] + 1,P[1])==4,
18915                    edgels = 0; Q0 = Q = [ P[0] + 1,P[1],2 ];
18916                    do (i(#"$new_strokes",Q[0],Q[1]) = 0; Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
18917                  );
18918                  if (i(#"$new_strokes",P[0],P[1] + 1)==4,
18919                    edgels = 0; Q0 = Q = [ P[0],P[1] + 1,3 ];
18920                    do (i(#"$new_strokes",Q[0],Q[1]) = 0; Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
18921                  );
18922                  if (i(#"$new_strokes",P[0] - 1,P[1])==4,
18923                    edgels = 0; Q0 = Q = [ P[0] - 1,P[1],0 ];
18924                    do (i(#"$new_strokes",Q[0],Q[1]) = 0; Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
18925                  );
18926                  if (i(#"$new_strokes",P[0],P[1] - 1)==4,
18927                    edgels = 0; Q0 = Q = [ P[0],P[1] - 1,1 ];
18928                    do (i(#"$new_strokes",Q[0],Q[1]) = 0; Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
18929                  );
18930                0);
18931                for_spline(i(#"$new_strokes",P) = is_cond?1:(i(#"$new_strokes",P)&1));
18932                if (is_cond,
18933                  ++i(#"$keycoords",indS,0,0,2);
18934                  ++i(#"$keycoords",indT,0,0,2);
18935                );
18936              );
18937            );
18938          ); I"
18939        rm[keypairs]
18940      fi
18941
18942      # Find and connect segments.
18943      ==[new_strokes] 0 # Negate values to consider boundary pixels (0) as strokes.
18944
18945      f[keycoords] ">"$_edgel_lib"
18946        for_segment(code) = for (t = 0, t<=tmax, ++t,
18947          P = round(S + t*ST);
18948          code#;
18949        );
18950
18951        if (i(#"$keycoords",x,0,0,2)<"$endpoint_connectivity",
18952          S = I(#"$keycoords",x)[0,2];
18953          angS = i(#"$keycoords",x,0,0,3)*pi/180;
18954          NS = [ cos(angS),sin(angS) ];
18955          ST = round(NS*"$segment_distmax");
18956          tmax = max(abs(ST));
18957          ST/=tmax;
18958          is_cond = 0;
18959          current_val = 0;
18960          nb_switches = 0;
18961          for_segment(
18962            if (i(#"$new_strokes",P,0,0)!=current_val, ++nb_switches; current_val=1 - current_val);
18963            if (nb_switches==2,is_cond = 1; break(););
18964          );
18965          tmax = t;
18966          if (is_cond,
18967
18968            # Check that the new segment did not create small closed regions.
18969            const area_max_threshold = "$area_min";
18970            const area_min_threshold = 5;
18971            const max_edgels = 2*(area_max_threshold + 1);
18972            if (area_max_threshold<=0,
18973              for_segment(i(#"$new_strokes",P)=0);
18974            , #else
18975              for_segment(i(#"$new_strokes",P)|=4);
18976              for_segment(
18977                if (i(#"$new_strokes",P[0] + 1,P[1])==1,
18978                  edgels = area = 0; Q0 = Q = [ P[0] + 1,P[1],2 ];
18979                  do (
18980                    i(#"$new_strokes",Q[0],Q[1]) = 2;
18981                    area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
18982                    Q = next3(#"$new_strokes",Q),
18983                  Q!=Q0 && ++edgels<=max_edgels);
18984                  if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
18985                    is_cond = 0; break()
18986                  );
18987                );
18988                if (i(#"$new_strokes",P[0],P[1] + 1)==1,
18989                  edgels = area = 0; Q0 = Q = [ P[0],P[1] + 1,3 ];
18990                  do (
18991                    i(#"$new_strokes",Q[0],Q[1]) = 2;
18992                    area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
18993                    Q = next3(#"$new_strokes",Q),
18994                  Q!=Q0 && ++edgels<=max_edgels);
18995                  if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
18996                    is_cond = 0; break()
18997                  );
18998                );
18999                if (i(#"$new_strokes",P[0] - 1,P[1])==1,
19000                  edgels = area = 0; Q0 = Q = [ P[0] - 1,P[1],0 ];
19001                  do (
19002                    i(#"$new_strokes",Q[0],Q[1]) = 2;
19003                    area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
19004                    Q = next3(#"$new_strokes",Q),
19005                  Q!=Q0 && ++edgels<=max_edgels);
19006                  if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
19007                    is_cond = 0; break()
19008                  );
19009                );
19010                if (i(#"$new_strokes",P[0],P[1] - 1)==1,
19011                  edgels = area = 0; Q0 = Q = [ P[0],P[1] - 1,1 ];
19012                  do (
19013                    i(#"$new_strokes",Q[0],Q[1]) = 2;
19014                    area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]);
19015                    Q = next3(#"$new_strokes",Q),
19016                  Q!=Q0 && ++edgels<=max_edgels);
19017                  if (edgels<=max_edgels && area>=area_min_threshold && area<area_max_threshold,
19018                    is_cond = 0; break()
19019                  );
19020                );
19021              0);
19022
19023              # Set or unset segment, and clean temp points used for local area estimations.
19024              for_segment(
19025                if (i(#"$new_strokes",P[0] + 1,P[1])==2,
19026                  edgels = 0; Q0 = Q = [ P[0] + 1,P[1],2 ];
19027                  do (i(#"$new_strokes",Q[0],Q[1]) = 1; Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
19028                );
19029                if (i(#"$new_strokes",P[0],P[1] + 1)==2,
19030                  edgels = 0; Q0 = Q = [ P[0],P[1] + 1,3 ];
19031                  do (i(#"$new_strokes",Q[0],Q[1]) = 1; Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
19032                );
19033                if (i(#"$new_strokes",P[0] - 1,P[1])==2,
19034                  edgels = 0; Q0 = Q = [ P[0] - 1,P[1],0 ];
19035                  do (i(#"$new_strokes",Q[0],Q[1]) = 1; Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
19036                );
19037                if (i(#"$new_strokes",P[0],P[1] - 1)==2,
19038                  edgels = 0; Q0 = Q = [ P[0],P[1] - 1,1 ];
19039                  do (i(#"$new_strokes",Q[0],Q[1]) = 1; Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels)
19040                );
19041              0);
19042              for_segment(i(#"$new_strokes",P) = is_cond?0:i(#"$new_strokes",P)&3);
19043              if (is_cond, ++i(#"$keycoords",x,0,0,2));
19044            );
19045          );
19046        ); I"
19047      ==[new_strokes] 0
19048      if !0$_keep_keycoords rm[keycoords] fi # Small hack used by the plug-in preview.
19049    fi
19050    rm[strokes]
19051    nm[new_strokes] $nm
19052  endl done
19053
19054#@cli ellipse : x[%],y[%],R[%],r[%],_angle,_opacity,_pattern,_color1,... : (+)
19055#@cli : Draw specified colored ellipse on selected images.
19056#@cli : A radius of '100%' stands for 'sqrt(width^2+height^2)'.
19057#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
19058#@cli : even if a color is specified. If a pattern is specified, the ellipse is
19059#@cli : drawn outlined instead of filled.
19060#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
19061#@cli : $ image.jpg repeat 300 ellipse {u(100)}%,{u(100)}%,{u(30)},{u(30)},{u(180)},0.3,${-rgb} done \
19062# ellipse 50%,50%,100,100,0,0.7,255
19063
19064#@cli flood : x[%],_y[%],_z[%],_tolerance>=0,_is_high_connectivity={ 0 | 1 },_opacity,_color1,... : (+)
19065#@cli : Flood-fill selected images using specified value and tolerance.
19066#@cli : Default values: 'y=z=0', 'tolerance=0', 'is_high_connectivity=0', 'opacity=1' and 'color1=0'.
19067#@cli : $ image.jpg repeat 1000 flood {u(100)}%,{u(100)}%,0,20,0,1,${-rgb} done
19068
19069#@cli gaussian : _sigma1[%],_sigma2[%],_angle
19070#@cli : Draw a centered gaussian on selected images, with specified standard deviations and orientation.
19071#@cli : Default values: 'sigma1=3', 'sigma2=sigma1' and 'angle=0'.
19072#@cli : $ 400,400 gaussian 100,30,45
19073#@cli : $$ https://gmic.eu/oldtutorial/_gaussian
19074gaussian : skip ${1=3},${2=$1},${3=0}
19075  e[^-1] "Draw centered gaussian on image$? with standard deviations ($1,$2) and angle $3 deg."
19076  u={cos($3*pi/180)}
19077  v={sin($3*pi/180)}
19078  dmax={max(w,h)}
19079  if isnum($1) l1=$1 else l1={${1}10000*$dmax/100} fi
19080  if isnum($2) l2=$2 else l2={${2}10000*$dmax/100} fi
19081  l1={1/(2*max(1/3,$l1)^2)}
19082  l2={1/(2*max(1/3,$l2)^2)}
19083  A={$l1*$u*$u+$l2*$v*$v}
19084  B={($l1-$l2)*$u*$v}
19085  C={$l1*$v*$v+$l2*$u*$u}
19086  repeat $! l[$>] nm={0,n}
19087    w={w} h={h} ds={d},{s} rm
19088    $w,$h,1,1,'X=x-{($w-1)/2};Y=y-{($h-1)/2};$A*X*X+2*$B*X*Y+$C*Y*Y'
19089    * -1 exp r $w,$h,$ds
19090  nm $nm endl done
19091
19092#@cli graph : [function_image],_plot_type,_vertex_type,_ymin,_ymax,_opacity,_pattern,_color1,... : \
19093# 'formula',_resolution>=0,_plot_type,_vertex_type,_xmin,xmax,_ymin,_ymax,_opacity,_pattern,_color1,... : (+)
19094#@cli : Draw specified function graph on selected images.
19095#@cli : 'plot_type' can be { 0=none | 1=lines | 2=splines | 3=bar }.
19096#@cli : 'vertex_type' can be { 0=none | 1=points | 2,3=crosses | 4,5=circles | 6,7=squares }.
19097#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
19098#@cli : even if a color is specified.
19099#@cli : Default values: 'plot_type=1', 'vertex_type=1', 'ymin=ymax=0 (auto)', 'opacity=1', 'pattern=(undefined)'
19100#@cli : and 'color1=0'.
19101#@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 \
19102# graph[0] [2],2,0,0,0,1,0,255,0 graph[0] [3],2,0,0,0,1,0,0,255 keep[0]
19103
19104#@cli grid : size_x[%]>=0,size_y[%]>=0,_offset_x[%],_offset_y[%],_opacity,_pattern,_color1,...
19105#@cli : Draw xy-grid on selected images.
19106#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
19107#@cli : even if a color is specified.
19108#@cli : Default values: 'offset_x=offset_y=0', 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
19109#@cli : $ image.jpg grid 10%,10%,0,0,0.5,255
19110#@cli : $ 400,400,1,3,255 grid 10%,10%,0,0,0.3,0xCCCCCCCC,128,32,16
19111grid : check "$1>=0 && $2>=0" skip ${3=0},${4=0},${5=1},${6=0},${7=$6}
19112  if ${"is_pattern \"$6\""}
19113    e[0--3] "Draw xy-grid on image$?, with sizes ($1,$2), offsets ($3,$4), opacity $5, pattern $6 and color (${7--1})."
19114    pattern=$6 color=${7--1}
19115  else
19116    e[0--3] "Draw xy-grid on image$?, with sizes ($1,$2), offsets ($3,$4), opacity $5, and color (${6--1})."
19117    pattern=0xFFFFFFFF color=${6--1}
19118  fi
19119  eval "
19120    is_percent(str) = (unref(_is_pct); _is_pct=['#str']; _is_pct[size(_is_pct) - 1]==_'%');
19121    repeat (l,k,
19122
19123      # Horizontal lines.
19124      size = is_percent($1)?max(1,w#k*$1):$1;
19125      size>=1?(
19126        off = (is_percent($3)?size*$3:$3)%size;
19127        for (x = off, x<w#k, x+=size, polygon(#k,-2,x,0,x,h - 1,$5,"$pattern","$color"));
19128      );
19129
19130      # Vertical lines.
19131      size = is_percent($2)?max(1,h#k*$2):$2;
19132      size>=1?(
19133        off = (is_percent($4)?size*$4:$4)%size;
19134        for (y = off, y<h#k, y+=size, polygon(#k,-2,0,y,w - 1,y,$5,"$pattern","$color"));
19135      )
19136    )"
19137
19138#@cli j : eq. to 'image'. : (+)
19139
19140#@cli image : [sprite],_x[%|~],_y[%|~],_z[%|~],_c[%|~],_opacity,_[opacity_mask],_max_opacity_mask : (+)
19141#@cli : Draw specified sprite image on selected images.
19142#@cli : (eq. to 'j').\n
19143#@cli : If one of the x,y,z or c argument ends with a '~', its value is expected to be
19144#@cli : a centering ratio (in [0,1]) rather than a position.
19145#@cli : Usual centering ratio are { 0=left-justified | 0.5=centered | 1=right-justified }.
19146#@cli : Default values: 'x=y=z=c=0', 'opacity=1', 'opacity_mask=(undefined)' and 'max_opacity_mask=1'.
19147#@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]
19148
19149#@cli line : x0[%],y0[%],x1[%],y1[%],_opacity,_pattern,_color1,... : (+)
19150#@cli : Draw specified colored line on selected images.
19151#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
19152#@cli : even if a color is specified.
19153#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
19154#@cli : $ image.jpg repeat 500 line 50%,50%,{u(w)},{u(h)},0.5,${-rgb} done line 0,0,100%,100%,1,0xCCCCCCCC,255 \
19155# line 100%,0,0,100%,1,0xCCCCCCCC,255
19156
19157#@cli linethick : x0[%],y0[%],x1[%],y1[%],_thickness,_opacity,_color1
19158#@cli : Draw specified colored thick line on selected images.
19159#@cli : Default values: 'thickness=2', 'opacity=1' and 'color1=0'.
19160#@cli : $ 400,400,1,3 repeat 100 linethick {u([w,h,w,h,5])},0.5,${-rgb} done
19161linethick : check "${5=2}>=0 && isnum(${6=1}) && isnum(${7=0})"
19162  e[^-1] "Draw thick line ($1,$2) - ($3,$4) on image$?, with thickness $5, opacity $6 and color (${7--1})."
19163  if !$5 line ${1-4},${6--1}
19164  else repeat $! l[$>]
19165    x0={${"is_percent $1"}?(w-1)*$1:$1}
19166    y0={${"is_percent $2"}?(h-1)*$2:$2}
19167    x1={${"is_percent $3"}?(w-1)*$3:$3}
19168    y1={${"is_percent $4"}?(h-1)*$4:$4}
19169    coords={"
19170      const th = "$5";
19171      P0 = [ "$x0","$y0" ];
19172      P1 = [ "$x1","$y1" ];
19173      dP = P1 - P0;
19174      n = [ -dP[1],dP[0] ]/max(1e-8,norm(dP))*th/2;
19175      round([ P0 - n, P0 + n, P1 + n, P1 - n ]);
19176    "}
19177    polygon 4,$coords,${6--1}
19178  endl done fi
19179
19180#@cli mandelbrot : z0r,z0i,z1r,z1i,_iteration_max>=0,_is_julia={ 0 | 1 },_c0r,_c0i,_opacity : (+)
19181#@cli : Draw mandelbrot/julia fractal on selected images.
19182#@cli : Default values: 'iteration_max=100', 'is_julia=0', 'c0r=c0i=0' and 'opacity=1'.
19183#@cli : $ 400,400 mandelbrot -2.5,-2,2,2,1024 map 0 +blur 2 elevation3d[-1] -0.2
19184
19185#@cli marble : _image_weight,_pattern_weight,_angle,_amplitude,_sharpness>=0,_anisotropy>=0,_alpha,_sigma,\
19186# _cut_low>=0,_cut_high>=0
19187#@cli : Render marble like pattern on selected images.
19188#@cli : Default values: 'image_weight=0.2', 'pattern_weight=0.1', 'angle=45', 'amplitude=0', 'sharpness=0.4' \
19189# and 'anisotropy=0.8',
19190#@cli : 'alpha=0.6', 'sigma=1.1' and 'cut_low=cut_high=0'.
19191#@cli : $ image.jpg +marble ,
19192marble : 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%}
19193  e[^-1] "Render marble like pattern on image$?, with image weight $1, pattern weight $2, angle $3 deg.,
19194          amplitude $4, sharpness $5, anisotropy $6, alpha $7, sigma $8, and cut ($9,$10)."
19195  sx={$2*sin($3*pi/180)} sy={$2*cos($3*pi/180)} f sin(x*$sx+y*$sy+i*$1)
19196  if $4 smooth $4,$5,$6,$7,$8 fi
19197  c $9,$10 n 0,255
19198
19199#@cli maze : _width>0,_height>0,_cell_size>0
19200#@cli : Input maze with specified size.
19201#@cli : $ maze 30,20 negate normalize 0,255
19202+maze : check "isint(${1=15}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=24}) && $3>0"
19203  e[^-1] "Input $1x$2 maze."
19204  ({round(u($1-1))},{round(u($2-1))})  # Starting cell.
19205  $1,$2,1,1,15 +f. 0 a[-2,-1] c   # Starting maze data.
19206  _generate_maze $1,$2
19207  _render_maze. $3 nm. [maze]
19208
19209_generate_maze :
19210
19211  # Start opening walls.
19212  do
19213    x={-2,@-2} y={-2,@-1} # Coords of the current cell.
19214    =. 1,$x,$y,0,1       # Mark current cell as visited.
19215
19216    # Check for neighboring cells that are candidate for opening wall, and select one random.
19217    is_candidate=0
19218    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.
19219    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.
19220    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.
19221    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.
19222    if $is_candidate
19223      ($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
19224    fi
19225
19226    # Remove wall between the current and chosen neighboring cells.
19227    if $is_candidate
19228      if {-2,@-1}==8   =. {i($x,$y)&7},$x,$y =. {i($x,$y-1)&11},$x,{$y-1}   # Remove up wall.
19229      elif {-2,@-1}==4 =. {i($x,$y)&11},$x,$y =. {i($x,$y+1)&7},$x,{$y+1}   # Remove down wall.
19230      elif {-2,@-1}==2 =. {i($x,$y)&13},$x,$y =. {i($x-1,$y)&14},{$x-1},$y  # Remove left wall.
19231      else             =. {i($x,$y)&14},$x,$y =. {i($x+1,$y)&13},{$x+1},$y  # Remove right wall.
19232      fi
19233      z.. 0,1 a[-3,-2] y  # Add neighboring cell to stack of cells to explore.
19234    else # No candidate : remove current cell from cells to explore.
19235      if h#-2==1 break fi
19236      rows.. 0,{{-2,h}-2}
19237    fi
19238  while 1
19239  rm.. channels. 0
19240
19241_render_maze :
19242  # Create the 16 configurations of walls.
19243  i[0] $1,$1 i[1] [0]x15
19244  line[8-15] 0,0,100%,0,1,1
19245  line[4-7,12-15] 0,100%,100%,100%,1,1
19246  line[2-3,6-7,10-11,14-15] 0,0,0,100%,1,1
19247  line[1-15:2] 100%,0,100%,100%,1,1
19248  # Map the wall data with them.
19249  a[0-15] x r. {w*$1},{h*$1} *. $1 channels. 0,1
19250  $1,$1,1,1,x $1,$1,1,1,y a[-2,-1] c r. ..,..,1,2,0,2 +[-2,-1]
19251  warp.. .,0,0,0 rm.
19252
19253#@cli maze_mask : _cellsize>0
19254#@cli : Input maze according to size and shape of selected mask images.
19255#@cli : Mask may contain disconnected shapes.
19256#@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
19257maze_mask : check "isint(${1=24}) && $1>0"
19258  e[^-1] "Input masked maze from image$? with cell size $1."
19259  compose_channels + >= 50% repeat $! l[$>]
19260    do
19261      +rand[0] 0,1 *. [0] ({[xM,yM]}) rm..  # Select one starting point in the mask.
19262      +flood[0] {^},0,0,0,1,2 >=. 2 +negate. *.. 15 a[-2,-1] c
19263      flood[0] {-2,^},0,0,0,1,0
19264      _generate_maze {w},{h}
19265    while iM#0
19266    rm[0] + _render_maze. $1 nm. [maze]
19267  endl done
19268
19269#@cli newton_fractal : z0r,z0i,z1r,z1i,_angle,0<=_descent_method<=2,_iteration_max>=0,_convergence_precision>0,\
19270# _expr_p(z),_expr_dp(z),_expr_d2p(z)
19271#@cli : Draw newton fractal on selected images, for complex numbers in range (z0r,z0i) - (z1r,z1i).
19272#@cli : Resulting images have 3 channels whose meaning is [ last_zr, last_zi, nb_iter_used_for_convergence ].
19273#@cli : 'descent_method' can be { 0=secant | 1=newton | 2=householder }.
19274#@cli : Default values: 'angle=0', 'descent_method=1', 'iteration_max=200', 'convergence_precision=0.01', \
19275# 'expr_p(z)=z^^3-1', 'expr_dp(z)=3*z^^2' and 'expr_d2z(z)=6*z'.
19276#@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" \
19277# f "[ atan2(i1,i0)*90+20,1,cut(i2/30,0.2,0.7) ]" hsl2rgb
19278newton_fractal : check "isin(${6=1},0,1,2) && ${7=200}>=0 && ${8=0.01}>0"
19279                 skip "${4=0},${9=z^^3-1},${10=3*z^^2},${11=6*z}"
19280  m0,m1,m2=secant,newton,householder
19281  e[^-1] "Draw newton fractal on image$?, for complex range ($1,$2)-($3,$4), with angle $5, $7 max "${m$6}" "\
19282   "iterations, precision $8, and expressions 'p(z)=$9', 'dp(z)=$10' and 'd2p(z)=$11'."
19283  channels 0,2
19284  f "*
19285    begin(
19286      const dx = abs($3 - $1);
19287      const dy = abs($4 - $2);
19288      const angle = $5;
19289      const method = $6;
19290      const itermax = $7;
19291      const precision = $8;
19292
19293      zc = [ $1 + $3, $2 + $4 ]/2;
19294      R = rot(-angle°);
19295    );
19296
19297    p(z) = ($9);
19298    dp(z) = ($10);
19299    d2p(z) = ($11);
19300
19301    zn = [ $1 + x*dx/(w-1), $2 + y*dy/(h-1) ];
19302    angle?(zn = (R*(zn-=zc)+=zc));
19303
19304    !method?(znm1 = zn + [ precision,0 ]);
19305    repeat (itermax,iter,
19306      pzn = p(zn);
19307      method==0?(
19308        znp1 = zn - pzn**(zn - znm1)//(pzn - p(znm1)); # secant
19309        znm1 = zn;
19310      ):method==1?(
19311        dpzn = dp(zn);
19312        znp1 = zn - pzn//dpzn; # newton
19313      ):( # householder
19314        dpzn = dp(zn);
19315        d2pzn = d2p(zn);
19316        hn = (pzn**d2pzn)//(2*dpzn^^2);
19317        znp1 = zn - pzn//dpzn**([1,0] + hn);
19318      );
19319      norm(znp1 - zn)<precision?break();
19320      zn = znp1;
19321    );
19322    cabs(zn)<2?[ zn,iter ]:[ 0,0,iter ]"
19323
19324#@cli j3d : eq. to 'object3d'. : (+)
19325
19326#@cli object3d : [object3d],_x[%],_y[%],_z,_opacity,_rendering_mode,_is_double_sided={ 0 | 1 },\
19327# _is_zbuffer={ 0 | 1 },_focale,_light_x,_light_y,_light_z,_specular_lightness,_specular_shininess : (+)
19328#@cli : Draw specified 3D object on selected images.
19329#@cli : (eq. to 'j3d').\n
19330#@cli : 'rendering_mode' can be { 0=dots | 1=wireframe | 2=flat | 3=flat-shaded | 4=gouraud-shaded | 5=phong-shaded }.
19331#@cli : Default values: 'x=y=z=0', 'opacity=1' and 'is_zbuffer=1'. All other arguments take their default values
19332#@cli : from the 3D environment variables.
19333#@cli : $ image.jpg torus3d 100,10 cone3d 30,-120 add3d[-2,-1] rotate3d. 1,1,0,60 object3d[0] [-1],50%,50% keep[0]
19334
19335#@cli pack_sprites : _nb_scales>=0,0<=_min_scale<=100,_allow_rotation={ 0=0 deg. | 1=180 deg. | 2=90 deg. | 3=any },\
19336# _spacing,_precision>=0,max_iterations>=0
19337#@cli : Try to randomly pack as many sprites as possible onto the 'empty' areas of an image.
19338#@cli : Sprites can be eventually rotated and scaled during the packing process.
19339#@cli : First selected image is the canvas that will be filled with the sprites.
19340#@cli : Its last channel must be a binary mask whose zero values represent potential locations for drawing the sprites.
19341#@cli : All other selected images represent the sprites considered for packing.
19342#@cli : Their last channel must be a binary mask that represents the sprite shape (i.e. a 8-connected component).
19343#@cli : The order of sprite packing follows the order of specified sprites in the image list.
19344#@cli : Sprite packing is done on random locations and iteratively with decreasing scales.
19345#@cli : 'nb_scales' sets the number of decreasing scales considered for all specified sprites to be packed.
19346#@cli : 'min_scale' (in %) sets the minimal size considered for packing (specified as a percentage of the
19347#@cli : original sprite size).
19348#@cli : 'spacing' can be positive or negative.
19349#@cli : 'precision' tells about the desired number of failed trials before ending the filling process.
19350#@cli : Default values: 'nb_scales=5', 'min_scale=25', 'allow_rotation=3', 'spacing=1', 'precision=7' \
19351# and 'max_iterations=256'.
19352#@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 \
19353# to_rgba pack_sprites 3,25
19354pack_sprites : check "isint(${1=5}) && $1>=0 && ${2=25}>=0 && $2<=100 && isint(${3=3}) && $3>=0 && $3<=3 &&
19355                      isint(${4=1}) && isint(${5=7}) && $5>=0 && isint(${6=256}) && $6>=0"
19356  e[^-1] "Randomly pack image$? with $1 scales, minimum scale $2%, "${arg\ 1+$3,no,180\"\ \"deg.,90\"\ \"deg.,any}\
19357         " rotation, spacing $4, precision $5 and $6 maximum iterations."
19358  N={$!-1} is_first_time=1
19359  repeat $! r[$>] 100%,100%,1,{$>,max(2,s)} done   # Ensure all images have a binary shape mask.
19360
19361  # Start iterations over scales.
19362  repeat $1
19363    rprogress {$>*100/$1}
19364    nb_attempts=0
19365
19366    # Generate all sprites for current scale.
19367    ratio={if($1>1,$2+(100-$2)*$</($1-1),100)}%
19368    repeat $N +l[{1+$>}]
19369      w={w*$ratio} h={h*$ratio}
19370      if $w<1||$h<1 rm
19371      else r $w,$h,1,100%,2 sh. 100% !=. 0 area{1+$>}={is} rm.
19372      fi
19373    endl done
19374
19375    # Pack rescaled sprites together.
19376    l[0,{$N+1}--1] repeat $6
19377
19378      # Compute reference sprite.
19379      ind={1+($>%$N)} area=${area$ind}
19380      if $3==0 [$ind]
19381      elif $3==1 +rotate[$ind] {round(u)*180}
19382      elif $3==2 +rotate[$ind] {round(u(3))*90}
19383      else +rotate[$ind] {u*360} sh. 100% !=. 0 area={is} rm.
19384      fi
19385
19386      # Get binary map of possible locations.
19387      +channels[0] 100% ==. 0
19388      if $4>1 erode. {2*$4-1}
19389      elif $4<1 dilate. {-2*$4+3}
19390      fi
19391
19392      # Generate random skeleton-oriented point cloud.
19393      +rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0
19394      if $is_first_time noise. 0.1,2 ==. 1 fi
19395      distance. 0 noise. 1,1
19396      max_patch. {$ind,round(1.5*max(w,h))}
19397      *. .. pointcloud3d.
19398
19399      # Subdivide point cloud if multiple sprites.
19400      if $N>1 l.
19401        s3d /[1] $N round[1] max[1] 1 n={1,@0}
19402        r[2] 3,{{2,h}/3},1,1,-1
19403        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]
19404        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
19405      endl fi
19406
19407      # Create 3D cloud of sprites.
19408      n={@7}
19409      if $n
19410        s3d. rm[-2,-1]
19411        if $3==0 # No rotation allowed.
19412          [-6] i.. (-128;{w};{h};{s})
19413          if $n>1 4,{$n-1},1,1,-128,0,0,0 fi
19414          +channels.. 100% i.. (-128;{w};{h};{s})
19415          if $n>1 ... fi
19416        elif $3==1 # 180 deg. rotation allowed.
19417          +rotate[-6] {round(u(1))*180} i.. (-128;{w};{h};{s})
19418          if $n>1 +rotate. 180 i.. (-128;{w};{h};{s}) fi
19419          if $n>2 4,{$n-2},1,1,-128,0,0,0 1,100% rand. 0,1 round. 1 j.. .,1 rm. fi
19420          +channels[-4] 100% i.. (-128;{w};{h};{s})
19421          if $n>1 +channels[-4] 100% i.. (-128;{w};{h};{s}) fi
19422          if $n>2 [-5] fi
19423        else # 90 deg. rotation (or more) allowed.
19424          +rotate[-6] {round(u(3))*90} i.. (-128;{w};{h};{s})
19425          if $n>1 +rotate. 90 i.. (-128;{w};{h};{s}) fi
19426          if $n>2 +rotate. 90 i.. (-128;{w};{h};{s}) fi
19427          if $n>3 +rotate. 90 i.. (-128;{w};{h};{s}) fi
19428          if $n>4 4,{$n-4},1,1,-128,0,0,0 1,100% rand. 0,3 round. 1 j.. .,1 rm. fi
19429          +channels[-8] 100% i.. (-128;{w};{h};{s})
19430          if $n>1 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi
19431          if $n>2 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi
19432          if $n>3 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi
19433          if $n>4 [-9] fi
19434        fi
19435        y[{$N+3}--1] a[{$N+3}--1] y
19436      fi
19437      rm... # Delete reference sprite.
19438
19439      # Draw cloud and detect non-intersecting sprites.
19440      [0] sh. 100% f. 1 -. [-4]
19441      j3d.. ...,0,0,0,1,2,0,0 rm[-3,-1]
19442      sh. 100% area_fg. 0,1 ==. $area
19443      *. ... rm... sh.. 0,{-2,s-2} *. .. rm.
19444
19445      # Draw selected sprites on rendering image.
19446      if iM j[0] ..,0,0,0,0,1,. rm[-2,-1]
19447      else
19448        rm[-2,-1]
19449        nb_attempts+=1
19450        if $nb_attempts>$5 break else continue fi
19451      fi
19452
19453    done k[0] endl
19454
19455  done k[0]
19456
19457#@cli piechart : label_height>=0,label_R,label_G,label_B,"label1",value1,R1,G1,B1,...,"labelN",valueN,RN,GN,BN
19458#@cli : Draw pie chart on selected (RGB) images.
19459#@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
19460piechart : check $1>=0
19461  e[^-1] "Draw pie chart on image$?, with label height $1 and color ($2,$3,$4)."
19462  $=arg repeat $! l[$>]
19463    ellipse 50%,50%,{w/2-1},{h/2-1},0,1,1
19464    ellipse 50%,50%,{w/2-1},{h/2-1},0,1,0xFFFFFFFF
19465    (${6--1:5}) normalize_sum.
19466    theta=0
19467    if w>1 repeat w
19468      xe={0.5*{-2,w}*(1+cos($theta))}
19469      ye={0.5*{-2,h}*(1+sin($theta))}
19470      line.. 50%,50%,$xe,$ye
19471      theta-={2*pi*i($>)}
19472    done fi
19473    theta=0
19474    repeat w if i($>)
19475      ntheta={$theta-2*pi*i($>)}
19476      xc={0.5*{-2,w}*(1+0.5*cos(0.5*($ntheta+$theta)))}
19477      yc={0.5*{-2,h}*(1+0.5*sin(0.5*($ntheta+$theta)))}
19478      xf={0.5*{-2,w}*(1+0.8*cos(0.5*($ntheta+$theta)))}
19479      yf={0.5*{-2,h}*(1+0.8*sin(0.5*($ntheta+$theta)))}
19480      flood.. $xf,$yf,0,0,0,1,${arg{7+5*$>}},${arg{8+5*$>}},${arg{9+5*$>}}
19481      if abs($ntheta-$theta)>0.1
19482        0 t. ${arg{5+5*$>}},0,0,$1,1,1
19483        ($2^$3^$4) r. ..,..,1,3 *. ..
19484        j[-4] .,{$xc-w/2},{$yc-h/2},0,0,1,..
19485        rm[-2,-1]
19486      fi
19487      theta=$ntheta
19488    fi done
19489    rm.
19490  endl done
19491
19492#@cli plasma : _alpha,_beta,_scale>=0 : (+)
19493#@cli : Draw a random colored plasma fractal on selected images.
19494#@cli : This command implements the so-called 'Diamond-Square' algorithm.
19495#@cli : Default values: 'alpha=1', 'beta=1' and 'scale=8'.
19496#@cli : $ 400,400,1,3 plasma
19497#@cli : $$ https://gmic.eu/oldtutorial/_plasma
19498
19499#@cli point : x[%],_y[%],_z[%],_opacity,_color1,... : (+)
19500#@cli : Set specified colored pixel on selected images.
19501#@cli : Default values: 'z=0', 'opacity=1' and 'color1=0'.
19502#@cli : $ image.jpg repeat 10000 point {u(100)}%,{u(100)}%,0,1,${-rgb} done
19503
19504#@cli polka_dots : diameter>=0,_density,_offset1,_offset2,_angle,_aliasing,_shading,_opacity,_color,...
19505#@cli : Draw dots pattern on selected images.
19506#@cli : Default values: 'density=20', 'offset1=offset2=50', 'angle=0', 'aliasing=10', 'shading=1', 'opacity=1' \
19507# and 'color=255'.
19508#@cli : $ image.jpg polka_dots 10,15,0,0,20,10,1,0.5,0,128,255
19509polka_dots : check $1>=0 skip ${2=20},${3=50},${4=50},${5=0},${6=10},${7=1},${8=1},${9=255}
19510  e[^-1] "Draw polka dots on image$?, with diameter $1, density $2, angle $3 deg., shift ($4,$5), aliasing $6 and
19511          shading $7."
19512  theta={$5*pi/180} ct={cos($theta)} st={sin($theta)} mid1={$1/2} mid2={$2/2}
19513  i[0] (${9--1}) y[0] c
19514  repeat $!-1
19515    WH={max(w,h)}
19516    100%,100%,100%,1,"xn = 100*x/"$WH"-$3; yn = 100*y/"$WH"-$4; \
19517                      xr = xn*"$ct"-yn*"$st"; yr = xn*"$st"+yn*"$ct"; \
19518                      xc = xr%$2-"$mid2"; yc = yr%$2-"$mid2"; \
19519                      "$mid1"-sqrt(xc*xc+yc*yc)"
19520    *. $6 c. 0,$7 n. 0,$8 (${9--1}) y. c r. ..,..,..
19521    j... .,0,0,0,0,1,.. rm[-2,-1]
19522  mv. 1 done rm[0]
19523
19524#@cli polygon : N>=1,x1[%],y1[%],...,xN[%],yN[%],_opacity,_pattern,_color1,... : (+)
19525#@cli : Draw specified colored N-vertices polygon on selected images.
19526#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
19527#@cli : even if a color is specified. If a pattern is specified, the polygon is
19528#@cli : drawn outlined instead of filled.
19529#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
19530#@cli : $ image.jpg polygon 4,20%,20%,80%,30%,80%,70%,20%,80%,0.3,0,255,0 \
19531# polygon 4,20%,20%,80%,30%,80%,70%,20%,80%,1,0xCCCCCCCC,255
19532#@cli : $ image.jpg 2,16,1,1,'u(if(x,{h},{w}))' polygon[-2] {h},{^},0.6,255,0,255 remove[-1]
19533
19534#@cli quiver : [function_image],_sampling[%]>0,_factor>=0,_is_arrow={ 0 | 1 },_opacity,_color1,...
19535#@cli : Draw specified 2D vector/orientation field on selected images.
19536#@cli : Default values: 'sampling=5%', 'factor=1', 'is_arrow=1', 'opacity=1', 'pattern=(undefined)'
19537#@cli : and 'color1=0'.
19538#@cli : $ 100,100,1,2,'if(c==0,x-w/2,y-h/2)' 500,500,1,3,255 quiver[-1] [-2],10
19539#@cli : $ image.jpg +resize2dy 600 luminance[0] gradient[0] mul[1] -1 reverse[0,1] append[0,1] c \
19540# blur[0] 8 orientation[0] quiver[1] [0],20,1,1,0.8,255
19541quiver : check ${"is_image_arg $1"}" && ${2=5%}>0 && ${3=1}>=0 && isbool(${4=1})" skip "${5=1},${6=0}"
19542  e[^-1] "Draw 2D vector field $1 on image$?, with sampling $2, factor $3, arrows "${"arg 1+$4,disabled,enabled"}",
19543          opacity $5 and color (${6--1})."
19544  pass$1 repeat $!-1 l[$>,-1]
19545    eval ${-math_lib}"
19546      s_sampling = ['$2'];
19547      sampling = s_sampling[size(s_sampling) - 1 ]==_'%'?min(w#0,h#0)*$2:$2;
19548      vmax = max(abs(im),abs(iM));
19549      vmax = vmax?vmax:1;
19550      fact = $3*sampling/vmax;
19551      for (y = sampling/2, y<h#0, y+=sampling,
19552        for (x = sampling/2, x<w#0, x+=sampling,
19553          X = round(x*w/w#0); Y = round(y*h/h#0);
19554          u = i(X,Y,0,0)*fact; v = i(X,Y,0,1)*fact;
19555          if ($4,
19556            arrow(#0,[x,y],[x + u,y + v],45,sampling/4,$5,[${6--1}]),
19557            polygon(#0,2,[x - 0.5*u,y - 0.5*v],[x + 0.5*u,y + 0.5*v],$5,[${6--1}]);
19558          );
19559        );
19560      );
19561    "
19562  endl done rm.
19563
19564#@cli rectangle : x0[%],y0[%],x1[%],y1[%],_opacity,_pattern,_color1,...
19565#@cli : Draw specified colored rectangle on selected images.
19566#@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted
19567#@cli : even if a color is specified. If a pattern is specified, the rectangle is
19568#@cli : drawn outlined instead of filled.
19569#@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'.
19570#@cli : $ image.jpg repeat 30 rectangle {u(100)}%,{u(100)}%,{u(100)}%,{u(100)}%,0.3,${-rgb} done
19571rectangle : skip ${5=1},${6=0},${7=$6}
19572  if ${"is_pattern \"$5\""}
19573    e[0--3] "Draw outlined rectangle from ($1,$2) to ($3,$4) on image$?, with opacity $5 and color (${7--1})."
19574  else
19575    e[0--3] "Draw filled rectangle from ($1,$2) to ($3,$4) on image$?, with opacity $5 and color (${6--1})."
19576  fi
19577  polygon 4,$1,$2,$3,$2,$3,$4,$1,$4,${5--1}
19578
19579#@cli rorschach : 'smoothness[%]>=0','mirroring={ 0=none | 1=x | 2=y | 3=xy }
19580#@cli : Render rorschach-like inkblots on selected images.
19581#@cli : Default values: 'smoothness=5%' and 'mirroring=1'.
19582#@cli : $ 400,400 rorschach 3%
19583rorschach : check "${1=5%}>=0 && isint(${2=1}) && $2>=0 && $2<=3"
19584  e[^-1] "Render rorschach-like inkblots on image$?, with smoothness $1 and "${arg\ 1+$2,no,x,y,xy}"-mirroring."
19585  if $2==0 # No mirroring.
19586   rand -1,1 b $1 >= 0
19587  elif $2==1 # X-mirroring.
19588    repeat $! l[$>]
19589      w={w}
19590      columns 0,{w/2-1} rand -1,1 b $1 >= 0
19591      +mirror x if $w%2 columns. 1,100% fi a x
19592    endl done
19593  elif $2==2 # Y-mirroring.
19594    repeat $! l[$>]
19595      h={h}
19596      rows 0,{h/2-1} rand -1,1 b $1 >= 0
19597      +mirror y if $h%2 rows. 1,100% fi a y
19598    endl done
19599  elif $2==3 # XY-mirroring.
19600    repeat $! l[$>]
19601      w={w} h={h}
19602      z 0,0,{w/2-1},{h/2-1} rand -1,1 b $1 >= 0
19603      +mirror x if $w%2 columns. 1,100% fi a x
19604      +mirror y if $h%2 rows. 1,100% fi a y
19605    endl done
19606  fi
19607
19608#@cli sierpinski : recursion_level>=0
19609#@cli : Draw Sierpinski triangle on selected images.
19610#@cli : Default value: 'recursion_level=7'.
19611#@cli : $ image.jpg sierpinski 7
19612sierpinski : check ${1=7}>=0 skip ${2=50},${3=0},${4=0},${5=100},${6=100},${7=100}
19613  e[^-1] "Draw Sierpinski triangle of degree $1 on image$?."
19614  _sierpinski ${2-7},$1
19615
19616_sierpinski :
19617  if $7<=0 polygon 3,$1%,$2%,$3%,$4%,$5%,$6%,1,255 return fi
19618  _sierpinski $1,$2,{($1+$3)/2},{($2+$4)/2},{($1+$5)/2},{($2+$6)/2},{$7-1}
19619  _sierpinski {($1+$3)/2},{($2+$4)/2},$3,$4,{($3+$5)/2},{($4+$6)/2},{$7-1}
19620  _sierpinski {($1+$5)/2},{($2+$6)/2},$5,$6,{($3+$5)/2},{($4+$6)/2},{$7-1}
19621
19622#@cli spiralbw : width>0,_height>0,_is_2dcoords={ 0 | 1 }
19623#@cli : Input a 2D rectangular spiral image with specified size.
19624#@cli : Default values: 'height=width' and 'is_2dcoords=0'.
19625#@cli : $ spiralbw 16
19626#@cli : $ image.jpg spiralbw {[w,h]},1 +warp[0] [1],0 +warp[2] [1],2
19627+spiralbw : check "$1>=1 && ${2=$1}>=1 && isbool(${3=0})"
19628  e[^-1] "Input 2D rectangular spiral image of size $1x$2."
19629  main="alpha = min(x,y,w - 1 - x,h - 1 - y);
19630        t0 = alpha*2*(w + h) - 4*alpha^2;
19631        X = x - alpha;
19632        Y = y - alpha;
19633        W = w - 2*alpha;
19634        H = h - 2*alpha;
19635        t = t0 + (Y==0?X:
19636                  X==W - 1?W - 1 + Y:
19637                  Y==H - 1?2*W + H - 3 - X:
19638                  2*(W + H - 2) - Y);"
19639  if $3 $1,$2,1,2,$main"[ t%w, int(t/w) ]" else $1,$2,1,1,$main fi
19640
19641#@cli spline : x0[%],y0[%],u0[%],v0[%],x1[%],y1[%],u1[%],v1[%],_opacity,_color1,...
19642#@cli : Draw specified colored spline curve on selected images (cubic hermite spline).
19643#@cli : Default values: 'opacity=1' and 'color1=0'.
19644#@cli : $ image.jpg repeat 30 spline {u(100)}%,{u(100)}%,{u(-600,600)},{u(-600,600)},{u(100)}%,{u(100)}%,\
19645# {u(-600,600)},{u(-600,600)},0.6,255 done
19646spline : skip ${9=1},${10=0}
19647  e[^-1] "Draw spline from ($1,$2) [$3,$4] to ($5,$6) [$7,$8] on image$?, with opacity $9 and color (${10--1})."
19648  repeat $! l[$>]
19649    x0={if(${"is_percent $1"},$1*(w-1),$1)}
19650    y0={if(${"is_percent $2"},$2*(h-1),$2)}
19651    u0={if(${"is_percent $3"},$3*(w-1),$3)}
19652    v0={if(${"is_percent $4"},$4*(h-1),$4)}
19653    x1={if(${"is_percent $5"},$5*(w-1),$5)}
19654    y1={if(${"is_percent $6"},$6*(h-1),$6)}
19655    u1={if(${"is_percent $7"},$7*(w-1),$7)}
19656    v1={if(${"is_percent $8"},$8*(h-1),$8)}
19657    eval ${-math_lib}"spline(#0,["$x0","$y0"],["$u0","$v0"],["$x1","$y1"],["$u1","$v1"],$9,[${10--1}])"
19658  endl done
19659
19660#@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,...
19661#@cli : Draw tetraedron with interpolated colors on selected (volumetric) images.
19662tetraedron_shade :
19663  e[^-1] "Draw tetraderon ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)-($10,$11,$12) with interpolated colors in image$?."
19664
19665  # Find bounding box.
19666  xm={round(min($1,$4,$7,$10),1,-1)} xM={round(max($1,$4,$7,$10),1,1)}
19667  ym={round(min($2,$5,$8,$11),1,-1)} yM={round(max($2,$5,$8,$11),1,1)}
19668  zm={round(min($3,$6,$9,$12),1,-1)} zM={round(max($3,$6,$9,$12),1,1)}
19669
19670  # Find color mapping coefficients for each vertex.
19671  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
19672
19673  # Draw tetraedron on selected images.
19674  f[^-1] "*
19675  begin(
19676    x0 = $1; y0 = $2; z0 = $3;
19677    x1 = $4; y1 = $5; z1 = $6;
19678    x2 = $7; y2 = $8; z2 = $9;
19679    x3 = $10; y3 = $11; z3 = $12;
19680    u01 = x1 - x0; v01 = y1 - y0; w01 = z1 - z0;
19681    u02 = x2 - x0; v02 = y2 - y0; w02 = z2 - z0;
19682    u03 = x3 - x0; v03 = y3 - y0; w03 = z3 - z0;
19683    u12 = x2 - x1; v12 = y2 - y1; w12 = z2 - z1;
19684    u13 = x3 - x1; v13 = y3 - y1; w13 = z3 - z1;
19685    u23 = x3 - x2; v23 = y3 - y2; w23 = z3 - z2;
19686    nx012 = v01*w02 - w01*v02; ny012 = w01*u02 - u01*w02; nz012 = u01*v02 - v01*u02;
19687    if (nx012*u03 + ny012*v03 + nz012*w03<0, nx012*=-1; ny012*=-1; nz012*=-1);
19688    nx013 = v01*w03 - w01*v03; ny013 = w01*u03 - u01*w03; nz013 = u01*v03 - v01*u03;
19689    if (nx013*u02 + ny013*v02 + nz013*w02<0, nx013*=-1; ny013*=-1; nz013*=-1);
19690    nx023 = v02*w03 - w02*v03; ny023 = w02*u03 - u02*w03; nz023 = u02*v03 - v02*u03;
19691    if (nx023*u01 + ny023*v01 + nz023*w01<0, nx023*=-1; ny023*=-1; nz023*=-1);
19692    nx123 = v12*w13 - w12*v13; ny123 = w12*u13 - u12*w13; nz123 = u12*v13 - v12*u13;
19693    if (-nx123*u01 - ny123*v01 - nz123*w01<0, nx123*=-1; ny123*=-1; nz123*=-1);
19694  );
19695  if (x<"$xm" || x>"$xM" || y<"$ym" || y>"$yM" || z<"$zm" || z>"$zM",i,
19696    dx0 = x - x0; dy0 = y - y0; dz0 = z - z0;
19697    dx1 = x - x1; dy1 = y - y1; dz1 = z - z1;
19698    is_in = dx0*nx012 + dy0*ny012 + dz0*nz012>=0 &&
19699    dx0*nx013 + dy0*ny013 + dz0*nz013>=0 &&
19700    dx0*nx023 + dy0*ny023 + dz0*nz023>=0 &&
19701    dx1*nx123 + dy1*ny123 + dz1*nz123>=0;
19702    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
19703  )
19704"
19705  rm.
19706
19707#@cli t : eq. to 'text'. : (+)
19708
19709#@cli text : text,_x[%|~],_y[%|~],_font_height[%]>=0,_opacity,_color1,... : (+)
19710#@cli : Draw specified colored text string on selected images.
19711#@cli : (eq. to 't').\n
19712#@cli : If one of the x or y argument ends with a '~', its value is expected to be
19713#@cli : a centering ratio (in [0,1]) rather than a position.
19714#@cli : Usual centering ratio are { 0=left-justified | 0.5=centered | 1=right-justified }.
19715#@cli : Sizes '13' and '128' are special and correspond to binary fonts (no-antialiasing).
19716#@cli : Any other font size is rendered with anti-aliasing.
19717#@cli : Specifying an empty target image resizes it to new dimensions such that the image contains
19718#@cli : the entire text string.
19719#@cli : Default values: 'x=y=0.01~', 'font_height=16', 'opacity=1' and 'color1=0'.
19720#@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 \
19721# y+={2*$>} done
19722#@cli : $ 0 text "G'MIC",0,0,23,1,255
19723
19724#@cli to : eq. to 'text_outline'.
19725to : skip "${1=}",${2=0.01~},${3=0.01~} check "${4=7.5%}>0 && ${5=2}>=0 && isnum(${6=1}) && isnum(${7=255}) && "\
19726                                      "isnum(${8=$7}) && isnum(${9=$7}) && isnum(${10=255})"
19727  _text_outline $"*"
19728
19729#@cli text_outline : text,_x[%|~],_y[%|~],_font_height[%]>0,_outline>=0,_opacity,_color1,...
19730#@cli : Draw specified colored and outlined text string on selected images.
19731#@cli : If one of the x or y argument ends with a '~', its value is expected to be
19732#@cli : a centering ratio (in [0,1]) rather than a position.
19733#@cli : Usual centering ratio are { 0=left-justified | 0.5=centered | 1=right-justified }.
19734#@cli : Default values: 'x=y=0.01~', 'font_height=7.5%', 'outline=2', 'opacity=1', 'color1=color2=color3=255' \
19735# and 'color4=255'.
19736#@cli : $ image.jpg text_outline "Hi there!",10,10,63,3
19737text_outline : skip "${1=}",${2=0.01~},${3=0.01~}
19738  check "${4=7.5%}>0 && ${5=2}>=0 && isnum(${6=1}) && isnum(${7=255}) && "\
19739        "isnum(${8=$7}) && isnum(${9=$7}) && isnum(${10=255})"
19740  _text_outline $"*"
19741
19742_text_outline : skip "${1=}"
19743  e[0--3] "Draw outlined text '$1' at position ($2,$3) on image$?, with font height $4, outline $5, opacity $6 and
19744           color ${7--1}."
19745  if ['"$1"']==0 return fi
19746  sepx,sepy={"sx=['$2']; sy=['$3']; [sx[size(sx)-1], sy[size(sy)-1]]"}
19747  is_fontpercent=${"is_percent $4"}
19748  xpos={`s=['"$2"'];$sepx==_'~'||$sepx==_'%'?s[0,size(s)-1]:s`}
19749  ypos={`s=['"$3"'];$sepy==_'~'||$sepy==_'%'?s[0,size(s)-1]:s`}
19750  repeat $! l[$>]
19751    0 t. "$1",0,0,{-2,$is_fontpercent?h*$4:$4},1,1 expand_xy. {1+$5},0
19752    +dilate. {2*$5+1}
19753    i[-3] (${7--1}) r... {s#0},1,1,1,0,2 y... c r... .,.,1,100%
19754    if $5 *[-3,-2] else rm.. fi
19755    if w#0
19756      j... ..,{[($sepx==_'~'?(w#0-1-w):$sepx==_'%'?(w#0-1)%:1)*$xpos,\
19757                ($sepy==_'~'?(h#0-1-h):$sepy==_'%'?(h#0-1)%:1)*$ypos]},0,0,$6,.
19758      k[0]
19759    else k[1]
19760    fi
19761  endl done
19762
19763#@cli triangle_shade : x0,y0,x1,y1,x2,y2,R0,G0,B0,...,R1,G1,B1,...,R2,G2,B2,...
19764#@cli : Draw triangle with interpolated colors on selected images.
19765#@cli : $ image.jpg triangle_shade 20,20,400,100,120,200,255,0,0,0,255,0,0,0,255
19766triangle_shade :
19767  e[^-1] "Draw triangle ($1,$2)-($3,$4)-($5,$6) with interpolated colors on image$?."
19768  # Find color mapping coefficients for each vertex.
19769  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
19770
19771  # Pre-compute coefs to test point inside triangle.
19772  invarea={(-$4*$5+$2*(-$3+$5)+$1*($2-$6)+$3*$6)^-1}
19773  s1={$2*$5-$1*$6} s2={$6-$2} s3={$1-$5}
19774  t1={$1*$4-$2*$3} t2={$2-$4} t3={$3-$1}
19775
19776  # Begin drawing on each selected image.
19777  repeat $!-1 l[$>,-1] repeat s#0
19778    a={i(0,0,0,$>)} b={i(0,1,0,$>)} c={i(0,2,0,$>)}
19779    sh[0] $>
19780    f. "s = "$invarea"*("$s1" + "$s2"*x + "$s3"*y);
19781        t = "$invarea"*("$t1" + "$t2"*x + "$t3"*y);
19782        s>=0 && t>=0 && t+s<=1 ? "$a"*x+"$b"*y+"$c":i"
19783    rm.
19784  done endl done
19785  rm.
19786
19787#@cli truchet : _scale>0,_radius>=0,_pattern_type={ 0=straight | 1=curved }
19788#@cli : Fill selected images with random truchet patterns.
19789#@cli : Default values: 'scale=32', 'radius=5' and 'pattern_type=1'.
19790#@cli : $ 400,300 truchet ,
19791truchet : check "isint(${1=32}) && $1>0 && ${2=3}>=0" skip ${3=1}
19792  e[^-1] "Render "${arg\ 1+!$3,curved,straight}" truchet patterns in image$?, with scale $1 and radius $2."
19793  repeat $! l[$>] nm={0,n}
19794    w={w} h={h} s={s} rm
19795    $1,$1 = 1,0,0 = 1,100%,100% distance 1,{1+$3} M={int(iM/2)} # Generate truchet pattern and its mirrored version.
19796    ir {$M-$2/2-($1%2)},{$M+$2/2} +mirror y a x
19797    {round($w/$1,1,1)},{round($h/$1,1,1)} rand. 0,1 >=. 50% r. {w*$1},{h*$1} *. $1
19798    channels. 0,1 (0,{$1-1}) r. $1,$1,1,1,3 +transpose. a[-2,-1] c ri. ..,0,2 +[-2,-1]
19799    warp.. . rm. >= 50% r $w,$h,1,1,0 r 100%,100%,1,$s
19800  nm $nm endl done
19801
19802#@cli turbulence : _radius>0,_octaves={1,2,3...,12},_alpha>0,_difference={-10,10},_mode={0,1,2,3}
19803#@cli : Render fractal noise or turbulence on selected images.
19804#@cli : Default values: 'radius=32', 'octaves=6', 'alpha=3', 'difference=0' and 'mode=0'.
19805#@cli : $ 400,400,1,3 turbulence 16
19806#@cli : $$ https://gmic.eu/oldtutorial/_turbulence
19807turbulence : check "${1=32}>0 && ${2=6}>0" skip ${3=3},${4=0},${5=0}
19808  e[^-1] "Render fractal noise or turbulence on image$?, with radius $1, octaves $2, damping per octave $3,
19809          difference $4 and mode $5."
19810  repeat $! l[$>] nm={0,n}
19811    if $4 . fi
19812    f. 0 +noise. 10,0 b. $1,0
19813    if $5==0||$5==1 -. {ia} abs.
19814    elif $5==3||$5==4 ^. 2
19815    elif $5==5 ^. 3
19816    fi
19817    repeat $2-1
19818      +noise.. 10,0 b. {$1/2^$>},0
19819      if $5==0 -. {ia} abs.
19820      elif $5==4 ^. 2
19821      elif $5==5 ^. 3
19822      fi
19823      *.. $3 +[-2--1]
19824    done
19825    n. 0,255
19826    rm..
19827    if $4 *. $4 mv.. 2 - n. 0,255 fi
19828  nm $nm endl done
19829
19830#@cli yinyang
19831#@cli : Draw a yin-yang symbol on selected images.
19832#@cli : $ 400,400 yinyang
19833yinyang :
19834  e[^-1] "Draw yin-yang symbol on image$?."
19835  f 0 repeat $! l[$>]
19836    s={s} channels 0
19837    r={round(0.95*min(w,h)/4)}
19838    +line 50%,0,50%,50%,1,2 ellipse. 50%,{h/2-$r},$r,$r,0,1,2
19839    line. 50%,50%,50%,100%,1,1 ellipse. 50%,{h/2+$r},$r,$r,0,1,1
19840    flood. {w/2-$r},50%,0,0,0,1,2
19841    flood. {w/2+$r},50%,0,0,0,1,1
19842    ellipse.. 50%,50%,{2*$r},{2*$r},0,1,1
19843    *
19844    ellipse. 50%,{h/2-$r},{$r/3},{$r/3},0,1,1
19845    ellipse. 50%,{h/2+$r},{$r/3},{$r/3},0,1,2
19846    r 100%,100%,1,$s
19847  endl done
19848
19849#---------------------------------
19850#
19851#@cli :: Matrix Computation
19852#
19853#---------------------------------
19854
19855#@cli dijkstra : starting_node>=0,ending_node>=0 : (+)
19856#@cli : Compute minimal distances and paths from specified adjacency matrices by the Dijkstra algorithm.
19857
19858#@cli eigen : (+)
19859#@cli : Compute the eigenvalues and eigenvectors of selected symmetric matrices or matrix fields.
19860#@cli : If one selected image has 3 or 6 channels, it is regarded as a field of 2x2 or 3x3 symmetric matrices,
19861#@cli : whose eigen elements are computed at each point of the field.
19862#@cli : $ (1,0,0;0,2,0;0,0,3) +eigen
19863#@cli : $ image.jpg structuretensors blur 2 eigen split[0] c
19864#@cli : $$ https://gmic.eu/oldtutorial/_eigen
19865
19866#@cli invert : solver={ 0=SVD | 1=LU } : (+)
19867#@cli : Compute the inverse of the selected matrices.
19868#@cli : SVD solver is slower but less numerically instable than LU.
19869#@cli : Default value: 'solver=1'.
19870#@cli : $ (0,1,0;0,0,1;1,0,0) +invert
19871
19872#@cli orthogonalize : _mode = { 0=orthogonalize | 1=orthonormalize }
19873#@cli : Orthogonalize or orthonormalize selected matrices, using Modified Gram-Schmidt process.
19874#@cli : Default value: 'mode=0'.
19875orthogonalize :
19876  if isbool($1) mode=$1 else mode=0 noarg fi
19877  u0,u1,v0,v1=Orthogonalize,Orthonormalize,x,ce
19878  e[^-1] ${u$mode}" matri"${v{$!!=1}}"$?, using Modified Gram-Schmidt process."
19879  repeat $! l[$>]
19880    eval ">
19881      proj(u,v) = (dot(u,v)/dot(u,u)*u);
19882      for (p = 1, p<w, ++p,
19883        ref(crop(p,0,1,h),v);
19884        repeat (p,q,
19885          ref(crop(q,0,1,h),u);
19886          v-=proj(u,v);
19887        );
19888        draw(v,p,0,0,0,1,h);
19889      )"
19890    if $mode
19891      eval. "*!y?(
19892        ref(crop(x,0,1,h),v);
19893        n = norm(v);
19894        v/=n?norm(v):1;
19895        draw(v,x,0,0,0,1,h);
19896      )"
19897    fi
19898  endl done
19899
19900#@cli meigen : m>=1
19901#@cli : Compute an approximation of the 'm' largest eigenvalues and eigenvectors of selected symmetric matrices,
19902#@cli : using the Arnoldi iteration method (https://en.wikipedia.org/wiki/Arnoldi_iteration).
19903#@cli : A larger 'm' goes with better numerical precision.
19904#@cli : $ (1,0,0;0,2,0;0,0,3) +meigen 3
19905meigen : check "isint($1) && $1>0"
19906  if $!!=1 s="ce" else s="x" fi
19907  e[^-1] "Compute $1 largest eigen-values of matri"$s"$?."
19908  repeat $! l[$>] nm={n}
19909    if w!=h" || "d!=1" || "s!=1 v 1 error[0--5] "Command 'meigen': Image '"$nm"' is not a square matrix." fi
19910    eval ${-math_lib}" store(meig(crop(),$1,h),'val',1,min($1,h))" $val k. nm $nm
19911  endl done
19912
19913#@cli mproj : [dictionary],_method,_max_iter={ 0=auto | >0 },_max_residual>=0 : (+)
19914#@cli : Find best matching projection of selected matrices onto the span of an over-complete
19915#@cli : dictionary D, using the orthogonal projection or Matching Pursuit algorithm.
19916#@cli : Selected images are 2D-matrices in which each column represent a signal to project.
19917#@cli : '[dictionary]' is a matrix in which each column is an element of the dictionary D.
19918#@cli : 'method' tells what projection algorithm must be applied. It can be:
19919#@cli : \   - 0 = orthogonal projection (least-squares solution using LU-based solver).
19920#@cli : \   - 1 = matching pursuit.
19921#@cli : \   - 2 = matching pursuit, with a single orthogonal projection step at the end.
19922#@cli : \   - >=3 = orthogonal matching pursuit where an orthogonal projection step is performed
19923#@cli : \           every 'method-2' iterations.
19924#@cli : 'max_iter' sets the max number of iterations processed for each signal.
19925#@cli : If set to '0' (default), 'max_iter' is equal to the number of columns in D.
19926#@cli : (only meaningful for matching pursuit and its variants).
19927#@cli : 'max_residual' gives a stopping criterion on signal reconstruction accuracy.
19928#@cli : (only meaningful for matching pursuit and its variants).
19929#@cli : For each selected image, the result is returned as a matrix W
19930#@cli : whose columns correspond to the weights associated to each column of D,
19931#@cli : such that the matrix product D*W is an approximation of the input matrix.
19932#@cli : Default values: 'method=0', 'max_iter=0' and 'max_residual=1e-6'.
19933
19934#@cli solve : [image] : (+)
19935#@cli : Solve linear system AX = B for selected B-matrices and specified A-matrix.
19936#@cli : If the system is under- or over-determined, the least squares solution is returned
19937#@cli : (using SVD-based solver).
19938#@cli : $ (0,1,0;1,0,0;0,0,1) (1;2;3) +solve[-1] [-2]
19939
19940#@cli svd : (+)
19941#@cli : Compute SVD decomposition of selected matrices.
19942#@cli : $ 10,10,1,1,'if(x==y,x+u(-0.2,0.2),0)' +svd
19943
19944#@cli transpose
19945#@cli : Transpose selected matrices.
19946#@cli : $ image.jpg +transpose
19947transpose :
19948  e[^-1] "Transpose image$?."
19949  permute yxzc
19950
19951#@cli trisolve : [image] : (+)
19952#@cli : Solve tridiagonal system AX = B for selected B-vectors and specified tridiagonal A-matrix.
19953#@cli : Tridiagonal matrix must be stored as a 3 column vector, where 2nd column contains the
19954#@cli : diagonal coefficients, while 1st and 3rd columns contain the left and right coefficients.
19955#@cli : $ (0,0,1;1,0,0;0,1,0) (1;2;3) +trisolve[-1] [-2]
19956
19957#---------------------------------
19958#
19959#@cli :: 3D Meshes
19960#
19961#---------------------------------
19962
19963#@cli +3d : eq. to 'add3d'. : (+)
19964
19965#@cli add3d : tx,_ty,_tz : [object3d] : (no arg) : (+)
19966#@cli : Shift selected 3D objects with specified displacement vector, or merge them with specified
19967#@cli : 3D object, or merge all selected 3D objects together.
19968#@cli : (eq. to '+3d').
19969#@cli : Default values: 'ty=tz=0'.
19970#@cli : $ sphere3d 10 repeat 5 +add3d[-1] 10,{u(-10,10)},0 color3d[-1] ${-rgb} done add3d
19971#@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 \
19972# add3d rotate3d[-1] 0,0,1,18 done double3d 0
19973
19974#@cli animate3d : nb_frames>0,_step_angle_x,_step_angle_y,_step_angle_z,_zoom_factor,0<=_fake_shadow_level<=100,\
19975# _[background]
19976#@cli : Generate 3D animation frames of rotating 3D objects.
19977#@cli : Frames are stacked along the z-axis (volumetric image).
19978#@cli : Frame size is the same as the size of the '[background]' image (or 800x800 if no background specified).
19979#@cli : Default values: 'filename=(undefined)'.
19980animate3d : check "isint($1) && $1>0 && isnum(${2=0}) && isnum(${3=5}) && isnum(${4=0}) && ${5=1}>0 && "\
19981                  "inrange(${6=50},0,100)" skip "${7=}"
19982  e[^-1] "Generate 3D animation frames from 3D object$?, with $1 frames, angle steps (${2-4}), zoom factor $5 "\
19983         "and $6% fake shadow."
19984  if ${"is_image_arg $7"} pass$7
19985  else 3,2,1,1,"32,32,64,64,116,96" permute. cyzx r. 800,800,1,3,3 round.
19986  fi
19987  nm. anim3d_bg
19988  repeat {$!-1} l[$>,-1] check ${-is_3d..} nm={0,n} bn={0,b}
19989    e[] "  * Object '"$bn"': 0/$1"
19990    c3d[0] n3d[0] *3d[0] {1,$5*min(w,h)/2}
19991    repeat $1
19992      e[] "\r  * Object '"$bn"': "{1+$>}"/$1"
19993      +r3d[0] 0,0,1,{$>*$4} r3d. 0,1,0,{$>*$3} r3d. 1,0,0,{$>*$2}
19994      {1,[w,h,d,4]},-1 j3d. ..,50%,50%,0,1
19995      sh. 0,{s-2} sh.. 100% !=. -1 mul.. . mul. 255 rm[-2,-1]
19996      if $6 sh. 100% +b. 2% shift. {m=min(w,h)*2%;[m,m]} -. {255-$6*255%} c. 0,255 max[-2,-1] rm. fi # Add fake shadow
19997      +blend[1,-1] alpha rm[-3,-2]
19998      if {*} w. -1,-1,0 fi
19999    done
20000    a[2--1] z
20001    rv[0,-1] rm.
20002  endl nm[$>] $nm done
20003  rm.
20004
20005#@cli apply_camera3d : pos_x,pos_y,pos_z,target_x,target_y,target_z,up_x,up_y,up_z
20006#@cli : Apply 3D camera matrix to selected 3D objects.
20007#@cli : Default values: 'target_x=0', 'target_y=0', 'target_z=0', 'up_x=0', 'up_y=-1' and 'up_z=0'.
20008apply_camera3d : skip ${4=0},${5=0},${6=0},${7=0},${8=-1},${9=0}
20009  e[^-1] "Apply 3D camera matrix to 3D object$?, with camera position ($1,$2,$3), target position ($4,$5,$6) and
20010          up-vector ($7,$8,$9)."
20011  ({$4-$1}^{$5-$2}^{$6-$3})  # f.
20012  ($7^$8^$9)                 # up.
20013  orientation[-2,-1]         # f/|f| and up/|up|.
20014  _cross3d {-2,^},{^}        # s = f x up
20015  _cross3d {^},{-3,^}        # u = s x f
20016  rm... y[-3--1] x mv[-2,-1] -3
20017  a[-3--1] y z. 0,3  # Rotation matrix R.
20018  -3d[^-1] $1,$2,$3 pose3d[^-1] {^} rm. -3d 0,0,800
20019
20020_cross3d :
20021  ({$2*$6-$3*$5}^{$3*$4-$1*$6}^{$1*$5-$2*$4}) orientation. y.
20022
20023#@cli apply_matrix3d : a11,a12,a13,...,a31,a32,a33
20024#@cli : Apply specified 3D rotation matrix to selected 3D objects.
20025#@cli : $ torus3d 10,1 +apply_matrix3d {mul(rot(1,0,1,-15°),[1,0,0,0,2,0,0,0,8],3)} double3d 0
20026apply_matrix3d :
20027  e[^-1] "Apply 3x3 matrix (${1-3};${4-6};${7-9}) to 3D object$?."
20028  repeat $! l[$>]
20029    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.
20030  endl done
20031
20032#@cli array3d : size_x>=1,_size_y>=1,_size_z>=1,_offset_x[%],_offset_y[%],_offset_y[%]
20033#@cli : Duplicate a 3D object along the X,Y and Z axes.
20034#@cli : Default values: 'size_y=1', 'size_z=1' and 'offset_x=offset_y=offset_z=100%'.
20035#@cli : $ torus3d 10,1 +array3d 5,5,5,110%,110%,300%
20036array3d : check "isint($1) && $1>0 && isint(${2=1}) && $2>0 && isint(${3=1}) && $3>0"
20037  skip ${4=100%},${5=100%},${6=100%}
20038  e[^-1] "Duplicate 3D object$? along X,Y,Z axes with factors ($1,$2,$3) and offsets ($4,$5,$6)."
20039  repeat $! l[$>]
20040
20041    # Retrieve object dimensions.
20042    +rows 8,{8+3*i[6]} r. 3,{h/3},1,1,-1 s. x,3
20043    dx={-3,if(${is_percent\ $4},$4*(iM-im),$4)}
20044    dy={-2,if(${is_percent\ $5},$5*(iM-im),$5)}
20045    dz={if(${is_percent\ $6},$6*(iM-im),$6)}
20046    rm[-3--1]
20047
20048    # Duplicate along X.
20049    off=0 repeat int(log2($1))
20050      ++3d. {2^$>*$dx} +3d. ..
20051      if !($1&(2^$>)) rm.. else +3d.. $off off+={2^$>*$dx} fi
20052    done +3d. $off +3d
20053
20054    # Duplicate along Y.
20055    off=0 repeat int(log2($2))
20056      ++3d. 0,{2^$>*$dy} +3d. ..
20057      if !($2&(2^$>)) rm.. else +3d.. 0,$off off+={2^$>*$dy} fi
20058    done +3d. 0,$off +3d
20059
20060    # Duplicate along Z.
20061    off=0 repeat int(log2($3))
20062      ++3d. 0,0,{2^$>*$dz} +3d. ..
20063      if !($3&(2^$>)) rm.. else +3d.. 0,0,$off off+={2^$>*$dz} fi
20064    done +3d. 0,0,$off +3d
20065  endl done
20066
20067#@cli arrow3d : x0,y0,z0,x1,y1,z1,_radius[%]>=0,_head_length[%]>=0,_head_radius[%]>=0
20068#@cli : Input 3D arrow with specified starting and ending 3D points.
20069#@cli : Default values: 'radius=5%', 'head_length=25%' and 'head_radius=15%'.
20070#@cli : $ repeat 10 a={$>*2*pi/10} arrow3d 0,0,0,{cos($a)},{sin($a)},-0.5 done +3d
20071+arrow3d : check "${7=5%}>=0 && ${8=25%}>=0 && ${9=15%}>=0"
20072  e[^-1] "Input 3D arrow, from (${1-3}) to (${4-6}), with radius $7, head length $8 and head radius $9."
20073
20074  # Create 3D object.
20075  L={sqrt(($4-$1)^2+($5-$2)^2+($6-$3)^2)}
20076  R={if(${is_percent\ $7},$7*$L,$7)}
20077  l={if(${is_percent\ $8},$8*$L,$8)}
20078  r={if(${is_percent\ $9},$9*$L,$9)}
20079  L-=$l cylinder3d $R,$L cone3d $r,$l +3d. 0,0,$L +3d[-2,-1]
20080
20081  # Compute rotation matrix for arrow orientation.
20082  ({$4-$1}^{$5-$2}^{$6-$3}) (0.01^-0.02^0.03) orientation[-2,-1]
20083  _cross3d {-2,^},{^} _cross3d {^},{-3,^} rm... y[-3--1] x mv[-2,-1] -3
20084  a[-3--1] y
20085
20086  # Rotate and translate the arrow at specified coordinates.
20087  s3d.. r[-5] 3,{-5,h/3},1,1,-1 m*[-5,-1]
20088  y[-4] a[-6--1] y +3d. ${1-3} rv3d.
20089
20090#@cli axes3d : _size_x,_size_y,_size_z,_font_size>0,_label_x,_label_y,_label_z,_is_origin={ 0=no | 1=yes }
20091#@cli : Input 3D axes with specified sizes along the x,y and z orientations.
20092#@cli : Default values: 'size_x=size_y=size_z=1', 'font_size=23', 'label_x=X', 'label_y=Y', 'label_z=Z' and \
20093# 'is_origin=1'
20094#@cli : $ axes3d ,
20095+axes3d : check "${4=23}>0 && isbool(${8=1})" skip ${1=1},${2=$1},${3=$2},"${5=X},${6=Y},${7=Z}"
20096  e[^-1] "Input 3D axes with sizes ($1,$2,$3)."
20097  l[]
20098  m={max(abs($1),abs($2),abs($3))/40} m2={2*$m} m3={1.2*$m2}
20099  if $1 line3d 0,0,0,$1,0,0 fi
20100  if $2 line3d 0,0,0,0,$2,0 fi
20101  if $3 line3d 0,0,0,0,0,$3 fi
20102  if $1
20103    cone3d $m,{2*$m},16 r3d. 0,1,0,90 +3d. {$1-$m2},0,0
20104    _axes3d "$5",$4 +3d. {$1+$m3},0,0
20105  fi
20106  if $2
20107    cone3d $m,{2*$m},16 r3d. 1,0,0,-90 +3d. 0,{$2-$m2},0
20108    _axes3d "$6",$4 +3d. 0,{$2+$m3},0
20109  fi
20110  if $3
20111    cone3d $m,{2*$m},16 +3d. 0,0,{$3-$m2}
20112    _axes3d "$7",$4 +3d. 0,0,{$3+$m3}
20113  fi
20114  if $8 _axes3d "O",$4 -3d. $m3,$m3,$m3 fi
20115  +3d nm [3d\ axes]
20116  endl
20117
20118_axes3d :
20119  0 t. "$1",2,0,$2,1,1 +dilate. 3 *.. 255 r.. 100%,100%,1,3
20120  i... (67.5;73.5;109.5;103.5;51.5;100.5;1;1;0;0;0;1;0;-128;{w};{h};3)
20121  i.. (-128;{w};{h};1) y[-3,-1] a[-4--1] y
20122
20123#@cli boundingbox3d
20124#@cli : Replace selected 3D objects by their 3D bounding boxes.
20125#@cli : $ torus3d 100,30 +boundingbox3d +3d[-1] [-2]
20126boundingbox3d :
20127  e[^-1] "Replace 3D object$? by their 3D bounding boxes."
20128  repeat $! l[$>]
20129    nbv={f2ui(i[6])} rows 8,{8+3*$nbv} r. 3,$nbv,1,1,-1 s. x
20130    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 ]"}
20131    rm box3d $sx,$sy,$sz c3d +3d $xc,$yc,$zc p3d. 1
20132  endl done
20133
20134#@cli box3d : _size_x,_size_y,_size_z
20135#@cli : Input 3D box at (0,0,0), with specified geometry.
20136#@cli : Default values: 'size_x=1' and 'size_z=size_y=size_x'.
20137#@cli : $ box3d 100,40,30 +primitives3d 1 color3d[-2] ${-rgb}
20138+box3d : skip ${1=1},${2=$1},${3=$2}
20139  e[^-1] "Input 3D box, with size ($1,$2,$3)."
20140  1,86,1,1,\
20141  67.5,73.5,109.5,103.5,51.5,100.5,8,6,\
20142  0,0,0,$1,0,0,$1,$2,0,0,$2,0,\
20143  0,0,$3,$1,0,$3,$1,$2,$3,0,$2,$3,\
20144  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,\
20145  200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,\
20146  1,1,1,1,1,1
20147  nm. [3D\ box]
20148
20149#@cli c3d : eq. to 'center3d'.
20150c3d :
20151  _center3d
20152
20153#@cli center3d
20154#@cli : Center selected 3D objects at (0,0,0).
20155#@cli : (eq. to 'c3d').
20156#@cli : $ repeat 100 circle3d {u(100)},{u(100)},{u(100)},2 done add3d color3d[-1] 255,0,0 +center3d \
20157# color3d[-1] 0,255,0 add3d
20158center3d :
20159  _$0
20160
20161_center3d :
20162  e[0--3] "Center 3D object$?."
20163  check3d 0 repeat $! l[$>]
20164    if i[6]
20165      s3d r[2] 3,{2,h/3},1,1,-1 s[2] x
20166      -[2] {2,(iM+im)/2} -[3] {3,(iM+im)/2} -[4] {4,(iM+im)/2}
20167      a[2-4] x y[2] a y
20168    fi
20169  endl done
20170
20171#@cli circle3d : _x0,_y0,_z0,_radius>=0
20172#@cli : Input 3D circle at specified coordinates.
20173#@cli : Default values: 'x0=y0=z0=0' and 'radius=1'.
20174#@cli : $ repeat 500 a={$>*pi/250} circle3d {cos(3*$a)},{sin(2*$a)},0,{$a/50} color3d[-1] ${-rgb},0.4 done add3d
20175+circle3d : skip ${1=0},${2=0},${3=0},${4=1}
20176  e[^-1] "Input 3D circle at position ($1,$2,$3) with radius $4."
20177  r={$4/sqrt(3)}
20178  1,24,1,1,\
20179  67.5,73.5,109.5,103.5,51.5,100.5,2,1,\
20180  {$1-$r},{$2-$r},{$3-$r},\
20181  {$1+$r},{$2+$r},{$3+$r},\
20182  5,0,1,0,0,0,200,200,200,1
20183  nm. [3D\ circle]
20184
20185#@cli circles3d : _radius>=0,_is_wireframe={ 0 | 1 }
20186#@cli : Convert specified 3D objects to sets of 3D circles with specified radius.
20187#@cli : Default values: 'radius=1' and 'is_wireframe=1'.
20188#@cli : $ image.jpg luminance resize2dy 40 threshold 50% * 255 pointcloud3d color3d[-1] 255,255,255 circles3d 0.7
20189circles3d : check "${1=1}>=0 && isbool(${2=0})"
20190  e[^-1] "Convert 3D object$? to sets of 3D "${arg\ 1+$2,filled,wireframe}" circles with radius $1."
20191  p3d 0 repeat $! l[$>]
20192    -3d {$1/2},0,0 ++3d $1,0,0 +3d[1] [0]
20193    s3d # Two 3d objects decomposed here!
20194    rows[7] 0 j[1] [7] # Number of points
20195    rv[2,8] # Points
20196    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
20197    k[0-5] a y
20198  endl done
20199
20200#@cli col3d : eq. to 'color3d'. : (+)
20201
20202#@cli color3d : R,_G,_B,_opacity : (no arg) : (+)
20203#@cli : Set color and opacity of selected 3D objects.
20204#@cli : (eq. to 'col3d').
20205#@cli : Default value: 'B=G=R' and 'opacity=(undefined)'.
20206#@cli : $ torus3d 100,10 double3d 0 repeat 7 +rotate3d[-1] 1,0,0,20 color3d[-1] ${-rgb} done add3d
20207
20208#@cli colorcube3d
20209#@cli : Input 3D color cube.
20210#@cli : $ colorcube3d mode3d 2 +primitives3d 1
20211+colorcube3d :
20212  e[^-1] "Input 3D RGB-color cube."
20213  (67.5;73.5;109.5;103.5;51.5;100.5;8;6)
20214  (0;0;0;\
20215   255;0;0;\
20216   255;255;0;\
20217   0;255;0;\
20218   0;0;255;\
20219   255;0;255;\
20220   255;255;255;\
20221   0;255;255)
20222  (12;0;3;2;1;0;0;0;63;63;63;63;0;\
20223   12;1;2;6;5;0;0;0;63;63;63;63;0;\
20224   12;0;4;7;3;0;0;63;0;63;63;0;63;\
20225   12;4;5;6;7;0;0;63;0;63;63;0;63;\
20226   12;0;1;5;4;0;0;63;0;63;63;0;63;\
20227   12;3;7;6;2;0;0;0;63;63;63;63;0)
20228  (0,255;0,255^0,0;255,255^0,0;0,0)
20229  (255,255;255,255^0,0;255,255^0,255;0,255)
20230  (0,0;0,0^0,0;255,255^0,255;0,255)
20231  (0,255;0,255^0,0;255,255^255,255;255,255)
20232  (0,255;0,255^0,0;0,0^0,0;255,255)
20233  (0,255;0,255^255,255;255,255^0,0;255,255)
20234  r[-6--1] 64,64,1,3,3 round[-6--1] y[-6--1] i[-7--2] (-128;64;64;3)
20235  (1;1;1;1;1;1)
20236  a[-16--1] y nm. [3D\ colorcube]
20237
20238#@cli cone3d : _radius,_height,_nb_subdivisions>0
20239#@cli : Input 3D cone at (0,0,0), with specified geometry.
20240#@cli : Default value: 'radius=1','height=1' and 'nb_subdivisions=24'.
20241#@cli : $ cone3d 10,40 +primitives3d 1 color3d[-2] ${-rgb}
20242+cone3d : check ${3=24}>0 skip ${1=1},${2=1}
20243  e[^-1] "Input 3D cone, with radius $1, height $2 and $3 subdivisions."
20244  # Header.
20245  (67.5;73.5;109.5;103.5;51.5;100.5)
20246  ({$3+2};{2*$3})
20247
20248  # Vertices.
20249  (0,0,0;0,0,$2)
20250  (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
20251
20252  # Primitives.
20253  1,$3,1,1,'y' +shift. 0,-1 +[-2,-1] 2
20254  2,$3,1,1,3,0 .. [-4] a[-3--1] x
20255  i[-4] 2,$3,1,1,3,1 a[-4--2] x
20256  a[-2,-1] y
20257
20258  # Colors / opacities.
20259  3,{h},1,1,200
20260  1,{h},1,1,1
20261  y[-4--2] a[-6--1] y nm. [3D\ cone]
20262
20263#@cli cubes3d : _size>=0
20264#@cli : Convert specified 3D objects to sets of 3D cubes with specified size.
20265#@cli : Default value: 'size=1'.
20266#@cli : $ image.jpg luminance resize2dy 40 threshold 50% * 255 pointcloud3d color3d[-1] 255,255,255 cubes3d 1
20267cubes3d : check ${1=1}>=0
20268  e[^-1] "Convert 3D object$? to sets of 3D cubes with size $1."
20269  p3d 0 repeat $! l[$>]
20270    nbv={@6} nbp={@7}
20271    if $nbv&&$nbp
20272      s3d
20273      l[1] = {8*i[0]} = {6*i[1]},0,1 endl  # Header.
20274      l[2] r 3,{h/3},1,1,-1                  # Vertices.
20275      half={$1/2}
20276      - '$half,0,0' ++ '$1,0,0' a x
20277      - '0,$half,0' ++ '0,$1,0' a x
20278      - '0,0,$half' ++ '0,0,$1' a x
20279      endl
20280      l[3] r 2,{h/2},1,1,-1                  # Primitives.
20281      z 1,1 * 8 r 4,100% i[0] 1,100%,1,1,4 a x [-1]x5 a x
20282      + '"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"'
20283      endl
20284      l[4] r 3,{h/3},1,1,-1 r 18,100%,1,1,0,2 endl r[5] 6,100% # Colors & opacities.
20285      y a y
20286    fi
20287  endl done
20288
20289#@cli cup3d : _resolution>0
20290#@cli : Input 3D cup object.
20291#@cli : Default value: 'resolution=128'.
20292#@cli : $ cup3d ,
20293+cup3d : check ${1=128}>0
20294  e[^-1] "Input 3D cup, with resolution $1."
20295  100,200
20296  ellipse. 0%,0%,40%,40%,0,1,1
20297  ellipse. 0,0,35%,35%,0,1,0
20298  polygon. 4,0,45%,8%,45%,20%,90%,0,90%,1,1
20299  ellipse. 0%,100%,30%,10%,0,1,1 b. 0.1%
20300  lathe3d. $1,2 nm. [3D\ cup]
20301
20302#@cli cylinder3d : _radius,_height,_nb_subdivisions>0
20303#@cli : Input 3D cylinder at (0,0,0), with specified geometry.
20304#@cli : Default value: 'radius=1','height=1' and 'nb_subdivisions=24'.
20305#@cli : $ cylinder3d 10,40 +primitives3d 1 color3d[-2] ${-rgb}
20306+cylinder3d : check ${3=24}>0 skip ${1=1},${2=1}
20307  e[^-1] "Input 3D cylinder, with radius $1, height $2 and $3 subdivisions."
20308  l[]
20309    N={round($3)}
20310    nbv,nbp={[2*$N+2,3*$N]}
20311    ({0.5+[{'CImg3d'}]})
20312    ($nbv,$nbp)
20313    1,$nbv,1,3,"Z = (y<="$N"?0:$2); ang = ((y%("$N"+1))-1)*2*pi/"$N";
20314                !(y%("$N"+1))?[0,0,Z]:[$1*cos(ang),$1*sin(ang),Z]"
20315    1,$N,1,13,"i1 = 1 + y; i2 = 1 + (i1%"$N"); const j0 = "$N" + 1; j1 = j0 + i1; j2 = j0 + i2;
20316               [ 3,0,i2,i1, 3,j0,j1,j2, 4,i1,i2,j2,j1 ]"
20317    permute[^0,1] "cyzx" 1,$nbp,1,3,200 1,$nbp,1,1,1 y a y
20318   nm. [3D\ cylinder] endl
20319
20320#@cli delaunay3d
20321#@cli : Generate 3D Delaunay triangulations from selected images.
20322#@cli : One assumes that the selected input images are binary images containing the set of points to mesh.
20323#@cli : The output 3D object is a mesh composed of non-oriented triangles.
20324#@cli : $ 500,500 noise 0.05,2 eq 1 * 255 +delaunay3d color3d[1] 255,128,0 dilate_circ[0] 5 to_rgb[0] \
20325# +object3d[0] [1],0,0,0,1,1 max[-1] [0]
20326delaunay3d :
20327  e[^-1] "Generate 3D Delaunay triangulation from image$?."
20328  repeat $! l[$>]
20329    channels 0 != 0
20330
20331    # Label each point separately
20332    whd={w},{h},{d} +r 1,{w*h*d},1,1,-1 cumulate. *. .. r. $whd,1,-1
20333
20334    # Compute voronoi diagram of point cloud.
20335    +distance[0] 1 *[2] -1 watershed[1] [2] rm[2]
20336
20337    # Get redondant set of Delaunay triangles from the voronoi diagram.
20338    r[1] 100%,100%,100%,3
20339    if d>1 # Add detection cases for 3D images.
20340      +_delaunay3d[1] 1,0,0,0,0,1 +_delaunay3d[1] -1,0,0,0,0,-1
20341      +_delaunay3d[1] 0,1,0,0,0,1 +_delaunay3d[1] 0,-1,0,0,0,-1
20342    fi
20343    +_delaunay3d[1] 1,0,0,0,1,0 _delaunay3d[1] -1,0,0,0,-1,0 # 2D detection.
20344    a[^0] x transpose. -. 1
20345
20346    # Build 3D mesh.
20347    pointcloud3d[0]
20348    s3d[0] rm[3-5] i.. 1,100%,1,1,3 a[-2,-1] x
20349    3,100%,1,1,200 1,100%,1,1,1 =[1] {h},0,1 y a y
20350  endl done
20351
20352_delaunay3d :
20353  f. "A=j($1,$2,$3,0,0,1); B=j($4,$5,$6,0,0,1);
20354      if(i!=A && i!=B && A!=B, kth(1+c,i,A,B),0)"
20355  discard. 0 r. {h/3},3,1,1,-1
20356
20357#@cli distribution3d
20358#@cli : Get 3D color distribution of selected images.
20359#@cli : $ image.jpg distribution3d colorcube3d primitives3d[-1] 1 add3d
20360distribution3d :
20361  e[^-1] "Get 3D color distribution of image$?."
20362  to_rgb permute "cxyz" y
20363  repeat $! l[$>]
20364    nbp={round(h/3)}
20365    i.. (67.5;73.5;109.5;103.5;51.5;100.5;\    # Magick number for CImg3d.
20366         $nbp;$nbp)                            # Number of vertices and primitives.
20367    1,$nbp,1,1,1 +f. y a[-2,-1] x y.           # Primitives.
20368    ..                                         # Colors.
20369    1,$nbp,1,1,1                               # Opacities.
20370    a y nm. [3D\ distribution]                 # Build 3D object.
20371  endl done
20372
20373#@cli /3d : eq. to 'div3d'. : (+)
20374
20375#@cli div3d : factor : factor_x,factor_y,_factor_z : (+)
20376#@cli : Scale selected 3D objects isotropically or anisotropically, with the inverse of specified
20377#@cli : factors.
20378#@cli : (eq. to '/3d').
20379#@cli : Default value: 'factor_z=0'.
20380#@cli : $ torus3d 5,2 repeat 5 +add3d[-1] 12,0,0 div3d[-1] 1.2 color3d[-1] ${-rgb} done add3d
20381
20382#@cli db3d : eq. to 'double3d'. : (+)
20383
20384#@cli double3d : _is_double_sided={ 0 | 1 } : (+)
20385#@cli : Enable/disable double-sided mode for 3D rendering.
20386#@cli : (eq. to 'db3d').
20387#@cli : Default value: 'is_double_sided=1'.
20388#@cli : $ mode3d 1 repeat 2 torus3d 100,30 rotate3d[-1] 1,1,0,60 double3d $> snapshot3d[-1] 400 done
20389
20390#@cli elevation3d : { z-factor | [elevation_map] | 'formula' },base_height={ -1 | >=0 } : (no arg)
20391#@cli : Generate 3D elevation of selected images, opt. with a specified elevation map.
20392#@cli : When invoked with (no arg) or 'z-factor', the elevation map is computed as the pointwise L2 norm of the
20393#@cli : pixel values. Otherwise, the elevation map is taken from the specified image or formula.
20394#@cli : $ image.jpg +blur 5 elevation3d. 0.75
20395#@cli : $ 128,128,1,3,u(255) plasma 10,3 blur 4 sharpen 10000 n 0,255 \
20396# elevation3d[-1] 'X=(x-64)/6;Y=(y-64)/6;-100*exp(-(X^2+Y^2)/30)*abs(cos(X)*sin(Y))'
20397elevation3d : skip "${1=_noarg}" check "${2=-1}==-1 || $2>=0"
20398  if $2>=0 base_str=" and base height $2" else base_str= fi
20399  if isnum($1)
20400    e[^-1] "Generate 3D elevation of image$?, with z-factor $1"$base_str. argtype,zfactor=0,$1
20401  elif ${"is_image_arg $1"}
20402    e[^-1] "Generate 3D elevation of image$?, from elevation $1"$base_str. argtype=2
20403    pass$1 0 if s>1 norm. fi store. elevation_img
20404  elif isexpr($1)
20405    e[^-1] "Generate 3D elevation of image$?, with formula '$1'"$base_str. argtype=1
20406  else
20407    e[^-1] "Generate 3D elevation of image$?." argtype,zfactor=0,1
20408  fi
20409  is_base={$2>=0}
20410
20411  repeat $! l[$>] nm={n}
20412    to_rgb M,N={[w,h]}
20413
20414    # Generate vertices.
20415    100%,100%,1,2,[x,y]
20416    if !$argtype +norm[0] *. $zfactor # with z-factor
20417    elif $argtype==1 [0],[0],1,1,"$1" # with formula
20418    else $elevation_img # from [image]
20419    fi
20420    a[-2,-1] c
20421
20422    if $is_base . sh. 100% f. {-sign($1)*$2} rm. a[-2,-1] y fi
20423    r. {wh},1,1,3,-1 permute. cxyz nbv={h}
20424
20425    # Generate primitives.
20426    header="const M = "$M"; const N = "$N"; const MN = M*N"
20427    {[$M,$N]-1},1,5,$header"; # Rear
20428      i0 = M*y + x; i1 = i0 + MN;
20429      [ 4,ui2f(i0),ui2f(i0 + M),ui2f(i0 + 1 + M),ui2f(i0 + 1) ]"
20430    r. 1,{wh},1,100%,-1 nbp={h}
20431
20432    if $is_base # Add base primitives if necessary
20433      {[$M,$N]-1},1,5,$header"; # Front
20434        i0 = M*y + x; i1 = i0 + MN;
20435        [ 4,ui2f(i1),ui2f(i1 + 1),ui2f(i1 + 1 + M),ui2f(i1 + M) ]"
20436      r. 1,{wh},1,100%,-1 nbp+={h}
20437      {$M-1},1,1,10,$header" ; # Top and bottom
20438        i0 = x; i1 = i0 + MN; i2 = i0 + M*(N - 1); i3 = i2 + MN;
20439        [ 4,ui2f(i0),ui2f(i0 + 1),ui2f(i1 + 1),ui2f(i1),
20440          4,ui2f(i2),ui2f(i3),ui2f(i3 + 1),ui2f(i2 + 1) ]"
20441      r. 1,{wh},1,100%,-1 s. c,2 nbp+={2*h}
20442      {$N-1},1,1,10,$header"; # Left and right
20443        i0 = M*x; i1 = i0 + MN; i2 = i0 + M - 1; i3 = i2 + MN;
20444        [ 4,ui2f(i0),ui2f(i1),ui2f(i1 + M),ui2f(i0 + M),
20445          4,ui2f(i2),ui2f(i2 + M),ui2f(i3 + M),ui2f(i3) ]"
20446      r. 1,{wh},1,100%,-1 s. c,2 nbp+={2*h}
20447      permute[-6--1] cyxz -a[-6--1] y
20448    else permute. cyxz
20449    fi
20450
20451    # Generate colors / opacities.
20452    mv[0] $! r. {[w,h]-1},1,3,0 r. {wh},1,1,3,-1 permute. cxyz
20453    if $is_base 3,{$nbp-h},1,1,200 fi
20454    1,$nbp,1,1,1
20455
20456    # Add header and merge object.
20457    i[0] ('CImg3d':y) +[0] 0.5 i[1] ({ui2f([$nbv,$nbp]):;}) y a y
20458    nm $nm
20459  endl done
20460
20461#@cli empty3d
20462#@cli : Input empty 3D object.
20463#@cli : $ empty3d
20464+empty3d :
20465  e[^-1] "Input empty 3D object."
20466  (67.5;73.5;109.5;103.5;51.5;100.5;0;0) nm. [3D\ empty]
20467
20468#@cli extrude3d : _depth>0,_resolution>0,_smoothness[%]>=0
20469#@cli : Generate extruded 3D object from selected binary XY-profiles.
20470#@cli : Default values: 'depth=16', 'resolution=1024' and 'smoothness=0.5%'.
20471#@cli : $ image.jpg threshold 50% extrude3d 16
20472extrude3d : check "${1=16}>0 && ${2=1024}>0 && ${3=0.5%}>=0"
20473  e[^-1] "Generate extruded 3D object from XY-profile$?, with depth $1, resolution $2 and smoothness $3."
20474  norm n 0,1 autocrop 0 repeat $! l[$>] nm={0,n}
20475    wr={round(max(1,if(w>h,min($2,w),min($2,h)*w/h)))}
20476    hr={round(max(1,if(w>h,min($2,w)*h/w,min($2,h))))}
20477    fact={$1/max(w/$wr,h/$hr)}
20478    b $3,0 r $wr,$hr,1,1,2 expand_xyz 1,0
20479    isosurface3d 50% *3d 1,1,$fact rv3d
20480  nm $nm endl done
20481
20482#@cli f3d : eq. to 'focale3d'. : (+)
20483
20484#@cli focale3d : focale : (+)
20485#@cli : Set 3D focale.
20486#@cli : (eq. to 'f3d').\n
20487#@cli : Set 'focale' to 0 to enable parallel projection (instead of perspective).
20488#@cli : Set negative 'focale' will disable 3D sprite zooming.
20489#@cli : Default value: 'focale=700'.
20490#@cli : $ repeat 5 torus3d 100,30 rotate3d[-1] 1,1,0,60 focale3d {$<*90} snapshot3d[-1] 400 done remove[0]
20491
20492#@cli gaussians3d : _size>0,_opacity
20493#@cli : Convert selected 3D objects into set of 3D gaussian-shaped sprites.
20494#@cli : $ image.jpg r2dy 32 distribution3d gaussians3d 20 colorcube3d primitives3d[-1] 1 +3d
20495gaussians3d : check "${1=32}>0" skip ${2=0.3}
20496  e[^-1] "Convert 3D object$? into sets of gaussian-shaped 3D sprites, with size $1 and opacity $2."
20497  p3d 2 p3d 0 repeat $! l[$>] nm={0,n} s3d
20498    nbv={h} rm. (-128;$1;$1;1)
20499    $1,$1 gaussian. 35%,35%,0 c. 30%,100% n. 0,$2 y. a[-2,-1] y      # First opacity is generated.
20500    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.
20501    a y
20502  nm $nm endl done
20503
20504#@cli gmic3d
20505#@cli : Input a 3D G'MIC logo.
20506#@cli : $ gmic3d +primitives3d 1
20507+gmic3d :
20508  e[^-1] "Input 3D G\47MIC logo."
20509  text3d G,60,20,2 col3d. 16,64,255
20510  text3d \',60,20,2 +3d. 40 col3d. 64,128,255
20511  text3d M,60,20,2 +3d. 50 col3d. 96,196,255
20512  text3d I,60,20,2 +3d. 90 col3d. 64,128,255
20513  text3d C,60,20,2 +3d. 100 col3d. 16,64,255
20514  sphere3d 8 +3d. 102,-3,20 col3d. 192,128,255
20515  +3d[-6--1] c3d.
20516  repeat 30
20517    box3d {min(3+$</2,10)} col3d. {30*$>},{20+80*$>},{10*$>},0.5
20518    r3d. 1,1,1,{$>*12}
20519    +3d. {80*cos(0.5+1.02*$>*12*pi/180)},{30*sin(0.8+$>*12*pi/180)},{2*$>-60}
20520  done
20521  +3d[-30--1] +3d. 0,5,30 +3d[-2--1] nm. [3d\ gmic]
20522
20523#@cli gyroid3d : _resolution>0,_zoom
20524#@cli : Input 3D gyroid at (0,0,0), with specified resolution.
20525#@cli : Default values: 'resolution=32' and 'zoom=5'.
20526#@cli : $ gyroid3d 48 +primitives3d 1
20527+gyroid3d : check ${1=32}>0 skip ${2=5}
20528  e[^-1] "Input 3D gyroid, with resolution $1 and range $2."
20529  isosurface3d "'0.49*(\
20530    cos( 2*x + y + z - pi) + cos( 2*x - y + z - pi)\
20531    + cos(- 2*x + y - z - pi) + cos(- 2*x - y - z - pi)\
20532    + cos( x + 2*y + z - pi) + cos( x + 2*y - z - pi)\
20533    + cos(- x - 2*y + z - pi) + cos(- x - 2*y - z - pi)\
20534    + cos( x + y + 2*z - pi) + cos(- x + y + 2*z - pi)\
20535    + cos( x - y - 2*z - pi) + cos(- x - y - 2*z - pi)\
20536    + cos(- 2*x + y + z) + cos( 2*x + y - z)\
20537    + cos(- 2*x - y + z) + cos( 2*x - y - z)\
20538    + cos(- x + 2*y + z) + cos( x - 2*y + z)\
20539    + cos(- x + 2*y - z) + cos( x - 2*y - z)\
20540    + cos( x - y + 2*z) + cos( x + y - 2*z)\
20541    + cos(- x - y + 2*z) + cos(- x + y - 2*z)\
20542    ) + 0.27*( \
20543    cos(- 2*x + 2*y - pi) + cos( 2*x - 2*y - pi)\
20544    + cos( 2*x + 2*y - pi) + cos(- 2*x - 2*y - pi)\
20545    + cos(- 2*y + 2*z - pi) + cos( 2*y - 2*z - pi)\
20546    + cos( 2*y + 2*z - pi) + cos(- 2*y - 2*z - pi)\
20547    + cos(- 2*z + 2*x - pi) + cos( 2*z - 2*x - pi)\
20548    + cos( 2*z + 2*x - pi) + cos(- 2*z - 2*x - pi)\
20549    ) - 0.69'",0,{-$2},{-$2},{-$2},$2,$2,$2,$1,$1,$1
20550  c3d. n3d. nm. [3D\ gyroid]
20551
20552#@cli histogram3d
20553#@cli : Get 3D color histogram of selected images.
20554#@cli : $ image.jpg resize2dx 64 histogram3d circles3d 3 opacity3d. 0.75 colorcube3d primitives3d[-1] 1 add3d
20555histogram3d :
20556  e[^-1] "Get 3D color histogram of image$?."
20557  to_rgb repeat $! l[$>]
20558    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]"
20559  endl done
20560
20561#@cli image6cube3d
20562#@cli : Generate 3D mapped cubes from 6-sets of selected images.
20563#@cli : $ image.jpg animate flower,"30,0","30,5",6 image6cube3d
20564image6cube3d :
20565  e[^-1] "Generate 3D mapped cubes from image$?."
20566  M={max(${-max_wh})} r $M,$M,1,3 imageplane3d n3d c3d
20567  repeat int($!/6) l[$>-{$>+5}]
20568    +3d[0] 0,0,-0.5
20569    r3d[1] 0,1,0,90 +3d[1] -0.5,0,0
20570    r3d[2] 0,1,0,180 +3d[2] 0,0,0.5
20571    r3d[3] 0,1,0,270 +3d[3] 0.5,0,0
20572    r3d[4] 1,0,0,90 +3d[4] 0,0.5,0
20573    r3d[5] 1,0,0,270 +3d[5] 0,-0.5,0
20574    +3d nm "[3D image cube]"
20575  endl done
20576
20577#@cli imageblocks3d : _maximum_elevation,_smoothness[%]>=0
20578#@cli : Generate 3D blocks from selected images.
20579#@cli : Transparency of selected images is taken into account.
20580#@cli : Default values: 'maximum_elevation=10' and 'smoothness=0'.
20581#@cli : $ image.jpg resize2dy 32 imageblocks3d -20 mode3d 3
20582imageblocks3d : check ${2=0}>=0 skip ${1=10},${3=0}
20583  e[^-1] "Generate 3D blocks from image$?, with maximum elevation $1 and smoothness $2."
20584  repeat $! l[$>]
20585    w={w} h={h}
20586    split_opacity to_rgb[0] is_opacity={$!==2}
20587
20588    # Create 3D object template.
20589    l[] box3d 1,1,0
20590    repeat $w-1 ++3d. 1,0,0 done +3d
20591    repeat $h-1 ++3d. 0,1,0 done +3d
20592    endl
20593    s3d.
20594
20595    # Set vertex altitudes.
20596    +norm[0] b. $2
20597    y. n. 0,$1
20598    r[-5] 24,{-5,round(w*h/24)},1,1,-1
20599    if $1<0 j[-5] .,2 j[-5] .,5 j[-5] .,8 j[-5] .,11
20600    else j[-5] .,14 j[-5] .,17 j[-5] .,20 j[-5] .,23
20601    fi
20602    rm. y[-4]
20603
20604    # Set primitive colors.
20605    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
20606
20607    # Set primitive opacities.
20608    if $is_opacity rm. mv[0] $! /. 255 y. r. 6,100%,1,1 y. fi
20609
20610    a y
20611  endl done
20612
20613#@cli imagecube3d
20614#@cli : Generate 3D mapped cubes from selected images.
20615#@cli : $ image.jpg imagecube3d
20616imagecube3d :
20617  e[^-1] "Generate 3D mapped cubes from image$?."
20618  slices 50% to_rgb repeat $! l[$>] nm={0,n}
20619    i.. (67.5;73.5;109.5;103.5;51.5;100.5;\  # Magick number for CImg3d.
20620         8;6;\                               # Number of vertices and primitives.
20621         -0.5;-0.5;-0.5;\                    # Vertex coordinates.
20622         0.5;-0.5;-0.5;\
20623         0.5;0.5;-0.5;\
20624         -0.5;0.5;-0.5;\
20625         -0.5;-0.5;0.5;\
20626         0.5;-0.5;0.5;\
20627         0.5;0.5;0.5;\
20628         -0.5;0.5;0.5;\
20629         12;0;3;2;1;0;0;0;{h};{w};{h};{w};0;\    # Primitives description.
20630         12;1;2;6;5;0;0;0;{h};{w};{h};{w};0;\
20631         12;5;6;7;4;0;0;0;{h};{w};{h};{w};0;\
20632         12;4;7;3;0;0;0;0;{h};{w};{h};{w};0;\
20633         12;4;0;1;5;0;0;0;{h};{w};{h};{w};0;\
20634         12;3;7;6;2;0;0;0;{h};{w};{h};{w};0;\
20635         -128;{w};{h};{s})       # Texture map for the first face.
20636    y.
20637    (-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.
20638    a y
20639  nm $nm endl done
20640
20641#@cli imageplane3d
20642#@cli : Generate 3D mapped planes from selected images.
20643#@cli : $ image.jpg imageplane3d
20644imageplane3d :
20645  e[^-1] "Generate 3D mapped planes from image$?."
20646  slices 50% to_color repeat $! l[$>] nm={0,n}
20647    i.. (67.5;73.5;109.5;103.5;51.5;100.5;\   # Magick number for CImg3d.
20648         4;1;\                                # Number of vertices and primitives.
20649         0;0;0;\                              # Vertex coordinates.
20650         {w};0;0;\
20651         {w};{h};0;\
20652         {0};{h};0;\
20653         12;0;3;2;1;0;0;0;{h};{w};{h};{w};0;\ # Primitives description.
20654         -128;{w};{h};{s})                    # Texture map.
20655    y.
20656    (1)  # Opacity.
20657    a y
20658  nm $nm endl done
20659
20660#@cli imagepyramid3d
20661#@cli : Generate 3D mapped pyramids from selected images.
20662#@cli : $ image.jpg imagepyramid3d
20663imagepyramid3d :
20664  e[^-1] "Generate 3D mapped pyramids from image$?."
20665  to_rgb repeat $! l[$>] nm={0,n}
20666    w2={w/2}
20667    i.. (67.5;73.5;109.5;103.5;51.5;100.5;\  # Magick number for CImg3d.
20668         5;5;\                               # Number of vertices and primitives.
20669         -0.5;-0.5;-0.5;\                    # Vertex coordinates.
20670         0.5;-0.5;-0.5;\
20671         0.5;0.5;-0.5;\
20672         -0.5;0.5;-0.5;\
20673         0;0;0.5;\
20674         12;0;3;2;1;0;0;0;{h};{w};{h};{w};0;\    # Primitives description.
20675         9;0;4;3;0;{h};$w2;0;{w};{h};\
20676         9;1;4;0;0;{h};$w2;0;{w};{h};\
20677         9;2;4;1;0;{h};$w2;0;{w};{h};\
20678         9;3;4;2;0;{h};$w2;0;{w};{h};\
20679         -128;{w};{h};{s})                   # Texture map for the first face.
20680    y.
20681    (-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.
20682    a y
20683  nm $nm endl done
20684
20685#@cli imagerubik3d : _xy_tiles>=1,0<=xy_shift<=100,0<=z_shift<=100
20686#@cli : Generate 3D mapped rubik's cubes from selected images.
20687#@cli : Default values: 'xy_tiles=3', 'xy_shift=5' and 'z_shift=5'.
20688#@cli : $ image.jpg imagerubik3d ,
20689imagerubik3d : check "${1=3}>=1 && ${2=5}>=0 && $2<=100 && ${3=5}>=0 && $3<=100"
20690  e[^-1] "Generate 3D mapped rubik\47s cubes from image$? with $1 xy-tiles, xy-shift $2 and z-shift $3."
20691  repeat $! l[$>] nm={0,n}
20692    # Generate primary 3D side.
20693    ('CImg3d') +. 0.5
20694    (8,5)
20695    (0,0,0;\
20696     100,0,0;\
20697     100,100,0;\
20698     0,100,0;\
20699     $2,$2,{-$3};\
20700     {100-$2},$2,{-$3};\
20701     {100-$2},{100-$2},{-$3};\
20702     $2,{100-$2},{-$3})
20703    (4,4,7,6,5;\
20704     4,0,4,5,1;\
20705     4,3,2,6,7;\
20706     4,0,3,7,4;\
20707     4,1,5,6,2)
20708    3,5,1,1,200
20709    1,5,1,1,1
20710    y[-6--1] a[-6--1] y
20711    repeat $1-1 ++3d. 100 done +3d[-$1--1]   # Duplicate along X
20712    repeat $1-1 ++3d. 0,100 done +3d[-$1--1] # Duplicate along Y
20713    t3d. .. rm..
20714    /3d. $1 -3d. 50,50,50
20715    +r3d. 0,1,0,-90 +r3d. 0,1,0,-90 +r3d. 0,1,0,-90  # Generate the 5 other sides.
20716    +r3d. 0,0,1,-90 +r3d. 0,0,1,180
20717    +3d
20718  nm $nm endl done
20719
20720#@cli imagesphere3d : _resolution1>=3,_resolution2>=3
20721#@cli : Generate 3D mapped sphere from selected images.
20722#@cli : Default values: 'resolution1=32' and 'resolutions2=16'.
20723#@cli : $ image.jpg imagesphere3d 32,16
20724imagesphere3d : check "${1=32}>=3 && ${2=16}>=3"
20725  e[^-1] "Generate 3D mapped sphere from image$?, with resolutions ($1,$2)."
20726  to_rgb repeat $! l[$>] nm={0,n}
20727
20728    # Generate object header.
20729    tw={w-1} th={h-1}                    # Maximum texture xy-coordinates.
20730    nbv={2+$1*($2-2)}                    # Number of vertices.
20731    nbp={$1*($2-1)}                      # Number of primitives.
20732    (67.5;73.5;109.5;103.5;51.5;100.5;\  # Magick number for CImg3d.
20733     $nbv;$nbp)                          # Number of vertices and primitives.
20734
20735    # Define sphere vertices.
20736    (0;0;1) (0;0;-1) (0,{2*pi};0,{2*pi}^0,0;{pi},{pi})
20737    r. {$1+1},$2,1,2,3 z. 0,1,{w-2},{h-2} s. c
20738    +sin. +sin... *[-2,-1] +cos.. sin... cos[-4] *[-4,-3]
20739    a[-3--1] c permute. cxyz y. a[-3--1] y
20740
20741    # Define sphere primitives (triangles and quadrangles).
20742    repeat $1,v
20743      tx0={$v*$tw/$1} tx1={($v+1)*$tw/$1} ty1={$th/($2-1)}
20744      (9;0;{2+$v};{2+($v+1)%$1};{$tw/2};0;$tx0;$ty1;$tx1;$ty1) # Textured triangle from 1st pole.
20745      repeat $2-3,u
20746        ty0=$ty1 ty1={($u+2)*$th/($2-1)} i0={2+$u*$1+$v} i1={2+$u*$1+($v+1)%$1}
20747        (12;$i0;{$i0+$1};{$i1+$1};$i1;$tx0;$ty0;$tx0;$ty1;$tx1;$ty1;$tx1;$ty0) # Textured quadrangle.
20748      done
20749      (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.
20750    done
20751    a[-$nbp--1] y
20752
20753    # Define sphere textures, opacities and generate object.
20754    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
20755  nm $nm endl done
20756
20757#@cli isoline3d : isovalue[%] : 'formula',value,_x0,_y0,_x1,_y1,_size_x>0[%],_size_y>0[%] : (+)
20758#@cli : Extract 3D isolines with specified value from selected images or from specified formula.
20759#@cli : Default values: 'x0=y0=-3', 'x1=y1=3' and 'size_x=size_y=256'.
20760#@cli : $ image.jpg blur 1 isoline3d 50%
20761#@cli : $ isoline3d 'X=x-w/2;Y=y-h/2;(X^2+Y^2)%20',10,-10,-10,10,10
20762
20763#@cli isosurface3d : isovalue[%] : 'formula',value,_x0,_y0,_z0,_x1,_y1,_z1,_size_x>0[%],_size_y>0[%],_size_z>0[%] : (+)
20764#@cli : Extract 3D isosurfaces with specified value from selected images or from specified formula.
20765#@cli : Default values: 'x0=y0=z0=-3', 'x1=y1=z1=3' and 'size_x=size_y=size_z=32'.
20766#@cli : $ image.jpg resize2dy 128 luminance threshold 50% expand_z 2,0 blur 1 isosurface3d 50% mul3d 1,1,30
20767#@cli : $ isosurface3d 'x^2+y^2+abs(z)^abs(4*cos(x*y*z*3))',3
20768
20769#@cli label3d : "text",font_height>=0,_opacity,_color1,...
20770#@cli : Generate 3D text label.
20771#@cli : Default values: 'font_height=13', 'opacity=1' and 'color=255,255,255'.
20772+label3d : check ${2=13}>=0 skip ${3=1},${4=255},${5=$4},${6=$5}
20773  e[^-1] "Generate 3D label '$1' with font height $2, opacity $3 and color (${4--1})."
20774  l[] 0 t "$1",0,0,$2,1,${4--1},255 sprite3d endl
20775
20776#@cli label_points3d : _label_size>0,_opacity
20777#@cli : Add a numbered label to all vertices of selected 3D objects.
20778#@cli : Default values: 'label_size=13' and 'opacity=0.8'.
20779#@cli : $ torus3d 100,40,6,6 label_points3d 23,1 mode3d 1
20780label_points3d : check ${1=13}>0 skip ${2=0.8}
20781  e[^-1] "Label vertices of 3D object$?."
20782  repeat $!
20783    +p3d[$>] 0 l. s3d rm[-3--1]
20784    nbp={-2,@0} =.. $nbp,0,1                               # Set correct number of primitives
20785    (1,0;1,{$nbp-1}) r. 2,$nbp,1,1,3 r. 1,{2*h},1,1,-1    # Create new primitive data
20786    repeat $nbp                                            # Create texture labels as primitive colors.
20787      0 t. $>,0,0,$1,1,255,255,255 autocrop. 0
20788      i.. (-128;{w};{h};3) y.
20789    done
20790    repeat $nbp                                            # Create texture masks as primitive opacities.
20791      0 t. $>,0,0,$1,1,$2 autocrop. 0
20792      i.. (-128;{w};{h};1) y.
20793    done
20794    a y  # Merge final object data.
20795    endl
20796    +3d[$>,-1]
20797  done
20798
20799#@cli lathe3d : _resolution>0,_smoothness[%]>=0,_max_angle>=0
20800#@cli : Generate 3D object from selected binary XY-profiles.
20801#@cli : Default values: 'resolution=128', 'smoothness=0.5%' and 'max_angle=361'.
20802#@cli : $ 300,300 rand -1,1 blur 40 sign normalize 0,255 lathe3d ,
20803lathe3d : check "${1=128}>0 && ${2=0.5%}>=0 && ${3=361}>=0"
20804  e[^-1] "Generate lathed 3D object from XY-profile$?, with resolution $1, smoothness $2 and maximum angle $3 deg."
20805  tmax={($3-180)*pi/180} norm n 0,1 autocrop 0
20806  repeat $! l[$>]
20807    wr={max(1,w2=2*w;if(w2>h,min($1,w2),min($1,h)*w2/h))}
20808    hr={max(1,w2=2*w;if(w2>h,min($1,w2)*h/w2,min($1,h)))}
20809    rmax={sqrt(($wr)^2+($hr)^2)/2}
20810    $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))"
20811    *. {2*({-2,w}-1)/(w-1)} r. $wr,$hr,$wr
20812    (0;{{-2,h}-1}) r. $wr,$hr,$wr,1,3 a[-2--1] c
20813    warp.. .,0,1,0 rm.
20814    expand_xyz 10,0 b $2 isosurface3d 50% rv3d
20815  endl done
20816
20817#@cli l3d : eq. to 'light3d'. : (+)
20818
20819#@cli light3d : position_x,position_y,position_z : [texture] : (no arg) : (+)
20820#@cli : Set the light coordinates or the light texture for 3D rendering.
20821#@cli : (eq. to 'l3d').\n
20822#@cli : (no arg) resets the 3D light to default.
20823#@cli : $ torus3d 100,30 double3d 0 specs3d 1.2 repeat 5 light3d {$>*100},0,-300 +snapshot3d[0] 400 done remove[0]
20824
20825#@cli line3d : x0,y0,z0,x1,y1,z1
20826#@cli : Input 3D line at specified coordinates.
20827#@cli : $ repeat 100 a={$>*pi/50} line3d 0,0,0,{cos(3*$a)},{sin(2*$a)},0 color3d. ${-rgb} done add3d
20828+line3d :
20829  e[^-1] "Input 3D line (${1-3})-(${4-6})."
20830  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]
20831
20832#@cli lissajous3d : resolution>1,a,A,b,B,c,C
20833#@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))__.
20834#@cli : Default values: 'resolution=1024', 'a=2', 'A=0', 'b=1', 'B=0', 'c=0' and 'C=0'.
20835#@cli : $ lissajous3d ,
20836+lissajous3d : check ${1=1024}>1 skip ${2=2},${3=0},${4=1},${5=0},${6=0},${7=0}
20837  e[^-1] "Input 3D lissajous curve, with resolution $1, (a,A)=($2,$3), (b,B)=($4,$5) and (c,C)=($6,$7)."
20838  res={round($1)}
20839
20840  # Define object header and vertices.
20841  (67.5;73.5;109.5;103.5;51.5;100.5;$res;{$res-1})
20842  (0,{2*pi}) r. $res,1,1,1,3 [-1]x2
20843  *... $2 +... {$3*2*pi} *.. $4 +.. {$5*2*pi} *. $6 +. {$7*2*pi}
20844  a[-3--1] y sin. transpose. r. 1,{w*h},1,1,-1
20845
20846  # Define object primitives, colors and opacities.
20847  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
20848  1,{3*($res-1)},1,1,200 1,{$res-1},1,1,1 a[-5--1] y nm. [3D\ lissajou]
20849
20850#@cli m3d : eq. to 'mode3d'. : (+)
20851
20852#@cli mode3d : _mode : (+)
20853#@cli : Set static 3D rendering mode.
20854#@cli : (eq. to 'm3d').\n
20855#@cli : 'mode' can be { -1=bounding-box | 0=dots | 1=wireframe | 2=flat | 3=flat-shaded | 4=gouraud-shaded | \
20856# 5=phong-shaded }.");
20857#@cli : Bounding-box mode ('mode==-1') is active only for the interactive 3D viewer.
20858#@cli : Default value: 'mode=4'.
20859#@cli : $ (0,1,2,3,4,5) double3d 0 repeat w torus3d 100,30 rotate3d[-1] 1,1,0,60 mode3d {0,@$>} \
20860# snapshot3d[-1] 300 done remove[0]
20861
20862#@cli md3d : eq. to 'moded3d'. : (+)
20863
20864#@cli moded3d : _mode : (+)
20865#@cli : Set dynamic 3D rendering mode for interactive 3D viewer.
20866#@cli : (eq. to 'md3d').\n
20867#@cli : 'mode' can be { -1=bounding-box | 0=dots | 1=wireframe | 2=flat | 3=flat-shaded | 4=gouraud-shaded | \
20868# 5=phong-shaded }.
20869#@cli : Default value: 'mode=-1'.
20870
20871#@cli *3d : eq. to 'mul3d'. : (+)
20872
20873#@cli mul3d : factor : factor_x,factor_y,_factor_z : (+)
20874#@cli : Scale selected 3D objects isotropically or anisotropically, with specified factors.
20875#@cli : (eq. to '*3d').
20876#@cli : Default value: 'factor_z=0'.
20877#@cli : $ torus3d 5,2 repeat 5 +add3d[-1] 10,0,0 mul3d[-1] 1.2 color3d[-1] ${-rgb} done add3d
20878
20879#@cli n3d : eq. to 'normalize3d'.
20880n3d :
20881  _normalize3d
20882
20883#@cli normalize3d
20884#@cli : Normalize selected 3D objects to unit size.
20885#@cli : (eq. to 'n3d').
20886#@cli : $ repeat 100 circle3d {u(3)},{u(3)},{u(3)},0.1 done add3d color3d[-1] 255,0,0 +normalize3d[-1] \
20887# color3d[-1] 0,255,0 add3d
20888normalize3d :
20889  _$0
20890
20891_normalize3d :
20892  e[0--3] "Normalize size of 3D object$?."
20893  check3d 0 repeat $! l[$>]
20894    if i[6]
20895      s3d r[2] 3,{2,h/3},1,1,-1 s[2] x
20896      factor={v=max({2,iM-im},{3,iM-im},{4,iM-im});if(v,v,1)}
20897      a[2-4] x /[2] $factor y[2] a y
20898    fi
20899  endl done
20900
20901#@cli o3d : eq. to 'opacity3d'. : (+)
20902
20903#@cli opacity3d : _opacity : (+)
20904#@cli : Set opacity of selected 3D objects.
20905#@cli : (eq. to 'o3d').
20906#@cli : Default value: 'opacity=1'.
20907#@cli : $ torus3d 100,10 double3d 0 repeat 7 +rotate3d[-1] 1,0,0,20 opacity3d[-1] {u} done add3d
20908
20909#@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,\
20910# _smoothness>=0,_isovalue>=0
20911#@cli : Input 3D object from specified parametric surface `(a,b) ⟶ (x(a,b),y(a,b),z(a,b))`.
20912#@cli : Default values: 'x=(2+cos(b))*sin(a)', 'y=(2+cos(b))*cos(a)', 'c=sin(b)', 'amin=-pi', 'amax=pi', \
20913# 'bmin=-pi', 'bmax=pi', \
20914# 'res_a=512', 'res_b=res_a', 'res_x=64', 'res_y=res_x', 'res_z=res_y', 'smoothness=2%' and 'isovalue=10%'.
20915#@cli : $ parametric3d ,
20916+parametric3d : skip "${1=(2+cos(b))*sin(a)}","${2=(2+cos(b))*cos(a)}","${3=sin(b)}"
20917                skip ${4={-pi}},${5={pi}},${6={-pi}},${7={pi}}
20918                check "${8=512}>0 && ${9=$8}>0 && ${10=64}>0 && ${11=$10}>0 && ${12=$11}>0 && \
20919                        ${13=2%}>=0 && ${14=10%}>=0"
20920  e[^-1] "Input 3D object from parametric surface ($1,$2,$3)."
20921  # Compute (x(a,b),y(a,b),z(a,b)) and normalize it.
20922  ($4,$5;$4,$5^$6,$6;$7,$7) r. $8,$9,1,2,3 channels. 0,2
20923  f. "a=i(x,y,0,0);b=i(x,y,0,1);if(c==0,$1,if(c==1,$2,$3))"
20924  sh. 0 xmin={im} xmax={iM} n. 16,{$10-17} rm.
20925  sh. 1 ymin={im} ymax={iM} n. 16,{$11-17} rm.
20926  sh. 2 zmin={im} zmax={iM} n. 16,{$12-17} rm.
20927  r. {w*h},3,1,1,-1
20928
20929  # Extract 3D surface.
20930  pointcloud. 1 r. $10,$11,$12,1,0 b. $13,0
20931  isosurface3d. $14
20932  c3d. n3d. *3d. {$xmax-$xmin},{$ymax-$ymin},{$zmax-$zmin} nm. [3D\ parametric]
20933
20934#@cli pca_patch3d : _patch_size>0,_M>0,_N>0,_normalize_input={ 0 | 1 },_normalize_output={ 0 | 1 },_lambda_xy
20935#@cli : Get 3D patch-pca representation of selected images.
20936#@cli : The 3D patch-pca is estimated from M patches on the input image, and displayed as a cloud of N 3D points.
20937#@cli : Default values: 'patch_size=7', 'M=1000', 'N=3000', 'normalize_input=1', 'normalize_output=0', \
20938# and 'lambda_xy=0'.
20939#@cli : $ image.jpg pca_patch3d 7
20940pca_patch3d : check "isint(${1=7}) && $1>0 && isint(${2=1000}) && $2>0 && isint(${3=3000}) && $3>0"
20941              skip ${4=1},${5=0},${6=0}
20942  e[^-1] "Get 3D patch-pca representation"${arg\ 1+($!>1),s,""}" of image$?, from $2 $1x$1 input patches,
20943          with $3 output patches, input normalization "${arg\ 1+!$4,enabled,disabled}", output normalization "\
20944          ${arg\ 1+!$5,enabled,disabled}" and lambda_xy $6."
20945  P1={int($1/2)}       # Backward half-patch size.
20946  P2={$1-$P1-1}        # Forward half-patch size.
20947
20948  n 0,255 round 1
20949  repeat $! l[$>] nm={0,n}
20950    s={s}
20951
20952    # Pick set of M random located patches.
20953    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
20954    z[1] 0,1 transpose[1] *[1] $6 a[1,2] y s[^0] x
20955
20956    # Normalize patch coordinates by using average and standard deviation.
20957    ++[^0] /. $2 -[1--2] . rm.
20958    a[^0] x
20959    if $4 l. s y / 'sqrt(1e-8+iv)' a y endl fi
20960
20961    # Do PCA for dimension reduction.
20962    +transpose. m*[-2,-1]
20963    eigen. rows.. 0,2 columns. 0,2 transpose.
20964    if $5 sqrt.. /.. {-2,iM} ri.. . /. .. fi
20965    rm..
20966
20967    # Pick set of N random located patches.
20968    repeat $3
20969      x={round(u({0,w}))}
20970      y={round(u({0,h}))}
20971      ({$6*$x};{$6*$y})
20972      +z[0] {$x-$P1},{$y-$P1},{$x+$P2},{$y+$P2},1
20973      y. a[-2,-1] y
20974    done
20975
20976    # Generate 3D representation of the projected patch set.
20977    +a[2--1] x m*[1,-1] transpose[1]                    # Vertex coordinates.
20978    rows[2--1] 2,100%                                   # Colors
20979    if $s!=3
20980      r[2--1] $1,$1,1,{min(3,$s)},-1
20981      r[2--1] $1,$1,1,3,{if($s!=1,0,1)}
20982      y[2--1]
20983    fi
20984    i[2--2] (-128;$1;$1;3) a[2--1] y
20985    rm[0]                                               # Remove input image (now useless).
20986    i[0] ('CImg3d')                                     # Header.
20987    i[1] ($3;$3)                                        # Geometry.
20988    i[3] 2,$3,1,1,if(x==0,1,y)                          # Primitives.
20989    1,$3,1,1,1                                          # Opacities.
20990    y a[-6--1] y                                        # Merge as a 3D object.
20991
20992  nm $nm endl done
20993
20994#@cli plane3d : _size_x,_size_y,_nb_subdivisions_x>0,_nb_subdisivions_y>0
20995#@cli : Input 3D plane at (0,0,0), with specified geometry.
20996#@cli : Default values: 'size_x=1', 'size_y=size_x' and 'nb_subdivisions_x=nb_subdivisions_y=24'.
20997#@cli : $ plane3d 50,30 +primitives3d 1 color3d[-2] ${-rgb}
20998+plane3d : check "${3=24}>0 && ${4=24}>0" skip ${1=1},${2=$1}
20999  e[^-1] "Input 3D plane, with size (${1,2}) and subdivisions (${3,4})."
21000  {$3+1},{$4+1} elevation3d. 0 *3d. {$1/$3},{$2/$4} col3d. 200 nm. [3D\ plane]
21001
21002#@cli point3d : x0,y0,z0
21003#@cli : Input 3D point at specified coordinates.
21004#@cli : $ repeat 1000 a={$>*pi/500} point3d {cos(3*$a)},{sin(2*$a)},0 color3d[-1] ${-rgb} done add3d
21005+point3d :
21006  e[^-1] "Input 3D point ($1,$2,$3)."
21007  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]
21008
21009#@cli pointcloud3d
21010#@cli : Convert selected planar or volumetric images to 3D point clouds.
21011#@cli : $ image.jpg luminance resize2dy 100 threshold 50% mul 255 pointcloud3d color3d[-1] 255,255,255
21012pointcloud3d :
21013  e[^-1] "Convert image$? to 3D point cloud."
21014  repeat $! l[$>] nm={0,n}
21015    s z repeat $! l[$>]
21016      +norm !=. 0
21017      i.. (1,{w};1,{w}^1,1;{h},{h}) r.. .,.,1,2,3 *[-2,-1] round. permute. cxyz
21018      l. s -,0 a y is_points=$! endl
21019      if $is_points
21020        -. 1 r. 2,{h/2},1,1,-1 permute. cyzx +warp.. .,0,0 rm...
21021        permute.. cyzx i.. 1,{h},1,1,$> a[-3,-2] x   # Coordinates.
21022        i... ('CImg3d') i... ({h},{h})               # Header and size.
21023        i.. 1,{h},1,1,1 i.. 1,{h},1,1,y a[-3,-2] x   # Primitives.
21024        permute. cyzx                                # Colors.
21025        if w==1 r. 3,{h},1,1
21026        elif w>3 i.. 4,{h},1,1,-128,1,1,{w} a[-2,-1] x
21027        else r. 3,{h},1,1,0
21028        fi
21029        1,{h},1,1,1                                      # Opacities.
21030        y[-6--1] a[-6--1] y
21031      else rm empty3d
21032      fi
21033      endl done
21034    +3d
21035  nm $nm endl done
21036
21037#@cli pose3d : p1,...,p12
21038#@cli : Apply 3D pose matrix to selected 3D objects.
21039#@cli : $ torus3d 100,20 pose3d 0.152437,1.20666,-0.546366,0,-0.535962,0.559129,1.08531,0,1.21132,0.0955431,\
21040# 0.548966,0,0,0,-206,1 snapshot3d 400
21041pose3d :
21042  e[^-1] "Apply 3D pose matrix [ $1,$2,$3,$4; $5,$6,$7,$8; $9,$10,$11,$12 ] to 3D object$?."
21043  repeat $! l[$>] if ${-_is_3d}
21044    s3d r[2] 3,{2,h/3},1,1,-1 i[3] 1,{2,h},1,1,1 a[2,3] x
21045    i[3] ($1,$5,$9;$2,$6,$10;$3,$7,$11;$4,$8,$12) m*[2,3]
21046    r[2] 1,{2,3*h},1,1,-1 a y
21047  else error "Command '$0': Image ["{$!-$>-1}"] does not represent a 3D object."
21048  fi endl done
21049
21050#@cli p3d : eq. to 'primitives3d'.
21051p3d : check "isint($1) && $1>=0 && $1<=2"
21052  _primitives3d $*
21053
21054#@cli primitives3d : mode
21055#@cli : Convert primitives of selected 3D objects.
21056#@cli : (eq. to 'p3d').\n
21057#@cli : 'mode' can be { 0=points | 1=outlines | 2=non-textured }.
21058#@cli : $ sphere3d 30 primitives3d 1 torus3d 50,10 color3d[-1] ${-rgb} add3d
21059primitives3d : check "isint($1) && $1>=0 && $1<=2"
21060  _$0 $*
21061
21062_primitives3d :
21063  e[0--3] "Convert primitives of 3D object$? to "${"arg 1+$1,points,outlines,non-textured"}"."
21064  repeat $! l[$>]
21065    s3d 1,64 .x2
21066    eval "
21067      const mode = $1;
21068
21069      # Read a RGBA color from the current primitive material.
21070      get_RGBA(tx,ty,goto_next) = (
21071        R = i[#4,pc];
21072        R!=-128?( # RGB color
21073          G = i[#4,pc+1];
21074          B = i[#4,pc+2];
21075          goto_next?(pc+=3);
21076        ):( # Texture
21077          W = i[#4,pc+1]; H = i[#4,pc+2]; S = i[#4,pc+3]; WH = W*H;
21078          WH?( # Non-shared texture
21079            off = pc + (ty%H)*W + (tx%W) + 4;
21080            goto_next?(pc+=WH*S + 4);
21081          ):( # Shared texture
21082            off = pcol[W];
21083            i[#4,off]==-128?(
21084              ++off;
21085              W = i[#4,off++]; H = i[#4,off++]; S = i[#4,off++]; WH = W*H;
21086              off += (ty%H)*W + (tx%W);
21087            ):(WH=1);
21088            goto_next?(pc+=4);
21089          );
21090          R = i[#4,off];
21091          S==1?(G = B = R):(
21092            G = i[#4,off + WH];
21093            S==2?(B = 0):(
21094              B = i[#4,off + 2*WH];
21095            );
21096          );
21097        );
21098
21099        A = i[#5,po];
21100        A!=-128?( # Scalar
21101          goto_next?(po+=1);
21102        ):( # Texture
21103          W = i[#5,po+1]; H = i[#5,po+2]; S = i[#5,po+3]; WH = W*H;
21104          WH?( # Non-shared texture
21105            off = po + (ty%H)*W + (tx%W) + 4;
21106            goto_next?(po+=WH*S + 4);
21107          ):( # Shared texture
21108            off = popa[W];
21109            i[#5,off]==-128?(
21110              ++off;
21111              W = i[#5,off++]; H = i[#5,off++]; S = i[#5,off++]; WH = W*H;
21112              off += (ty%H)*W + (tx%W);
21113            );
21114            goto_next?(po+=4);
21115          );
21116          A = i[#5,off];
21117        );
21118      );
21119
21120      # Copy material of current primitives 'nb' times.
21121      copy_material(nb) = (
21122        R = i[#4,pc++]; G = i[#4,pc++]; B = i[#4,pc++];
21123        R!=-128?( # RGB color
21124          unref(data); data = [ R,G,B ];
21125          repeat (nb, copy(i[#-2,qc],data,3); qc+=3);
21126        ):(
21127          W = G; H = B; S = i[#4,pc++]; WHS = W*H*S;
21128          WHS?( # Non-shared texture
21129            qc + WHS + 4*nb>=h(#-2)?resize(#-2,1,int(1.5*qc + WHS + 4*nb),1,1,0,0);
21130            copy(i[#-2,qc],i[#4,pc-4],WHS+4);
21131            pc+=WHS; qc+=WHS+4;
21132            unref(data); data = [-128,nq,0,0];
21133            for (k = 1, k<nb, ++k, copy(i[#-2,qc],data,4); qc+=4);
21134          ):( # Shared texture
21135            unref(data); data = [-128,pref[W],0,0];
21136            repeat (nb, copy(i[#-2,qc],data,4); qc+=4);
21137          );
21138        );
21139
21140        A = i[#5,po++];
21141        A!=-128?( # Opacity value
21142          repeat (nb, i[#-1,qo++] = A);
21143        ):( # Texture
21144          W = i[#5,po++]; H = i[#5,po++]; S = i[#5,po++]; WHS = W*H*S;
21145          WHS?( # Non-shared texture
21146            qo + WHS + 4*nb>=h(#-1)?resize(#-1,1,int(1.5*qo + WHS + 4*nb),1,1,0,0);
21147            copy(i[#-1,qo],i[#5,po-4],WHS+4);
21148            po+=WHS; qo+=WHS+4;
21149            unref(data); data = [-128,nq,0,0];
21150            for (k = 1, k<nb, ++k, copy(i[#-1,qo],data,4); qo+=4);
21151          ):( # Shared texture
21152            unref(data); data = [ -128,pref[W],0,0 ];
21153            repeat (nb, copy(i[#-1,qo],data,4); qo+=4);
21154          );
21155        );
21156      );
21157
21158      # Add a new colored point primitive.
21159      add_point(ind,R,G,B,A) = (
21160        copy(i[#-3,qp],[1,ind],2); qp+=2;
21161        copy(i[#-2,qc],[R,G,B],3); qc+=3;
21162        i[#-1,qo++] = A;
21163        ++nq;
21164      );
21165
21166      # Add a new colored segment primitive.
21167      add_segment(ind0,ind1,R,G,B,A) = (
21168        copy(i[#-3,qp],[2,ind0,ind1],3); qp+=3;
21169        copy(i[#-2,qc],[R,G,B],3); qc+=3;
21170        i[#-1,qo++] = A;
21171        ++nq;
21172      );
21173
21174      pcol = popa = pref = vector"{i[#1,1]}"();
21175      for (pp = pc = po = qp = qc = qo = np = nq = 0, pp<h#3, ++np,
21176        qp + 28>=h(#-3)?resize(#-3,1,int(1.5*qp + 28),1,1,0,0);
21177        qc + 16>=h(#-2)?resize(#-2,1,int(1.5*qc + 16),1,1,0,0);
21178        qo + 4>=h(#-1)?resize(#-1,1,int(1.5*qo + 4),1,1,0,0);
21179        N = i[#3,pp++];
21180        pcol[np] = pc;
21181        popa[np] = po;
21182        pref[np] = nq;
21183
21184        N==1?( # Colored point
21185          v0 = i[#3,pp++];
21186          get_RGBA(0,0,1);
21187          add_point(v0,R,G,B,A);
21188
21189        ):N==2?( # Colored segment
21190          v0 = i[#3,pp++]; v1 = i[#3,pp++];
21191          get_RGBA(0,0,1);
21192          mode==0?(
21193            add_point(v0,R,G,B,A);
21194            add_point(v1,R,G,B,A);
21195          ):(
21196            add_segment(v0,v1,R,G,B,A);
21197          );
21198
21199        ):N==3?( # Colored triangle
21200          v0 = i[#3,pp++]; v1 = i[#3,pp++]; v2 = i[#3,pp++];
21201          get_RGBA(0,0,1);
21202          mode==0?(
21203            add_point(v0,R,G,B,A);
21204            add_point(v1,R,G,B,A);
21205            add_point(v2,R,G,B,A);
21206          ):mode==1?(
21207            add_segment(v0,v1,R,G,B,A);
21208            add_segment(v1,v2,R,G,B,A);
21209            add_segment(v2,v0,R,G,B,A);
21210          ):(
21211            copy(i[#-3,qp],i[#3,pp-4],4); qp+=4;
21212            copy(i[#-2,qc],i[#4,pc-3],3); qc+=3;
21213            i[#-1,qo++] = A;
21214            ++nq;
21215          );
21216
21217        ):N==4?( # Colored quadrangle
21218          v0 = i[#3,pp++]; v1 = i[#3,pp++]; v2 = i[#3,pp++]; v3 = i[#3,pp++];
21219          get_RGBA(0,0,1);
21220          mode==0?(
21221            add_point(v0,R,G,B,A);
21222            add_point(v1,R,G,B,A);
21223            add_point(v2,R,G,B,A);
21224            add_point(v3,R,G,B,A);
21225          ):mode==1?(
21226            add_segment(v0,v1,R,G,B,A);
21227            add_segment(v1,v2,R,G,B,A);
21228            add_segment(v2,v3,R,G,B,A);
21229            add_segment(v3,v0,R,G,B,A);
21230          ):(
21231            copy(i[#-3,qp],i[#3,pp-5],5); qp+=5;
21232            copy(i[#-2,qc],i[#4,pc-3],3); qc+=3;
21233            i[#-1,qo++] = A;
21234            ++nq;
21235          );
21236
21237        ):N==5?( # Colored sphere
21238          v0 = i[#3,pp++]; v1 = i[#3,pp++]; i[#3,pp++] = (mode==1); v2 = i[#3,pp++]; v3 = i[#3,pp++];
21239          get_RGBA(0,0,1);
21240          mode==0?(
21241            x0 = i[#2,3*v0]; y0 = i[#2,3*v0+1]; z0 = i[#2,3*v0+2];
21242            x1 = i[#2,3*v1]; y1 = i[#2,3*v1+1]; z1 = i[#2,3*v1+2];
21243            copy(i[#2,3*v0],([ x0,y0,z0 ] + [ x1,y1,z1 ])/2,3);
21244            add_point(v0,R,G,B,A);
21245          ):(
21246            copy(i[#-3,qp],i[#3,pp-6],6); qp+=6;
21247            copy(i[#-2,qc],i[#4,pc-3],3); qc+=3;
21248            i[#-1,qo++] = A;
21249            ++nq;
21250          );
21251
21252        ):N==6?( # Textured segment
21253          v0 = i[#3,pp++]; v1 = i[#3,pp++];
21254          tx0 = i[#3,pp++]; ty0 = i[#3,pp++]; tx1 = i[#3,pp++]; ty1 = i[#3,pp++];
21255          mode==0?(
21256            get_RGBA(tx0,ty0,0); add_point(v0,R,G,B,A);
21257            get_RGBA(tx1,ty1,1); add_point(v1,R,G,B,A);
21258          ):mode==1?(
21259            copy(i[#-3,qp],i[#3,pp-7],7); qp+=7;
21260            copy_material(1);
21261            ++nq;
21262          ):(
21263            get_RGBA(tx0,ty0,0); R0 = R; G0 = G; B0 = B; A0 = A;
21264            get_RGBA(tx1,ty1,1); (R0+=R)/=2; (G0+=G)/=2; (B0+=B)/=2; (A0+=A)/=2;
21265            add_segment(v0,v1,R0,G0,B0,A0);
21266          );
21267
21268        ):N==9?( # Textured triangle
21269          v0 = i[#3,pp++]; v1 = i[#3,pp++]; v2 = i[#3,pp++];
21270          tx0 = i[#3,pp++]; ty0 = i[#3,pp++];
21271          tx1 = i[#3,pp++]; ty1 = i[#3,pp++];
21272          tx2 = i[#3,pp++]; ty2 = i[#3,pp++];
21273          mode==0?(
21274            get_RGBA(tx0,ty0,0); add_point(v0,R,G,B,A);
21275            get_RGBA(tx1,ty1,0); add_point(v1,R,G,B,A);
21276            get_RGBA(tx2,ty2,1); add_point(v2,R,G,B,A);
21277          ):mode==1?(
21278            copy(i[#-3,qp],[ 6,v0,v1,tx0,ty0,tx1,ty1,
21279                             6,v1,v2,tx1,ty1,tx2,ty2,
21280                             6,v2,v0,tx2,ty2,tx0,ty0 ],21); qp+=21;
21281            copy_material(3);
21282            nq+=3;
21283          ):(
21284            get_RGBA(tx0,ty0,0); R0 = R; G0 = G; B0 = B; A0 = A;
21285            get_RGBA(tx1,ty1,0); R0+=R; G0+=G; B0+=B; A0+=A;
21286            get_RGBA(tx2,ty2,2); (R0+=R)/=3; (G0+=G)/=3; (B0+=B)/=3; (A0+=A)/=3;
21287            copy(i[#-3,qp],[ 3,v0,v1,v2 ],4); qp+=4;
21288            copy(i[#-2,qc],[ R,G,B ],3); qc+=3;
21289            i[#-1,qo++] = A;
21290            ++nq;
21291          );
21292
21293        ):N==12?( # Textured quadrangle
21294          v0 = i[#3,pp++]; v1 = i[#3,pp++]; v2 = i[#3,pp++]; v3 = i[#3,pp++];
21295          tx0 = i[#3,pp++]; ty0 = i[#3,pp++];
21296          tx1 = i[#3,pp++]; ty1 = i[#3,pp++];
21297          tx2 = i[#3,pp++]; ty2 = i[#3,pp++];
21298          tx3 = i[#3,pp++]; ty3 = i[#3,pp++];
21299          mode==0?(
21300            get_RGBA(tx0,ty0,0); add_point(v0,R,G,B,A);
21301            get_RGBA(tx1,ty1,0); add_point(v1,R,G,B,A);
21302            get_RGBA(tx2,ty2,0); add_point(v2,R,G,B,A);
21303            get_RGBA(tx3,ty3,1); add_point(v3,R,G,B,A);
21304          ):mode==1?(
21305            copy(i[#-3,qp],[ 6,v0,v1,tx0,ty0,tx1,ty1,
21306                             6,v1,v2,tx1,ty1,tx2,ty2,
21307                             6,v2,v3,tx2,ty2,tx3,ty3,
21308                             6,v3,v0,tx3,ty3,tx0,ty0 ],28); qp+=28;
21309            copy_material(4);
21310            nq+=4;
21311          ):(
21312            get_RGBA(tx0,ty0,0); R0 = R; G0 = G; B0 = B; A0 = A;
21313            get_RGBA(tx1,ty1,0); R0+=R; G0+=G; B0+=B; A0+=A;
21314            get_RGBA(tx2,ty2,0); R0+=R; G0+=G; B0+=B; A0+=A;
21315            get_RGBA(tx3,ty3,2); (R0+=R)/=4; (G0+=G)/=4; (B0+=B)/=4; (A0+=A)/=4;
21316            copy(i[#-3,qp],[ 4,v0,v1,v2,v3 ],5); qp+=5;
21317            copy(i[#-2,qc],[ R,G,B ],3); qc+=3;
21318            i[#-1,qo++] = A;
21319            ++nq;
21320          );
21321        );
21322      );
21323      resize(#-3,1,qp,1,1,0,0);
21324      resize(#-2,1,qc,1,1,0,0);
21325      resize(#-1,1,qo,1,1,0,0);
21326      i[#1,1] = nq"
21327
21328    rm[3-5] a y
21329  endl done
21330
21331#@cli projections3d : _x[%],_y[%],_z[%],_is_bounding_box={ 0 | 1 }
21332#@cli : Generate 3D xy,xz,yz projection planes from specified volumetric images.
21333projections3d : skip ${1=50%},${2=50%},${3=50%},${4=1}
21334  e[^-1] "Generate 3D xy,xz,yz projection planes from image$?."
21335  n 0,255 repeat $! l[$>]
21336    w={w} h={h} d={d}
21337    x={if(${is_percent\ $1},$1*w,$1)}
21338    y={if(${is_percent\ $2},$2*h,$2)}
21339    z={if(${is_percent\ $3},$3*d,$3)}
21340    +rows $2,$2 r. {w},{d},1,100%,-1
21341    +columns.. $1,$1 permute. zyxc
21342    slices... $3,$3 r[-3--1] 100%,100%,1,3
21343    imageplane3d[-3--1]
21344    r3d. 0,1,0,-90 r3d.. 1,0,0,90
21345    +3d... 0,0,$z +3d.. 0,$y,0 +3d. $x,0,0
21346    +3d[-3--1] o3d. 0.8
21347    if $4 box3d $w,$h,$d p3d. 1 o3d. 0.4 +3d[-2,-1] fi
21348  endl done
21349
21350#@cli pyramid3d : width,height
21351#@cli : Input 3D pyramid at (0,0,0), with specified geometry.
21352#@cli : $ pyramid3d 100,-100 +primitives3d 1 color3d[-2] ${-rgb}
21353+pyramid3d :
21354  e[^-1] "Input new 3D pyramid, with width $1 and height $2."
21355  (67.5;73.5;109.5;103.5;51.5;100.5;\  # Magick number for CImg3d.
21356   5;5;\                               # Number of vertices and primitives.
21357   {-$1/2};{-$1/2};{-$2/2};\           # Vertex coordinates.
21358   {$1/2};{-$1/2};{-$2/2};\
21359   {$1/2};{$1/2};{-$2/2};\
21360   {-$1/2};{$1/2};{-$2/2};\
21361   0;0;{$2/2};\
21362   4;0;3;2;1;\                         # Primitives description.
21363   3;0;4;3;\
21364   3;1;4;0;\
21365   3;2;4;1;\
21366   3;3;4;2)
21367  1,15,1,1,200 1,5,1,1,1 a[-3--1] y nm. [3D\ pyramid]
21368
21369#@cli quadrangle3d : x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3
21370#@cli : Input 3D quadrangle at specified coordinates.
21371#@cli : $ quadrangle3d -10,-10,10,10,-10,10,10,10,10,-10,10,10 repeat 10 +rotate3d[-1] 0,1,0,30 \
21372# color3d[-1] ${-rgb},0.6 done add3d mode3d 2
21373+quadrangle3d :
21374  e[^-1] "Input 3D quadrangle ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)-($10,$11,$12)."
21375  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]
21376
21377#@cli random3d : nb_points>=0
21378#@cli : Input random 3D point cloud in [0,1]^3.
21379#@cli : $ random3d 100 circles3d 0.1 opacity3d 0.5
21380+random3d : check "$1>=0"
21381  e[^-1] "Input random 3D point cloud, with $1 points."
21382  if $1<0.5 empty3d
21383  else l[]
21384    N={round($1)}
21385    ({'CImg3d'},$N,$N)
21386    3,$N rand. 0,1
21387    1,$N,1,1,1 1,$N,1,1,y a[-2,-1] x
21388    3,$N,1,1,200 1,$N,1,1,1
21389    y a y
21390  endl fi
21391  nm. [3D\ random\ pointcloud]
21392
21393#@cli rv3d : eq. to 'reverse3d'. : (+)
21394
21395#@cli reverse3d : (+)
21396#@cli : Reverse primitive orientations of selected 3D objects.
21397#@cli : (eq. to 'rv3d').
21398#@cli : $ torus3d 100,40 double3d 0 +reverse3d
21399
21400#@cli r3d : eq. to 'rotate3d'. : (+)
21401
21402#@cli rotate3d : u,v,w,angle : (+)
21403#@cli : Rotate selected 3D objects around specified axis with specified angle (in deg.).
21404#@cli : (eq. to 'r3d').
21405#@cli : $ torus3d 100,10 double3d 0 repeat 7 +rotate3d[-1] 1,0,0,20 done add3d
21406
21407#@cli rotation3d : u,v,w,angle
21408#@cli : Input 3x3 rotation matrix with specified axis and angle (in deg).
21409#@cli : $ rotation3d 1,0,0,0 rotation3d 1,0,0,90 rotation3d 1,0,0,180
21410rotation3d :
21411  e[^-1] "Input 3D rotation matrix around axis ($1,$2,$3) with angle $4 deg."
21412  3,3,1,1,{"rot(${1-4}°)"} nm. [3D\ rotation]
21413
21414#@cli sierpinski3d : _recursion_level>=0,_width,_height
21415#@cli : Input 3d Sierpinski pyramid.
21416#@cli : $ sierpinski3d 3,100,-100 +primitives3d 1 color3d[-2] ${-rgb}
21417+sierpinski3d : check ${1=4}>=0 skip ${2=1},${3=1}
21418-e[^-1] "Input 3D Sierpinski pyramid of degree $1, with width $2 and height $3."
21419  l[]
21420    _sierpinski3d {-$2/2},{-$2/2},{-$3/2},{$2/2},{-$2/2},{-$3/2},\
21421                  {$2/2},{$2/2},{-$3/2},{-$2/2},{$2/2},{-$3/2},\
21422                  0,0,{$3/2},$1
21423  +3d endl
21424  nm. [3D\ sierpinski]
21425
21426_sierpinski3d :
21427  if $16<=0
21428    (67.5;73.5;109.5;103.5;51.5;100.5;\
21429     5;5;\
21430     $1;$2;$3;\
21431     $4;$5;$6;\
21432     $7;$8;$9;\
21433     $10;$11;$12;\
21434     $13;$14;$15;\
21435     4;0;3;2;1;\
21436     3;0;4;3;\
21437     3;1;4;0;\
21438     3;2;4;1;\
21439     3;3;4;2)
21440    1,15,1,1,200 1,5,1,1,1 a[-3--1] y
21441  return fi
21442  _sierpinski3d $1,$2,$3,\
21443                 {($1+$4)/2},{($2+$5)/2},{($3+$6)/2},\
21444                 {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\
21445                 {($1+$10)/2},{($2+$11)/2},{($3+$12)/2},\
21446                 {($1+$13)/2},{($2+$14)/2},{($3+$15)/2},\
21447                 {$16-1}
21448  _sierpinski3d {($1+$4)/2},{($2+$5)/2},{($3+$6)/2},\
21449                 $4,$5,$6,\
21450                 {($4+$7)/2},{($5+$8)/2},{($6+$9)/2},\
21451                 {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\
21452                 {($4+$13)/2},{($5+$14)/2},{($6+$15)/2},\
21453                 {$16-1}
21454  _sierpinski3d {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\
21455                 {($4+$7)/2},{($5+$8)/2},{($6+$9)/2},\
21456                 $7,$8,$9,\
21457                 {($7+$10)/2},{($8+$11)/2},{($9+$12)/2},\
21458                 {($7+$13)/2},{($8+$14)/2},{($9+$15)/2},\
21459                 {$16-1}
21460  _sierpinski3d {($1+$10)/2},{($2+$11)/2},{($3+$12)/2},\
21461                 {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\
21462                 {($7+$10)/2},{($8+$11)/2},{($9+$12)/2},\
21463                 $10,$11,$12,\
21464                 {($10+$13)/2},{($11+$14)/2},{($12+$15)/2},\
21465                 {$16-1}
21466  _sierpinski3d {($1+$13)/2},{($2+$14)/2},{($3+$15)/2},\
21467                 {($4+$13)/2},{($5+$14)/2},{($6+$15)/2},\
21468                 {($7+$13)/2},{($8+$14)/2},{($9+$15)/2},\
21469                 {($10+$13)/2},{($11+$14)/2},{($12+$15)/2},\
21470                 $13,$14,$15,\
21471                 {$16-1}
21472
21473#@cli size3d
21474#@cli : Return bounding box size of the last selected 3D object.
21475size3d :
21476  +rows. 8,{8+3*i[6]} r. 3,{h/3},1,1,-1 s. x,3
21477  u {-3,iM-im},{-2,iM-im},{iM-im}
21478  rm[-3--1]
21479
21480#@cli skeleton3d : _metric,_frame_type={ 0=squares | 1=diamonds | 2=circles | 3=auto },_skeleton_opacity,\
21481# _frame_opacity,_is_frame_wireframe={ 0 | 1 }
21482#@cli : Build 3D skeletal structure object from 2d binary shapes located in selected images.
21483#@cli : 'metric' can be { 0=chebyshev | 1=manhattan | 2=euclidean }.
21484#@cli : Default values: 'metric=2', 'bones_type=3', 'skeleton_opacity=1' and 'frame_opacity=0.1'.
21485#@cli : $ shape_cupid 480 +skeleton3d ,
21486skeleton3d : check "isint(${1=2}) && $1>=0 && $1<=2 && isint(${2=3}) && $2>=0 && $2<=3" skip ${3=1},${4=0.1},${5=1}
21487  e[^-1] "Build 3D skeletal structure object from image$?, with "${arg\ 1+$1,chebyshev,manhattan,euclidean}" metric, "\
21488          ${arg\ 1+$2,squares,diamonds,circles,auto}" bones, skeleton opacity $3 and frame opacity $4 ."
21489  repeat $! l[$>] channels 0
21490
21491    # Construct skeleton representation.
21492    +distance 0,$1
21493    +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))"
21494    if $3 +slices. -1,0 isosurface3d. 0.5 o3d. $3 col3d. 255,0,0 fi
21495    *[0-2] pointcloud3d[0]
21496
21497    # Construct bones from skeleton.
21498    if $4 l[0] s3d 0
21499      n={1,@0}
21500      if $n
21501        r[2] 3,$n,1,1,-1 r[3] 2,$n,1,1,-1 r[4] 3,$n,1,1,-1
21502
21503        if $2==0" || "($2==3" && "$1==0) # Frame with squares.
21504          =[1] {4*$n}
21505          i[3] [2]x3 +z.. 0,1 z. 0,2 -[2] . +[4] .
21506          s. x *.. -1 a[-3--1] x +[3] . -[5,-1] a[2-5] x
21507          rm[3] 1,$n,1,1,4 +f. 4*y ++. 1 ++. 1 ++. 1 rv[-3,-1] a[-5--1] x mv. 3
21508
21509        elif $2==1" || "($2==3" && "$1==1) # Frame with diamonds.
21510          =[1] {4*$n}
21511          i[3] [2]x3 +z.. 0,0 z. 0,2 -[2] . +[4] .
21512          shift. 1,0 -[3] . +[5,-1] a[2-5] x
21513          rm[3] 1,$n,1,1,4 +f. 4*y ++. 1 ++. 1 ++. 1 rv[-3,-1] a[-5--1] x mv. 3
21514
21515        elif $2==2" || "($2==3" && "$1==2) # Frame with circles.
21516          =[1] {2*$n}
21517          +z[4] 0,0 z. 0,2 ++[2,-1] -[2,-2] a[2,-1] x
21518          rm[3] 1,$n,1,1,5 +f. 2*y ++. 1 3,100% a[-4--1] x mv. 3
21519        fi
21520        y a y o3d $4 if $5 p3d 1 fi col3d 200
21521      else rm empty3d fi
21522    endl else rm[0] fi
21523    +3d
21524  endl done
21525
21526#@cli snapshot3d : _size>0,_zoom>=0,_backgroundR,_backgroundG,_backgroundB,_backgroundA : [background_image],zoom>=0
21527#@cli : Take 2d snapshots of selected 3D objects.
21528#@cli : Set 'zoom' to 0 to disable object auto-scaling.
21529#@cli : Default values: 'size=512', 'zoom=1' and '[background_image]=(default)'.
21530#@cli : $ torus3d 100,20 rotate3d 1,1,0,60 snapshot3d 400,1.2,128,64,32
21531#@cli : $ torus3d 100,20 rotate3d 1,1,0,60 sample ? +snapshot3d[0] [1],1.2
21532snapshot3d : check "${2=1}>=0" skip ${1=512},${3=""}
21533  if ${"is_image_arg $1"} # Background image specified.
21534    e[0--3] "Take $1x$1 snapshot$? of 3D object$?, with zoom factor $2 and background image $3."
21535    pass$1 0 to_color.
21536  elif isnum($3) # Background color specified.
21537    e[0--3] "Take $1x$1 snapshot$? of 3D object$?, with zoom factor $2 and background color ${3--1}."
21538    (${3--1}) y. c r. $1,$1 to_color.
21539  else # Default background color.
21540    e[0--3] "Take $1x$1 snapshot$? of 3D object$?, with zoom factor $2 and default background."
21541    1,2,1,3,32,64,32,116,64,96 r. $1,$1,1,3,3
21542  fi
21543  repeat $!-1 . l[$>,-1]
21544    if $2!=0 c3d[0] n3d[0] *3d[0] {3*min(w,h)*$2/4} fi
21545    if s>3 # RGBA rendering.
21546      100%,100%,1,3,-1 j3d. [0],50%,50%,0,1
21547      to_rgba. replace_color. 0,0,-1,-1,-1,255,0,0,0,0 blend[-2,-1] alpha
21548    else # RGB rendering.
21549      j3d[1] [0],50%,50%,0,1
21550    fi
21551   nm[1] {-2,n} rm[0]
21552  endl done rm.
21553
21554#@cli sl3d : eq. to 'specl3d'. : (+)
21555
21556#@cli specl3d : value>=0 : (+)
21557#@cli : Set lightness of 3D specular light.
21558#@cli : (eq. to 'sl3d').
21559#@cli : Default value: 'value=0.15'.
21560#@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,@$>} \
21561# snapshot3d[-1] 400 done remove[0]
21562
21563#@cli ss3d : eq. to 'specs3d'. : (+)
21564
21565#@cli specs3d : value>=0 : (+)
21566#@cli : Set shininess of 3D specular light.
21567#@cli : (eq. to 'ss3d').
21568#@cli : Default value: 'value=0.8'.
21569#@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,@$>} \
21570# snapshot3d[-1] 400 done remove[0]
21571
21572#@cli sphere3d : radius,_nb_recursions>=0 : (+)
21573#@cli : Input 3D sphere at (0,0,0), with specified geometry.
21574#@cli : Default value: 'nb_recursions=3'.
21575#@cli : $ sphere3d 100 +primitives3d 1 color3d[-2] ${-rgb}
21576
21577#@cli spherical3d : _nb_azimuth>=3,_nb_zenith>=3,_radius_function(phi,theta)
21578#@cli : Input 3D spherical object at (0,0,0), with specified geometry.
21579#@cli : Default values: 'nb_zenith=nb_azimut=64' and 'radius_function="abs(1+0.5*cos(3*phi)*sin(4*theta))"'.
21580#@cli : $ spherical3d 64 +primitives3d 1
21581+spherical3d : check "${1=64}>=3 && ${2=$1}>=3" skip "${3=abs(1+0.5*cos(3*phi)*sin(4*theta))}"
21582  e[^-1] "Input 3D spherical object, with subdivisions ($1,$2) and height function '$3'."
21583  ('CImg3d':y) # Magic number.
21584  n1={round($1)} n2={round($2)}
21585
21586  # Define 3D vertices.
21587  $n1,{$n2-1},1,3,"phi = 2*pi*(x+0.5)/w;\
21588                   theta = -pi/2+pi*(y+0.5)/h;\
21589                   cp = cos(phi);\
21590                   sp = sin(phi);\
21591                   ct = cos(theta);\
21592                   ($3)*if(c==0,ct*cp,if(c==1,ct*sp,sin(theta)))"
21593  r. {w*h},3,1,1,-1 permute. yxzc
21594  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.
21595  nbv={h} y.
21596
21597  # Define 3D primitives.
21598  $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")))"
21599  r. {w*h},4,1,1,-1 permute. yxzc i.. 1,{h},1,1,4 a[-2,-1] x
21600  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.
21601  +[-5,-4] 2 rv[-5,-4] +[-2,-1] {$nbv-$n1} a[-3--1] x a[-4--2] x
21602  nbp={h+{-2,h}+{-3,h}}
21603  y[-3--1] a[-3--1] y
21604
21605  # Define other object information (properties, colors and opacities).
21606  i... ($nbv;$nbp)
21607  1,{3*$nbp},1,1,200 1,$nbp,1,1,1 a[-2,-1] y
21608
21609  # Append as a 3D object.
21610  a[-5--1] y nm. "[3D spherical surface '$3']"
21611
21612#@cli spline3d : x0[%],y0[%],z0[%],u0[%],v0[%],w0[%],x1[%],y1[%],z1[%],u1[%],v1[%],w1[%],_nb_vertices>=2
21613#@cli : Input 3D spline with specified geometry.
21614#@cli : Default values: 'nb_vertices=128'.
21615#@cli : $ repeat 100 spline3d {u},{u},{u},{u},{u},{u},{u},{u},{u},{u},{u},{u},128 color3d[-1] ${-rgb} done \
21616# box3d 1 primitives3d[-1] 1 add3d
21617+spline3d : check ${13=128}>=2
21618  e[^-1] "Input new 3D spline from (${1-3}) [${4-6}] to (${7-9}) [${10-12}] with $13 vertices."
21619  ('CImg3d') +. 0.5   # Header.
21620  ($13;{$13-1})       # Nb vertices / primitives.
21621  # Define vertices.
21622  1,$13,1,1,1 (0;1) r. 1,$13,1,1,3 +sqr. +*[-2,-1] a[-4--1] x
21623  +*. '$2,$5,{3*(($8)-($2))-2*($5)-($11)},{($5)+($11)+2*(($2)-($8))}' l. s x + endl
21624  +*.. '$3,$6,{3*(($9)-($3))-2*($6)-($12)},{($6)+($12)+2*(($3)-($9))}' l. s x + endl
21625  *... '$1,$4,{3*(($7)-($1))-2*($4)-($10)},{($4)+($10)+2*(($1)-($7))}' l... s x + endl
21626  a[-3--1] x
21627  1,{$13-1},1,1,2 (0,1;{$13-2},{$13-1}) r. 2,..,1,1,3 round. a[-2,-1] x # Primitives.
21628  1,{3*($13-1)},1,1,200 1,{$13-1},1,1,1 # Colors / opacities.
21629  y[-3,-4,-6] a[-6--1] y
21630
21631#@cli s3d : eq. to 'split3d'. : (+)
21632
21633#@cli split3d : _full_split={ 0 | 1 } : (+)
21634#@cli : Split selected 3D objects into feature vectors :
21635#@cli : * If 'full_split==0', { header, sizes, vertices, primitives, colors, opacities }.
21636#@cli : * If 'full_split==1', { header, sizes, vertices, p0,...,pP, c0,...,cP, o0,...,oP }.
21637#@cli : (eq. to 's3d').\n
21638#@cli : To recreate the 3D object, append all produced images along the y-axis.
21639#@cli : Default value: 'full_split=0'.
21640#@cli : $ box3d 100 +split3d
21641
21642#@cli sprite3d
21643#@cli : Convert selected images as 3D sprites.
21644#@cli : Selected images with alpha channels are managed.
21645#@cli : $ image.jpg sprite3d
21646sprite3d :
21647  e[^-1] "Convert image$? as 3D sprites."
21648  repeat $! l[$>] nm={0,n}
21649    split_opacity
21650    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]
21651    if $!==2 (1) a y
21652    else /. 255 i.. (-128;{w};{h};{s}) y.
21653    fi
21654    a y
21655  nm $nm endl done
21656
21657#@cli sprites3d : [sprite],_sprite_has_alpha_channel={ 0 | 1 }
21658#@cli : Convert selected 3D objects as a sprite cloud.
21659#@cli : Set 'sprite_has_alpha_channel' to 1 to make the last channel of the selected sprite be a transparency mask.
21660#@cli : Default value: 'mask_has_alpha_channel=0'.
21661#@cli : $ torus3d 100,20 image.jpg resize2dy[-1] 64 100%,100% gaussian[-1] 30%,30% *[-1] 255 append[-2,-1] c \
21662# +sprites3d[0] [1],1 display_rgba[-2]
21663sprites3d : check ${is_image_arg\ $1} skip ${2=0}
21664  e[^-1] "Convert image$? as 3D sprites clouds, using sprite $1 ("${"arg {1+!$2},with,without"}" alpha-channel)."
21665  repeat $!
21666    if !{$>,i(0,7)} continue fi # Do nothing if 3D object is empty.
21667    pass$1 0
21668    if !w empty3d rv[$>,-1] nm[$>] {n} rm. continue fi
21669    l[$>,-1]
21670    s3d[0] N={1,@0} =[1] $N,0,1
21671    rm[3-5] i[3] (1,0;1,{$N-1}) r[3] 2,$N,1,1,3 round[3]
21672    if $2 # With alpha-channel.
21673      if s==1 # Only alpha-channel.
21674        i.. 3,$N,1,1,200 /. 255
21675        i.. (-128;{w};{h};1)
21676        if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi
21677      else # Image + alpha.
21678        s. c,-{s-1} /. 255
21679        i... (-128;{w};{h};{-2,s})
21680        if $N>1 i.. 1,{4*($N-1)},1,1,-128,0,0,0 fi
21681        i.. (-128;{w};{h};1)
21682        if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi
21683      fi
21684    else  # Without alpha-channel.
21685      i.. (-128;{w};{h};{s}) y[-3,-1]
21686      if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi
21687      1,$N,1,1,1
21688    fi
21689    y a y
21690  endl done
21691
21692#@cli star3d : _nb_branches>0,0<=_thickness<=1
21693#@cli : Input 3D star at position `(0,0,0)`, with specified geometry.
21694#@cli : Default values: 'nb_branches=5' and 'thickness=0.38'.
21695#@cli : $ star3d , +primitives3d 1 color3d[-2] ${-rgb}
21696+star3d : check "${1=5}>0 && ${2=0.38}>=0 && $2<=1"
21697  e[^-1] "Input 3D star, with $1 branches and thickness $2."
21698  N={2*$1} ('CImg3d') +. 0.5 ({$N+1};$N)
21699  ({-pi/2};{3*pi/2}) r. 1,{$N+1},1,1,3 rows. 0,{h-2} +sin. cos.. a[-2,-1] x
21700  (1,1;$2,$2) *[-2,-1] z. 0,2 r. 3,{h+1},1,1,0
21701  (3,$N,1,0;3,$N,$N,{$N-1}) r. 4,$N,1,1,3 round. =. 0,2,100%
21702  3,$N,1,1,200 1,$N,1,1,1 y[-6,-4--2] a[-6--1] y nm. [3D\ star]
21703
21704#@cli streamline3d : x[%],y[%],z[%],_L>=0,_dl>0,_interpolation,_is_backward={ 0 | 1 },_is_oriented={ 0 | 1 } : \
21705# 'formula',x,y,z,_L>=0,_dl>0,_interpolation,_is_backward={ 0 | 1 },_is_oriented={ 0 | 1 } : (+)
21706#@cli : Extract 3D streamlines from selected vector fields or from specified formula.
21707#@cli : 'interpolation' can be { 0=nearest integer | 1=1st-order | 2=2nd-order | 3=4th-order }.
21708#@cli : Default values: 'dl=0.1', 'interpolation=2', 'is_backward=0' and 'is_oriented=0'.
21709#@cli : $ 100,100,100,3 rand -10,10 blur 3 repeat 300 +streamline3d[0] {u(100)},{u(100)},{u(100)},1000,1,1 \
21710# color3d[-1] ${-rgb} done remove[0] box3d 100 primitives3d[-1] 1 add3d
21711
21712#@cli -3d : eq. to 'sub3d'. : (+)
21713
21714#@cli sub3d : tx,_ty,_tz : (+)
21715#@cli : Shift selected 3D objects with the opposite of specified displacement vector.
21716#@cli : (eq. to '3d').
21717#@cli : Default values: 'ty=tz=0'.
21718#@cli : $ sphere3d 10 repeat 5 +sub3d[-1] 10,{u(-10,10)},0 color3d[-1] ${-rgb} done add3d
21719
21720#@cli superformula3d : resolution>1,m>=1,n1,n2,n3
21721#@cli : Input 2D superformula curve as a 3D object.
21722#@cli : Default values: 'resolution=1024', 'm=8', 'n1=1', 'n2=5' and 'n3=8'.
21723#@cli : $ superformula3d ,
21724+superformula3d : check "${1=1024}>1 && ${2=8}>=1" skip ${3=1},${4=5},${5=8}
21725  e[^-1] "Input 2D superformula curve, with resolution $1, m=$2 and (n1,n2,n3)=($3,$4,$5)."
21726  res={round($1)}
21727
21728  # Define object header and vertices.
21729  (67.5;73.5;109.5;103.5;51.5;100.5;$res;{$res-1})
21730  (0,{2*pi}) r. $res,1,1,1,3 .
21731
21732  *. {$2/4} +sin. cos.. abs[-2,-1]
21733  ^.. $4 ^. $5 +[-2,-1] ^. {-1/$3}
21734  +sin.. cos... *. .. *[-3,-2] n[-2,-1] -1,1
21735  a[-2,-1] y rows. 0,2 transpose. r. 1,{w*h},1,1,-1
21736
21737  # Define object primitives, colors and opacities.
21738  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
21739  1,{3*($res-1)},1,1,200 1,{$res-1},1,1,1 a[-5--1] y nm. [3D\ superformula]
21740
21741#@cli tensors3d : _radius_factor>=0,_shape={ 0=box | >=N=ellipsoid },_radius_min>=0
21742#@cli : Generate 3D tensor fields from selected images.
21743#@cli : when 'shape'>0, it gives the ellipsoid shape precision.
21744#@cli : Default values: 'radius_factor=1', 'shape=2' and 'radius_min=0.05'.
21745#@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
21746tensors3d : check "${1=1}>=0 && isint(${2=2}) && $2>=0 && ${3=0.05}>=0"
21747  e[^-1] "Generate 3D tensor field(s) from image$?, with radius factor $1, "\
21748          ${"if $2 u ellipsoid else u box fi"}" shape and radius min $3."
21749  repeat $! l[$>]
21750
21751    # Check input image format.
21752    if s==1 100%,100%,100%,6,"[i#0,0,0,i#0,0,i#0]" k.
21753    elif s==3 100%,100%,100%,6,"[R#0,G#0,0,B#0,0,0]" k.
21754    elif s==4 100%,100%,100%,6,"[R#0,G#0,0,A#0,0,0]" k.
21755    elif s==9 100%,100%,100%,6,"I=I#0;[I[0],I[1],I[2],I[4],I[5],I[8]]" k.
21756    fi
21757    if s!=6 error[0--4] "Command '$0': Image '"{n}"' has an invalid size (spectrum="{s}")." fi
21758
21759    # Estimate eigenvalues/eigenvectors.
21760    100%,100%,100%,12,"
21761      T = I(#0);
21762      M = [ T[0], T[1], T[2], T[1], T[3], T[4], T[2], T[4], T[5] ];
21763      eig = eig(M);
21764      if (det(eig[3,9])<0, eig[3]*=-1; eig[4]*=-1; eig[5]*=-1);
21765      eig[0] = max(0,eig[0]);
21766      eig[1] = max(0,eig[1]);
21767      eig[2] = max(0,eig[2]);
21768      eig"
21769    k.
21770
21771    # Create 3D object.
21772    if $2 sphere3d 1,{$2-1} else box3d 1 fi
21773    N,P={[i[6],i[7]]} siz={h} n3d. c3d. .x{0,whd-1}
21774    f[0] "
21775      const N = "$N";
21776      const P = "$P";
21777      const siz = "$siz";
21778      eig = I;
21779      const d = size(eig);
21780      ind = 1 + x + w*y + wh*z;
21781      L = eig[0,3];
21782      if (max(L)==0, # Empty tensor -> do not display
21783        i[#ind,6] = i[#ind,7] = 0;
21784        resize(#ind,1,8,1,1,0);
21785      _(else),
21786        L*=$1;
21787        L[0] = max($3,L[0]);
21788        L[1] = max($3,L[1]);
21789        L[2] = max($3,L[2]);
21790        R = eig[3,9];
21791        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)));
21792
21793        ref(crop(#ind,0,8,0,0,1,3*N,1,1),pts);
21794        pts *= resize(L,size(pts),0,2);
21795        pts = mul(pts,R,3);
21796        pts += resize([x,y,z],size(pts),0,2);
21797        draw(#ind,pts,0,8,0,0,1,size(pts),1,1);
21798
21799        col0 = cut(255*anisotropy*abs([ R[0],R[1],R[2] ]) + (1 - anisotropy)*200,0,255);
21800        col = resize(col0,3*P,0,2);
21801        const off = siz - 4*P;
21802        draw(#ind,col,0,off,0,0,1,size(col),1,1);
21803        0); I"
21804    rm[0] +3d
21805  endl done
21806
21807#@cli text_pointcloud3d : _"text1",_"text2",_smoothness
21808#@cli : Input 3D text pointcloud from the two specified strings.
21809#@cli : Default values: 'text1="text1"', 'text2="text2"' and 'smoothness=1'.
21810#@cli : $ text_pointcloud3d "G'MIC","Rocks!"
21811+text_pointcloud3d : skip "${1=text1}","${2=text2}",${3=1}
21812  e[^-1] "Input 3D pointcloud text object from strings '$1' and '$2', with smoothness $3."
21813  0 t. "$1",0,0,53,1,1
21814  0 t. "$2",0,0,53,1,1 mirror. y
21815  autocrop[-2,-1] 0
21816  expand_xy[-2,-1] 2,0 dilate[-2,-1] 2
21817  permute. zyxc r[-2,-1] ${-max_whd} &[-2,-1]
21818
21819  100%,100% rand. 0,{{-2,d}-1} round. ri. .. f. 'if(z==i,1,0)'
21820  distance. 1 +. 1 +f. 1 rv[-2,-1] /[-2,-1] *. ..
21821  +dilate. 0,0,{d} ==[-2,-1] *. ..
21822
21823  1,100%,100% rand. 0,{{-2,w}-1} round. ri. .. f. 'if(x==i,1,0)'
21824  distance. 1 +. 1 +f. 1 rv[-2,-1] /[-2,-1] *. ...
21825  +dilate. 0,0,{d} ==[-2,-1] *[-3,-1]
21826
21827  -|[-2,-1]
21828
21829  b. $3 isosurface3d. 25%
21830  c3d. n3d. nm. "[3D text pointcloud]"
21831
21832#@cli text3d : text,_font_height>0,_depth>0,_smoothness
21833#@cli : Input a 3D text object from specified text.
21834#@cli : Default values: 'font_height=53', 'depth=10' and 'smoothness=1.5'.
21835#@cli : $ text3d "G'MIC as a\n3D logo!"
21836+text3d : skip ${2=53},${3=10},${4=1.5}
21837  e[^-1] "Input 3D text object '$1' with size $2, depth $3 and smoothness $4."
21838  0 t. "$1",0,0,$2,1,1 autocrop. 0 r. 100%,100%,$3 expand_xyz. 10,0
21839  b. $4 isosurface3d. 40% rv3d. nm. "[3D text '$1']"
21840
21841#@cli t3d : eq. to 'texturize3d'.
21842t3d : check ${"is_image_arg $1"}" && (!narg(${2=}) || "${"is_image_arg $2"}")"
21843  e[^-1] "Texturize 3D object$? with texture $1"\
21844         ${"if narg($2) u \" and texture coordinates $2\" else u \"\" fi"}"."
21845  pass$1 0 slices. 0 if s==1 to_rgb. else channels. 0,2 fi
21846  if narg($2) pass$2 else 0 fi
21847  v + _texturize3d
21848
21849#@cli texturize3d : [ind_texture],_[ind_coords]
21850#@cli : Texturize selected 3D objects with specified texture and coordinates.
21851#@cli : (eq. to 't3d').\n
21852#@cli : When '[ind_coords]' is omitted, default XY texture projection is performed.
21853#@cli : Default value: 'ind_coords=(undefined)'.
21854#@cli : $ image.jpg torus3d 100,30 texturize3d[-1] [-2] keep[-1]
21855texturize3d : check ${"is_image_arg $1"}" && (!narg(${2=}) || "${"is_image_arg $2"}")"
21856  e[^-1] "Texturize 3D object$? with texture $1"\
21857         ${"if narg($2) u \" and texture coordinates $2\" else u \"\" fi"}"."
21858  pass$1 0 slices. 0 if s==1 to_rgb. else channels. 0,2 fi
21859  if narg($2) pass$2 else 0 fi
21860  v + _$0
21861
21862_texturize3d :
21863  repeat $!-2 l[$>,-2,-1]
21864    np={f2ui(i[7])}
21865    s3d[0]
21866
21867    # Retrieve texture coordinates for each vertex.
21868    if !w
21869      +r[2] 3,{2,round(h/3)},1,1,-1 s. x,3 rm.
21870      n.. 0,{6,w-1} n. 0,{6,h-1} a[-2,-1] x
21871      mv. -2
21872    fi
21873
21874    # Texturize 3D object
21875    1,{5,2*h}
21876    1,{5,3*h}
21877    1,{5,h},1,1,1
21878    eval "
21879      add_material() = (
21880        ind_tex>=0?( # Shared texture
21881          copy(i[#-2,qc],[ -128,ind_tex,0,0 ],4); qc+=4
21882        ):( # Non-shared texture
21883          qc + whds#6 + 4>=h(#-2)?resize(#-2,1,int(1.5*qc + whds#6 + 4),1,1,0,0);
21884          copy(i[#-2,qc],[ -128,w#6,h#6,s#6 ],4); qc+=4;
21885          copy(i[#-2,qc],i(#6),whds#6); qc+=whds#6;
21886          ind_tex = np;
21887        );
21888      );
21889
21890      ind_tex = -1;
21891      for (pp = pc = qp = qc = np = 0, pp<h#3, ++np,
21892        qp + 13>=h(#-3)?resize(#-3,1,int(1.5*qp + 13),1,1,0,0);
21893        qc + 3>=h(#-2)?resize(#-2,1,int(1.5*qc + 3),1,1,0,0);
21894        N = i[#3,pp++];
21895
21896        N==1?( # Colored point
21897          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
21898          R = i(#6,tx0,ty0,0,0); G = i(#6,tx0,ty0,0,1); B = i(#6,tx0,ty0,0,2);
21899          copy(i[#-3,qp],[ 1,v0 ],2); qp+=2;
21900          copy(i[#-2,qc],[ R,G,B ],3); qc+=3;
21901
21902        ):(N==2 || N==6)?( # Colored segment
21903          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
21904          v1 = i[#3,pp++]; tx1 = i(#7,0,v1); ty1 = i(#7,1,v1);
21905          N==6?(pp+=4);
21906          copy(i[#-3,qp],[ 6,v0,v1,tx0,ty0,tx1,ty1 ],7); qp+=7;
21907          add_material();
21908
21909        ):(N==3 || N==9)?( # Colored triangle
21910          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
21911          v1 = i[#3,pp++]; tx1 = i(#7,0,v1); ty1 = i(#7,1,v1);
21912          v2 = i[#3,pp++]; tx2 = i(#7,0,v2); ty2 = i(#7,1,v2);
21913          N==9?(pp+=6);
21914          copy(i[#-3,qp],[ 9,v0,v1,v2,tx0,ty0,tx1,ty1,tx2,ty2 ],10); qp+=10;
21915          add_material();
21916
21917        ):(N==4 || N==12)?( # Colored quadrangle
21918          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
21919          v1 = i[#3,pp++]; tx1 = i(#7,0,v1); ty1 = i(#7,1,v1);
21920          v2 = i[#3,pp++]; tx2 = i(#7,0,v2); ty2 = i(#7,1,v2);
21921          v3 = i[#3,pp++]; tx3 = i(#7,0,v3); ty3 = i(#7,1,v3);
21922          N==12?(pp+=8);
21923          copy(i[#-3,qp],[ 12,v0,v1,v2,v3,tx0,ty0,tx1,ty1,tx2,ty2,tx3,ty3 ],13); qp+=13;
21924          add_material();
21925
21926        ):N==5?( # Colored sphere
21927          v0 = i[#3,pp++]; tx0 = i(#7,0,v0); ty0 = i(#7,1,v0);
21928          v1 = i[#3,pp++]; tx1 = i(#7,0,v1); ty1 = i(#7,1,v1);
21929          v2 = i[#3,pp++]; pp+=2;
21930          (tx0+=tx1)/=2; (ty0+=ty1)/=2;
21931          R = i(#6,tx0,ty0,0,0); G = i(#6,tx0,ty0,0,1); B = i(#6,tx0,ty0,0,2);
21932          copy(i[#-3,qp],[ 5,v0,v1,v2,0,0 ],6); qp+=6;
21933          copy(i[#-2,qc],[ R,G,B ],3); qc+=3;
21934
21935        );
21936      );
21937      resize(#-3,1,qp,1,1,0,0);
21938      resize(#-2,1,qc,1,1,0,0)"
21939
21940    rm[3-5] mv[-3--1] 3
21941    if !w rm.. fi
21942    a[0-5] y
21943  endl done rm[-2,-1]
21944
21945#@cli torus3d : _radius1,_radius2,_nb_subdivisions1>2,_nb_subdivisions2>2
21946#@cli : Input 3D torus at (0,0,0), with specified geometry.
21947#@cli : Default values: 'radius1=1', 'radius2=0.3', 'nb_subdivisions1=24' and 'nb_subdivisions2=12'.
21948#@cli : $ torus3d 10,3 +primitives3d 1 color3d[-2] ${-rgb}
21949+torus3d : check "${3=24}>2 && ${4=12}>2" skip ${1=1},${2=0.3}
21950  e[^-1] "Input 3D torus, with radii ($1,$2) and subdivisions ($3,$4)."
21951  # Header.
21952  nbp={$3*$4}
21953  1,8,1,1,67.5,73.5,109.5,103.5,51.5,100.5,$nbp,{$4*$3}
21954
21955  # Vertices.
21956  (0;{2*pi}) +y. x
21957  r.. 1,{$3+1},1,1,3 z.. 0,0,0,{$3-1}
21958  r. {$4+1},1,1,1,3 z. 0,{$4-1}
21959  +sin[-2,-1] cos[-4,-3] r[-4--1] $4,$3
21960  *... $2 +... $1 *. $2 *[-4] ... *[-3,-2]
21961  y[-3--1] a[-3--1] x
21962
21963  # Primitives.
21964  1,$3,1,1,'y' *. $4 +shift. 0,-1 $4,1,1,1,'x' +shift. -1 r[-4--1] $4,$3
21965  ++[-4,-1] +.. [-4] +[-5] ... +[-4,-3] y[-4--1] i[-5] 1,{h},1,1,4 a[-5--1] x
21966
21967  # Colors / opacities.
21968  3,{h},1,1,200 1,{h},1,1,1 y[-4--2] a[-5--1] y
21969  nm. [3D\ torus]
21970
21971#@cli triangle3d : x0,y0,z0,x1,y1,z1,x2,y2,z2
21972#@cli : Input 3D triangle at specified coordinates.
21973#@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
21974+triangle3d :
21975  e[^-1] "Input 3D triangle ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)."
21976  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]
21977
21978#@cli volume3d
21979#@cli : Transform selected 3D volumetric images as 3D parallelepipedic objects.
21980#@cli : $ image.jpg animate blur,0,5,30 append z volume3d
21981volume3d :
21982  e[^-1] "Transform image$? as 3D parallelepipedic objects."
21983  repeat $! l[$>]
21984    w={w} h={h} d={d}
21985    +slices[0] 0
21986    +columns[0] 0 permute. zyxc mirror. x
21987    +slices[0] 100% mirror. x
21988    +columns[0] 100% permute. zyxc
21989    +rows[0] 100% permute. xzyc
21990    +rows[0] 0 permute. xzyc mirror. y
21991    rm[0] image6cube3d *3d $w,$h,$d
21992  endl done
21993
21994#@cli weird3d : _resolution>0
21995#@cli : Input 3D weird object at (0,0,0), with specified resolution.
21996#@cli : Default value: 'resolution=32'.
21997#@cli : $ weird3d 48 +primitives3d 1 color3d[-2] ${-rgb}
21998+weird3d : skip ${1=32}
21999  e[^-1] "Input 3D weird object, with resolution $1."
22000  isosurface3d '"
22001    T = 1.61803399;
22002    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))"',\
22003    0,-4.7,-4.7,-4.7,4.7,4.7,4.7,$1,$1,$1
22004  c3d. n3d. nm. [3D\ weird]
22005
22006#-------------------------------
22007#
22008#@cli :: Control Flow
22009#
22010#-------------------------------
22011
22012#@cli ap : eq. to 'apply_parallel'.
22013ap :
22014  _gmic_s="$?" v + _apply_parallel "$*"
22015
22016#@cli apply_parallel : "command"
22017#@cli : Apply specified command on each of the selected images, by parallelizing it for all image of the list.
22018#@cli : (eq. to 'ap').
22019#@cli : $ image.jpg +mirror x +mirror y apply_parallel "blur 3"
22020apply_parallel :
22021  _gmic_s="$?" v + _$0 "$*"
22022
22023_apply_parallel :
22024  e[0--3] "Apply command '$*' on all image"$_gmic_s" in parallel, using "$_cpus" threads."
22025  if $!" && "narg("$*")
22026    m "_ap : repeat $! l[$>] $* if $! k[0] else 0 fi endl done"
22027    N={min($!,$_cpus)}
22028    commands= sep= repeat $N commands=$commands${sep}_ap[$>--1:$N] sep=, done
22029    parallel $commands
22030    um _ap
22031  fi
22032
22033#@cli apc : eq. to 'apply_parallel_channels'.
22034apc :
22035  _gmic_s="$?" v + _apply_parallel_channels "$*"
22036
22037#@cli apply_parallel_channels : "command"
22038#@cli : Apply specified command on each of the selected images, by parallelizing it for all channel
22039#@cli : of the images independently.
22040#@cli : (eq. to 'apc').
22041#@cli : $ image.jpg apply_parallel_channels "blur 3"
22042apply_parallel_channels :
22043  _gmic_s="$?" v + _$0 "$*"
22044
22045_apply_parallel_channels :
22046  e[0--3] "Apply command '$*' on all channels of image"$_gmic_s" in parallel, using "$_cpus" threads."
22047  N=$! repeat $N s$>={$>,s} done s c
22048  ap "$1"
22049  repeat $N a[$>-{$>+${s$>}-1}] c done
22050
22051#@cli apo : eq. to 'apply_parallel_overlap'.
22052apo : check "${2=0}>=0 && isint(${3=0}) && $3>=0"
22053  _gmic_s="$?" v + _apply_parallel_overlap "$1",${2--1}
22054
22055#@cli apply_parallel_overlap : "command",overlap[%],nb_threads={ 0=auto | 1 | 2 | 4 | 8 | 16 }
22056#@cli : Apply specified command on each of the selected images, by parallelizing it on 'nb_threads'
22057#@cli : overlapped sub-images.
22058#@cli : (eq. to 'apo').\n
22059#@cli : 'nb_threads' must be a power of 2.
22060#@cli : Default values: 'overlap=0','nb_threads=0'.
22061#@cli : $ image.jpg +apply_parallel_overlap "smooth 500,0,1",1
22062apply_parallel_overlap : check "${2=0}>=0 && isint(${3=0}) && $3>=0"
22063  _gmic_s="$?" v + _$0 "$1",${2--1}
22064
22065_apply_parallel_overlap : check "${2=0}>=0 && isint(${3=0}) && $3>=0"
22066  N={if($3,max(1,round($3)),$_cpus)} N={2^int(log2(min(16,$N)))}
22067  e[0--3] "Apply parallelized command '$1' on image"$_gmic_s", with overlap $2 and "$N" threads."
22068  __apo_exception=""
22069  m "_check1 : if $!!=1 rm 0 __apo_exception=\"Command 'apply_parallel_overlap': Specified command '$1' changes the
22070     size of the image stack.\" fi"
22071  repeat $! l[$>]
22072    _apply_parallel_overlap$N "$1",$2
22073  endl done
22074  um _check1
22075
22076_apply_parallel_overlap1 :
22077  $1
22078  if narg($__apo_exception) error[0--12] $__apo_exception fi
22079
22080_apply_parallel_overlap2 :
22081  if w>=h
22082    ovx={round(if(${"is_percent $2"},w*$2,$2))} w2={int(w/2)}
22083    +z[0] {$w2-$ovx},100% z[0] 0,{$w2+$ovx-1}
22084    parallel "l[0] $1 _check1 endl","l[1] $1 _check1 endl"
22085    if narg($__apo_exception) error[0--12] $__apo_exception fi
22086    z[0] 0,{0,w-1-$ovx} z[1] $ovx,100% a x
22087  else
22088    ovy={round(if(${"is_percent $2"},h*$2,$2))} h2={int(h/2)}
22089    +rows[0] {$h2-$ovy},100% rows[0] 0,{$h2+$ovy-1}
22090    parallel "l[0] $1 _check1 endl","l[1] $1 _check1 endl"
22091    if narg($__apo_exception) error[0--12] $__apo_exception fi
22092    rows[0] 0,{0,h-1-$ovy} rows[1] $ovy,100% a y
22093  fi
22094
22095_apply_parallel_overlap4 :
22096  if max(w,h)/min(w,h)>=3
22097    _apply_parallel_overlap2 "_apply_parallel_overlap2 \"$1\",$2",$2
22098  else
22099    ovx={round(if(${"is_percent $2"},w*$2,$2))} w2={int(w/2)}
22100    ovy={round(if(${"is_percent $2"},h*$2,$2))} h2={int(h/2)}
22101    +z[0] {$w2-$ovx},0,100%,{$h2+$ovy-1} +z[0] 0,{$h2-$ovy},{$w2+$ovx-1},100%
22102    +z[0] {$w2-$ovx},{$h2-$ovy},100%,100% z[0] 0,0,{$w2+$ovx-1},{$h2+$ovy-1}
22103    parallel "l[0] $1 _check1 endl","l[1] $1 _check1 endl","l[2] $1 _check1 endl","l[3] $1 _check1 endl"
22104    if narg($__apo_exception) error[0--12] $__apo_exception fi
22105    z[0] 0,0,{0,w-1-$ovx},{0,h-1-$ovy} z[1] $ovx,0,100%,{1,h-1-$ovy}
22106    z[2] 0,$ovy,{2,w-1-$ovx},100% z[3] $ovx,$ovy,100%,100%
22107    a[0,1] x a[1,2] x a y
22108  fi
22109
22110_apply_parallel_overlap8 :
22111  _apply_parallel_overlap2 "_apply_parallel_overlap4 \"$1\",$2",$2
22112
22113_apply_parallel_overlap16 :
22114  _apply_parallel_overlap2 "_apply_parallel_overlap8 \"$1\",$2",$2
22115
22116#@cli at : eq. to 'apply_tiles'.
22117at : check "${2=10%}>0 && ${3=10%}>0 && ${4=10%}>0 && ${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 &&
22118            isint(${8=1}) && $8>=0 && $8<=3"
22119  _gmic_s="$?" v + _apply_tiles "$1",${2--1}
22120
22121#@cli apply_tiles : "command",_tile_width[%]>0,_tile_height[%]>0,_tile_depth[%]>0,_overlap_width[%]>=0,\
22122# _overlap_height[%]>=0,_overlap_depth[%]>=0,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
22123#@cli : Apply specified command on each tile (neighborhood) of the selected images, eventually with overlapping tiles.
22124#@cli : (eq. to 'at').
22125#@cli : Default values: 'tile_width=tile_height=tile_depth=10%','overlap_width=overlap_height=overlap_depth=0' \
22126# and 'boundary_conditions=1'.
22127#@cli : $ image.jpg +equalize[0] 256 +apply_tiles[0] "equalize 256",16,16,1,50%,50%
22128apply_tiles : check "${2=10%}>0 && ${3=10%}>0 && ${4=10%}>0 && ${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 &&
22129                     isint(${8=1}) && $8>=0 && $8<=3"
22130  _gmic_s="$?" v + _$0 "$1",${2--1}
22131
22132_apply_tiles :
22133  e[0--3] "Apply command '$1' on $2x$3x$4 tiles of image$?, with overlaps ($5,$6,$7) and "\
22134          ${"arg 1+$8,dirichlet,neumann,periodic,mirror"}" boundary conditions."
22135  repeat $! l[$>]
22136    bw={cut(round(${"is_percent $2"}?w*$2:$2),1,w)}
22137    bh={cut(round(${"is_percent $3"}?h*$3:$3),1,h)}
22138    bd={cut(round(${"is_percent $4"}?d*$4:$4),1,d)}
22139    ow={round(${"is_percent $5"}?$bw*$5:$5)}
22140    oh={round(${"is_percent $6"}?$bh*$6:$6)}
22141    od={round(${"is_percent $7"}?$bd*$7:$7)}
22142    sw={cut($bw-$ow,1,$bw)}
22143    sh={cut($bh-$oh,1,$bh)}
22144    sd={cut($bd-$od,1,$bd)}
22145    100%,100%,100%,{s+1} # Reconstructed image + weights
22146    if $ow>0" || "$oh>0" || "$od>0 l[] # Generate gaussian weight in case of overlap
22147      $bw,1,1 1,$bh,1 1,1,$bd
22148      = 1,50%,50%,50% distance 1
22149      /[0] {0.3*$bw} /[1] {0.3*$bh} /[2] {0.3*$bd}
22150      sqr * -1 exp r $bw,$bh,$bd,1 *
22151    endl else $bw,$bh,$bd,1,1
22152    fi
22153    $bw,$bh,$bd,[0]
22154    m "__at : $1 k. r "$bw,$bh,$bd,{0,s},0
22155    eval "
22156      ref(crop(#2),mask);
22157      for (z = 0, z<d#0, z+="$sd",
22158        for (y = 0, y<h#0, y+="$sh",
22159          for (x = 0, x<w#0, x+="$sw",
22160            draw(crop(#0,x,y,z,w,h,d,$8),0,0,0,0,w,h,d);
22161            run('__at.');
22162            breakpoint();
22163            draw(#1,crop(#-1),x,y,z,0,w,h,d,s#0,-1,mask);
22164            draw(#1,mask,x,y,z,s#0,w,h,d,1,-1);
22165          )
22166        )
22167      )"
22168    rm[-2,-1]
22169    s. c,-{0,s} /[-2,-1] k.
22170    um __at
22171  endl done
22172
22173#@cli apply_timeout : "command",_timeout={ 0=no timeout | >0=with specified timeout (in seconds) }
22174#@cli : Apply a command with a timeout.
22175#@cli : Set variable '$_is_timeout' to '1' if timeout occurred, '0' otherwise.
22176#@cli : Default value: 'timeout=20'.
22177apply_timeout : check "${2=20}>=0"
22178  if $2<=0
22179    e[0--3] "Apply command '$1' on image$?, with no timeout."
22180    $1
22181    _is_timeout=0
22182  else
22183    e[0--3] "Apply command '$1' on image$?, with a timeout of $2 seconds."
22184    l[] ('$/') id={is} rm endl
22185    l
22186      +store initial
22187      __done$id=0 __is_timeout$id=0
22188      parallel "$1 __done"$id"=1",\
22189               "l[] do if $|-"$|">$2 __is_timeout"$id"=1 error \"\" elif $__done"$id" break fi wait 100 while 1 endl"
22190    onfail
22191      rm $initial
22192      _is_timeout=0
22193      if ${__is_timeout$id} _is_timeout=1 error[0--5] "Command '$0': Time out ($2 seconds) for command '$1'."
22194      else error[0--5] "Command '$0': "${}
22195      fi
22196    endl
22197  fi
22198
22199#@cli check : condition : (+)
22200#@cli : Evaluate specified condition and display an error message if evaluated to false.
22201
22202#@cli check3d : _is_full_check={ 0 | 1 } : (+)
22203#@cli : Check validity of selected 3D vector objects, and display an error message
22204#@cli : if one of the selected images is not a valid 3D vector object.
22205#@cli : Full 3D object check is slower but more precise.
22206#@cli : Default value: 'is_full_check=1'.
22207
22208# check_display : calling_command_name
22209# Check if a display is available, and throw an error otherwise.
22210check_display : skip "${1=check_display}"
22211  if !{*,u} error[0--3] "Command '$1': No display available." fi
22212
22213# check_opencv : calling_command_name
22214# Check is OpenCV features are available, and throw an error otherwise.
22215check_opencv : skip "${1=check_opencv}"
22216  l[] check_opencv.mp4,0,0,1 rm
22217  onfail if find(['${}'],'-Dcimg_use_opencv')>0
22218    error[0--4] "Command '$1': No OpenCV features available. "\
22219                "Your G'MIC interpreter has not been compiled with OpenCV support."
22220  fi rm endl
22221
22222#@cli continue : (+)
22223#@cli : Go to end of current 'repeat...done', 'do...while' or 'local...endlocal' block.
22224#@cli : $ image.jpg repeat 10 blur 1 if 1==1 continue fi deform 10 done
22225
22226#@cli break : (+)
22227#@cli : Break current 'repeat...done', 'do...while' or 'local...endlocal' block.
22228#@cli : $ image.jpg repeat 10 blur 1 if 1==1 break fi deform 10 done
22229
22230#@cli do : (+)
22231#@cli : Start a 'do...while' block.
22232#@cli : $ image.jpg luminance i={ia+2} do set 255,{u(100)}%,{u(100)}% while ia<$i
22233
22234#@cli done : (+)
22235#@cli : End a 'repeat/for...done' block, and go to associated 'repeat/for' position, if iterations remain.
22236
22237#@cli elif : condition : (+)
22238#@cli : Start a 'elif...[else]...fi' block if previous 'if' was not verified
22239#@cli : and test if specified condition holds
22240#@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0=false | other=true }..
22241#@cli : $$ https://gmic.eu/tutorial/iffi
22242
22243#@cli else : (+)
22244#@cli : Execute following commands if previous 'if' or 'elif' conditions failed.
22245#@cli : $$ https://gmic.eu/tutorial/iffi
22246
22247#@cli fi : (+)
22248#@cli : End a 'if...[elif]...[else]...fi' block.
22249#@cli : (eq. to 'fi').\n
22250#@cli : $$ https://gmic.eu/tutorial/iffi
22251
22252#@cli endl : eq. to 'endlocal'. : (+)
22253
22254#@cli endlocal : (+)
22255#@cli : End a 'local...endlocal' block.
22256#@cli : (eq. to 'endl').
22257
22258#@cli error : message : (+)
22259#@cli : Print specified error message on the standard error (stderr) and exit interpreter, except
22260#@cli : if error is caught by a 'onfail' command.
22261#@cli : Command selection (if any) stands for displayed call stack subset instead of image indices.
22262
22263#@cli eval : expression : (+)
22264#@cli : Evaluate specified math expression.
22265#@cli : - If no command selection is specified, the expression is evaluated once and its result is set to status.
22266#@cli : - If command selection is specified, the evaluation is looped over selected images. Status is not modified.
22267#@cli :   (in this latter case, 'eval' is similar to 'fill' without assigning the image values).
22268
22269#@cli x : eq. to 'exec'. : (+)
22270
22271#@cli exec : _is_verbose={ 0 | 1 },"command" : (+)
22272#@cli : Execute external command using a system call.
22273#@cli : The status value is then set to the error code returned by the system call.
22274#@cli : If 'is_verbose=1', the executed command is allowed to output on stdout/stderr.
22275#@cli : (eq. to 'x').
22276#@cli : Default value: 'is_verbose=1'.
22277
22278#@cli xo : eq. to 'exec_out'.
22279xo :
22280  v + _exec_out $"*"
22281
22282#@cli exec_out : _mode,"command"
22283#@cli : Execute external command using a system call, and return resulting `stdout` and/or `stderr`.
22284#@cli : 'mode' can be { 0=stdout | 1=stderr | 2=stdout+stderr }.
22285exec_out :
22286  v + _exec_out $"*"
22287
22288_exec_out :
22289  l[]
22290    if "isint($1) && isin($1,0,1,2)" mode=$1 command="${2--1}"
22291    else mode=0 command="$*"
22292    fi
22293  onfail mode=0 command="$*" endl
22294  s0,s1,s2=stdout,stderr,stdout+stderr
22295  e[0--3] "Execute external command '"$command"', and return "${s$mode}" output."
22296  file_rand filename=${}
22297  if $mode==0 x $command" > "$filename
22298  elif $mode==1 x $command" 2> "$filename
22299  else x $command"  >"$filename" 2>&1"
22300  fi
22301  it $filename u {t} rm. delete $filename
22302
22303#@cli for : condition : (+)
22304#@cli : Start a 'for...done' block.
22305#@cli : $ image.jpg resize2dy 32 400,400,1,3 x=0 for $x<400 image[1] [0],$x,$x x+=40 done
22306
22307#@cli if : condition : (+)
22308#@cli : Start a 'if...[elif]...[else]...fi' block and test if specified condition holds.
22309#@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0=false | other=true }.
22310#@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
22311#@cli : $$ https://gmic.eu/tutorial/iffi
22312
22313#@cli l : eq. to 'local'. : (+)
22314
22315#@cli local : (+)
22316#@cli : Start a 'local...[onfail]...endlocal' block, with selected images.
22317#@cli : (eq. to 'l').
22318#@cli : $ image.jpg local[] 300,300,1,3 rand[0] 0,255 blur 4 sharpen 1000 endlocal
22319#@cli : $ image.jpg +local repeat 3 deform 20 done endlocal
22320#@cli : $$ https://gmic.eu/oldtutorial/_local
22321
22322#@cli mutex : index,_action={ 0=unlock | 1=lock } : (+)
22323#@cli : Lock or unlock specified mutex for multi-threaded programming.
22324#@cli : A locked mutex can be unlocked only by the same thread. All mutexes are unlocked by default.
22325#@cli : 'index' designates the mutex index, in [0,255].
22326#@cli : Default value: 'action=1'.
22327
22328#@cli noarg : (+)
22329#@cli : Used in a custom command, 'noarg' tells the command that its argument list have not been used
22330#@cli : finally, and so they must be evaluated next in the G'MIC pipeline, just as if the custom
22331#@cli : command takes no arguments at all.
22332#@cli : Use this command to write a custom command which can decide if it takes arguments or not.
22333
22334#@cli onfail : (+)
22335#@cli : Execute following commands when an error is encountered in the body of the 'local...endlocal' block.
22336#@cli : The status value is set with the corresponding error message.
22337#@cli : $ image.jpg +local blur -3 onfail mirror x endlocal
22338
22339#@cli parallel : _wait_threads,"command1","command2",... : (+)
22340#@cli : Execute specified commands in parallel, each in a different thread.
22341#@cli : Parallel threads share the list of images.
22342#@cli : 'wait_threads' can be { 0=when current environment ends | 1=immediately }.
22343#@cli : Default value: 'wait_threads=1'.
22344#@cli : $ image.jpg [0] parallel "blur[0] 3","mirror[1] c"
22345
22346# The implementation below allows to use parallel as a regular command with selections.
22347parallel : skip "${1=},${2=},${3=},${4=},${5=},${6=},${7=},${8=},${9=},${10=},${11=},${12=},${13=},${14=},${15=}"
22348  if $1==0||$1==1||$1==2 e[0--3] "Execute "{$#-1}" commands '${2--1}' in parallel on image$?."
22349  else e[0--3] "Execute "$#" commands '$*' in parallel on image$?."
22350  fi
22351  parallel $"*"
22352
22353#@cli progress : 0<=value<=100 : -1 : (+)
22354#@cli : Set the progress index of the current processing pipeline.
22355#@cli : This command is useful only when G'MIC is used by an embedding application.
22356
22357#@cli q : eq. to 'quit'. : (+)
22358
22359#@cli quit : (+)
22360#@cli : Quit G'MIC interpreter.
22361#@cli : (eq. to 'q').
22362
22363#@cli repeat : nb_iterations,_variable_name : (+)
22364#@cli : Start 'nb_iterations' iterations of a 'repeat...done' block.
22365#@cli : 'nb_iterations' is a mathematical expression that will be evaluated.
22366#@cli : $ image.jpg split y repeat $!,n shift[$n] $<,0,0,0,2 done append y
22367#@cli : $ image.jpg mode3d 2 repeat 4 imagecube3d rotate3d 1,1,0,40 snapshot3d 400,1.4 done
22368#@cli : $$ https://gmic.eu/oldtutorial/_repeat
22369
22370#@cli return : (+)
22371#@cli : Return from current custom command.
22372
22373#@cli rprogress : 0<=value<=100 | -1 | "command",0<=value_min<=100,0<=value_max<=100
22374#@cli : Set the progress index of the current processing pipeline (relatively to
22375#@cli : previously defined progress bounds), or call the specified command with
22376#@cli : specified progress bounds.
22377rprogress : skip ${2=""}
22378  if !narg($_progress_bounds) _progress_bounds=0,100 fi
22379  m={arg(-2,$_progress_bounds)} M={arg(-1,$_progress_bounds)}
22380  if $#==2&&!narg($2) # 1 argument -> Set progress bar.
22381    e[0--3] "Set relative progress index to $1%."
22382    progress {if($1<0,-1,min(100,max(0,$m+($M-$m)*$1%)))}
22383  elif $#==3 # 3 arguments -> Call command with specified bounds.
22384    nm={min($2,$-1)} nM={max($2,$-1)}
22385    e[0--3] "Call command '$1' with progress bounds ["$nm,$nM"]."
22386    progress $m _progress_bounds=$_progress_bounds,{$m+$nm*($M-$m)/100},{$m+$nM*($M-$m)/100}  # Push new bounds.
22387    run "$1"
22388    progress $M ($_progress_bounds) _progress_bounds={@0--3} rm. # Pop bounds.
22389  else error[0--3] "Command '$0': Invalid argument '$*'."
22390  fi
22391
22392#@cli run : "G'MIC pipeline"
22393#@cli : Run specified G'MIC pipeline.
22394#@cli : This is only useful when used from a shell, e.g. to avoid shell substitutions to happen in argument.
22395run :
22396  $*
22397
22398#@cli skip : item : (+)
22399#@cli : Do nothing but skip specified item.
22400
22401#@cli u : eq. to 'status'. : (+)
22402
22403#@cli status : status_string : (+)
22404#@cli : Set the current status. Used to define a returning value from a function.
22405#@cli : (eq. to 'u').
22406#@cli : $ image.jpg command "foo : u0=Dark u1=Bright status ${u{ia>=128}}" text_outline ${-foo},2,2,23,2,1,255
22407
22408#@cli while : condition : (+)
22409#@cli : End a 'do...while' block and go back to associated 'do' if specified condition holds.
22410#@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0=false | other=true }.
22411
22412#-------------------------
22413#
22414#@cli :: Neural Networks
22415#
22416#-------------------------
22417
22418#@cli nn_lib :
22419#@cli : Return the list of library functions that has to be included in a math expression,\
22420# in order to use the neural network library.
22421nn_lib :
22422  u "
22423    #---------------------------------------------------------------------------
22424    # Definition of the library environment variables and convenience functions.
22425    #---------------------------------------------------------------------------
22426    begin(
22427      const nn_nb_threads_max = n;
22428    );
22429
22430    begin_t(
22431      nn_thread = t;
22432      nn_nb_threads_used = nn_thread + 1;
22433      nn_nb_samples = 0;
22434    );
22435
22436    ++nn_nb_samples;
22437
22438    end(
22439      merge(nn_nb_threads_used,max);
22440      merge(nn_nb_samples,+);
22441    );
22442
22443    nn_display(L) = display(L#_out,L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum);
22444    nn_display_deriv(L) = display(L#_deriv_out,L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum);
22445
22446    #--------------------------------------------
22447    # Activation functions and their derivatives.
22448    #--------------------------------------------
22449    _nn_activate_pointwise(Z,activation) = (fill(#Z,k,nn_activation_#activation(#Z[k])));
22450    nn_activate(Z,activation) = nn_activate_#activation(#Z);
22451
22452    # ELU: Exponential Linear Unit.
22453    #-------------------------------
22454    nn_activation_elu(z) = (z<0?exp(z) - 1:z);
22455    nn_activation_deriv_elu(z) = (z<0?exp(z):1);
22456    nn_activate_elu(Z) = _nn_activate_pointwise(Z,elu);
22457    nn_activate_deriv_elu(Z) = _nn_activate_pointwise(Z,deriv_elu);
22458
22459    # GELU: Gaussian Error Linear Unit.
22460    #-----------------------------------
22461    nn_activation_gelu(z) = (0.5*z*(1 + erf(z/sqrt(2))));
22462    nn_activation_deriv_gelu(z) = (0.5 + 0.5*erf(z/sqrt(2)) + z*exp(-z^2/2)/sqrt(2*pi));
22463    nn_activate_gelu(Z) = _nn_activate_pointwise(Z,gelu);
22464    nn_activate_deriv_gelu(Z) = _nn_activate_pointwise(Z,deriv_gelu);
22465
22466    # LeakyRELU: Leaky Rectified Linear Unit.
22467    #-----------------------------------------
22468    nn_activation_leakyrelu(z) = (z<0?0.05*z:z);
22469    nn_activation_deriv_leakyrelu(z) = (z<0?0.05:1);
22470    nn_activate_leakyrelu(Z) = _nn_activate_pointwise(Z,leakyrelu);
22471    nn_activate_deriv_leakyrelu(Z) = _nn_activate_pointwise(Z,deriv_leakyrelu);
22472
22473    # Linear.
22474    #--------
22475    nn_activate_linear(Z) = (0);
22476    nn_activate_deriv_linear(Z) = (#Z = 1);
22477
22478    # RELU: Rectified Linear Unit.
22479    #------------------------------
22480    nn_activation_relu(z) = (z<0?0:z);
22481    nn_activation_deriv_relu(z) = (z<0?0:1);
22482    nn_activate_relu(Z) = _nn_activate_pointwise(Z,relu);
22483    nn_activate_deriv_relu(Z) = _nn_activate_pointwise(Z,deriv_relu);
22484
22485    # Sigmoid.
22486    #---------
22487    nn_activation_sigmoid(z) = (0.5 + 0.5*tanh(z/2));
22488    nn_activation_deriv_sigmoid(z) = (_nn_sig = nn_activation_sigmoid(z); _nn_sig*(1-_nn_sig));
22489    nn_activate_sigmoid(Z) = _nn_activate_pointwise(Z,sigmoid);
22490    nn_activate_deriv_sigmoid(Z) = _nn_activate_pointwise(Z,deriv_sigmoid);
22491
22492    # Sqr: Square.
22493    #-------------
22494    nn_activate_sqr(Z) = (Z*=Z);
22495    nn_activate_deriv_sqr(Z) = (Z*=2);
22496
22497    # Sqrt: sqrt(|z|).
22498    #-----------------
22499    nn_activation_sqrt(z) = (sqrt(abs(z)));
22500    nn_activation_deriv_sqrt(z) = (0.5*sign(x)/sqrt(max(1e-8,abs(z))));
22501    nn_activate_sqrt(Z) = _nn_activate_pointwise(Z,sqrt);
22502    nn_activate_deriv_sqrt(Z) = _nn_activate_pointwise(Z,deriv_sqrt);
22503
22504    # Swish.
22505    #--------
22506    nn_activation_swish(z) = (z*nn_activation_sigmoid(z));
22507    nn_activation_deriv_swish(z) = (
22508      _nn_sig = nn_activation_sigmoid(z);
22509      _nn_swi = z*_nn_sig;
22510      _nn_swi + _nn_sig*(1 - _nn_swi)
22511    );
22512    nn_activate_swish(Z) = _nn_activate_pointwise(Z,swish);
22513    nn_activate_deriv_swish(Z) = _nn_activate_pointwise(Z,deriv_swish);
22514
22515    # Tanh.
22516    #------
22517    nn_activation_tanh(z) = (tanh(z));
22518    nn_activation_deriv_tanh(z) = (1 - tanh(z)^2);
22519    nn_activate_tanh(Z) = _nn_activate_pointwise(Z,tanh);
22520    nn_activate_deriv_tanh(Z) = _nn_activate_pointwise(Z,deriv_tanh);
22521
22522    #---------
22523    # Trainer.
22524    #---------
22525    nn_trainer_init_backward(T,L,O,S) = (
22526      const T#_ind = $T#;
22527      nn_iteration = i[##T#_ind,0];
22528      nn_learning_rate = i[##T#_ind,1];
22529      nn_learning_rate0 = i[##T#_ind,2];
22530      nn_previous_loss = i[##T#_ind,3];
22531      nn_best_loss = i[##T#_ind,4];
22532
22533      const nn_optimizer = O;
22534      const nn_scheduler = S;
22535      nn_scheduler==3?nn_scheduler_adaptive_init(#T);
22536      nn_optimizer==1?nn_optimizer_rmsprop_init():
22537      nn_optimizer==2?nn_optimizer_adam_init():
22538      nn_optimizer==3?nn_optimizer_adamax_init();
22539    );
22540
22541    nn_trainer_update(T,L) = (
22542      nn_scheduler==1?nn_scheduler_linear_update():
22543      nn_scheduler==2?nn_scheduler_exponential_update():
22544      nn_scheduler==3?nn_scheduler_adaptive_update(#T,#L);
22545      i[##T#_ind,0] = ++nn_iteration;
22546      i[##T#_ind,1] = nn_learning_rate;
22547      i[##T#_ind,3] = L#_out;
22548      i[##T#_ind,4] = min(nn_best_loss,L#_out);
22549    );
22550
22551    #-------------
22552    # Schedulers.
22553    #-------------
22554
22555    # Linear Decrease.
22556    #-----------------
22557    nn_scheduler_linear_update() = (
22558      nn_learning_rate = lerp(nn_learning_rate0,1e-8,min(1,nn_iteration/1000));
22559    );
22560
22561    # Exponential Decrease.
22562    #----------------------
22563    nn_scheduler_exponential_update() = (
22564      nn_learning_rate*=0.999;
22565    );
22566
22567    # Adaptive Learning Rate.
22568    #------------------------
22569    nn_scheduler_adaptive_init(T) = (
22570      nn_adaptive_trend_moment = i(##T#_ind,0,2,0,0);
22571      nn_adaptive_nb_decreases = i(##T#_ind,1,2,0,0);
22572      nn_adaptive_nb_increases = i(##T#_ind,2,2,0,0);
22573    );
22574
22575    nn_scheduler_adaptive_update(T,L) = (
22576      nn_iteration?(
22577        # Compute trend: percentage of loss decrease(<0) or increase(>0).
22578        nn_adaptive_trend = (L#_out - nn_previous_loss)/max(1e-8,nn_previous_loss);
22579        nn_adaptive_trend_moment = lerp(sign(nn_adaptive_trend),nn_adaptive_trend_moment,0.75);
22580
22581        # Adapt strategy regarding to trend.
22582        nn_adaptive_trend>=0.3?( # Large local loss increase -> drastically reduce learning rate
22583          nn_learning_rate = max(0.25*nn_learning_rate,1e-11);
22584          nn_adaptive_nb_increases = nn_adaptive_nb_decreases = 0;
22585        ):nn_adaptive_trend_moment>=0?( # Global loss increase
22586          ++nn_adaptive_nb_increases;
22587          nn_adaptive_nb_decreases = 0;
22588          nn_adaptive_nb_increases>=2?( # Global loss increase -> slightly reduce learning rate
22589            nn_learning_rate = max(0.75*nn_learning_rate,1e-11);
22590            nn_adaptive_nb_increases = 0;
22591          );
22592        ):nn_adaptive_trend_moment<0?( # Global loss decrease
22593          ++nn_adaptive_nb_decreases;
22594          nn_adaptive_nb_decreases>=4?( # Global loss decrease -> slightly increase learning rate
22595            nn_learning_rate = min(nn_learning_rate*1.15,0.1);
22596            nn_adaptive_nb_decreases = 0;
22597          );
22598        );
22599      );
22600      copy(i(##T#_ind,0,2,0,0),[ nn_adaptive_trend_moment,nn_adaptive_nb_decreases,nn_adaptive_nb_increases ]);
22601    );
22602
22603    #------------
22604    # Optimizers.
22605    #------------
22606    nn_optimizer_update_layer(L,dL) = (
22607      !nn_optimizer?nn_optimizer_sgd_update_layer(#L,#dL):       # Stochastic gradient descent
22608      nn_optimizer==1?nn_optimizer_rmsprop_update_layer(#L,#dL): # RMSprop
22609      nn_optimizer==2?nn_optimizer_adam_update_layer(#L,#dL):    # Adam
22610      nn_optimizer==3?nn_optimizer_adamax_update_layer(#L,#dL);  # Adamax
22611    );
22612
22613    # Stochastic gradient descent.
22614    #-----------------------------
22615    nn_optimizer_sgd_update_layer(L_params,dL_params) = (
22616      L_params#-=nn_learning_rate*#dL_params;
22617    );
22618
22619    # RMSprop optimizer.
22620    #-------------------
22621    nn_optimizer_rmsprop_init() = (
22622      const nn_optimizer_rmsprop_beta = 0.9;
22623    );
22624
22625    nn_optimizer_rmsprop_update_layer(L_params,dL_params) = (
22626      dL_params#_name_g2 = '_nn_#dL_params#_g2';
22627      !nn_iteration || isnan($_nn_#dL_params#_g2)?(
22628        dL_params#_g2 = vector(#size(dL_params));
22629      ):(
22630        dL_params#_g2 = get(dL_params#_name_g2,size(dL_params));
22631      );
22632      dL_params#_g2 = lerp(dL_params^2,dL_params#_g2,nn_optimizer_rmsprop_beta);
22633      store(dL_params#_g2,dL_params#_name_g2,size(dL_params));
22634      L_params#-=nn_learning_rate*dL_params/sqrt(1e-8 + dL_params#_g2);
22635    );
22636
22637    # Adam optimizer.
22638    #----------------
22639    nn_optimizer_adam_init() = (
22640      begin(
22641        const nn_optimizer_adam_beta1 = 0.9;
22642        const nn_optimizer_adam_beta2 = 0.999;
22643        nn_optimizer_adam_beta1_t = nn_iteration>200?0:nn_optimizer_adam_beta1^(nn_iteration + 1);
22644        nn_optimizer_adam_beta2_t = nn_iteration>200?0:nn_optimizer_adam_beta2^(nn_iteration + 1);
22645        nn_optimizer_adam_alpha_t = sqrt(1 - nn_optimizer_adam_beta2_t)/(1 - nn_optimizer_adam_beta1_t);
22646      );
22647    );
22648
22649    nn_optimizer_adam_update_layer(L_params,dL_params) = (
22650      dL_params#_name_m = '_nn_#dL_params#_m';
22651      dL_params#_name_v = '_nn_#dL_params#_v';
22652      !nn_iteration || isnan($_nn_#dL_params#_m)?(
22653        dL_params#_m = dL_params#_v = vector(#size(dL_params));
22654      ):(
22655        dL_params#_m = get(dL_params#_name_m,size(dL_params));
22656        dL_params#_v = get(dL_params#_name_v,size(dL_params));
22657      );
22658      dL_params#_m = lerp(dL_params,dL_params#_m,nn_optimizer_adam_beta1);
22659      dL_params#_v = lerp(dL_params^2,dL_params#_v,nn_optimizer_adam_beta2);
22660      store(dL_params#_m,dL_params#_name_m,size(dL_params));
22661      store(dL_params#_v,dL_params#_name_v,size(dL_params));
22662      L_params#-=nn_learning_rate*nn_optimizer_adam_alpha_t*dL_params#_m/sqrt(1e-8 + dL_params#_v);
22663    );
22664
22665    # Adamax optimizer.
22666    #------------------
22667    nn_optimizer_adamax_init() = (
22668      begin(
22669        const nn_optimizer_adamax_beta1 = 0.9;
22670        const nn_optimizer_adamax_beta2 = 0.999;
22671        const nn_optimizer_adamax_ombeta1_t = nn_iteration>200?1:(1 - nn_optimizer_adamax_beta1^(nn_iteration + 1));
22672      );
22673    );
22674
22675    nn_optimizer_adamax_update_layer(L_params,dL_params) = (
22676      dL_params#_name_m = '_nn_#dL_params#_m';
22677      dL_params#_name_v = '_nn_#dL_params#_v';
22678      !nn_iteration || isnan($_nn_#dL_params#_m)?(
22679        dL_params#_m = dL_params#_v = vector(#size(dL_params));
22680      ):(
22681        dL_params#_m = get(dL_params#_name_m,size(dL_params));
22682        dL_params#_v = get(dL_params#_name_v,size(dL_params));
22683      );
22684      dL_params#_m = lerp(dL_params,dL_params#_m,nn_optimizer_adamax_beta1);
22685      dL_params#_v = vmax(abs(dL_params),nn_optimizer_adamax_beta2*dL_params#_v);
22686      store(dL_params#_m,dL_params#_name_m,size(dL_params));
22687      store(dL_params#_v,dL_params#_name_v,size(dL_params));
22688
22689      L_params#-=(nn_learning_rate/nn_optimizer_adamax_ombeta1_t)*dL_params#_m/sqrt(1e-8 + dL_params#_v);
22690    );
22691
22692    #----------------
22693    # Network layers.
22694    #----------------
22695
22696    # add: Add two inputs.
22697    #---------------------
22698    nn_layer_add_init_forward(L,IN) = (
22699      const L#_out_width = IN#_out_width;
22700      const L#_out_height = IN#_out_height;
22701      const L#_out_depth = IN#_out_depth;
22702      const L#_out_spectrum = IN#_out_spectrum;
22703    );
22704
22705    nn_layer_add_forward(L,IN0,IN1) = (
22706      L#_out = IN0#_out + IN1#_out;
22707    );
22708
22709    nn_layer_add_backward(L,IN0,IN1) = (
22710      IN0#_deriv_out = IN1#_deriv_out = L#_deriv_out/2;
22711    );
22712
22713    # append: Append two inputs as a new output.
22714    #--------------------------------------------
22715    nn_layer_append_init_forward(L,IN0,IN1) = (
22716      const L#_out_width = IN0#_out_width;
22717      const L#_out_height = IN0#_out_height;
22718      const L#_out_depth = IN0#_out_depth;
22719      const L#_out_spectrum = IN0#_out_spectrum + IN1#_out_spectrum;
22720    );
22721
22722    nn_layer_append_forward(L,IN0,IN1) = (
22723      L#_out = vector(#size(IN0#_out) + size(IN1#_out));
22724      copy(L#_out,IN0#_out,size(IN0#_out));
22725      copy(L#_out[size(IN0#_out)],IN1#_out,size(IN1#_out));
22726    );
22727
22728    nn_layer_append_backward(L,IN0,IN1) = (
22729      IN0#_deriv_out = L#_deriv_out[0,size(IN0#_out)];
22730      IN1#_deriv_out = L#_deriv_out[size(IN0#_out),size(IN1#_out)];
22731    );
22732
22733    # avgpool2d : 2D average pooling.
22734    #--------------------------------
22735    nn_layer_avgpool2d_init_forward(L,IN) = (
22736      const L#_out_width = int(IN#_out_width/2);
22737      const L#_out_height = int(IN#_out_height/2);
22738      const L#_out_depth = IN#_out_depth;
22739      const L#_out_spectrum = IN#_out_spectrum;
22740      nn_avgpool2d_kernel_forward = vector4(0.25);
22741      nn_avgpool2d_kernel_backward = [ 1,0,0,0 ];
22742    );
22743
22744    nn_layer_avgpool2d_forward(L,IN) = (
22745      L#_out = correlate(IN#_out,
22746                         IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
22747                         nn_avgpool2d_kernel_forward,2,2,1,1,
22748                         1,0,1,
22749                         0,0,0,
22750                         0,0,0,
22751                         L#_out_width - 1,L#_out_height - 1,L#_out_depth - 1,
22752                         2,2,1);
22753    );
22754
22755    nn_layer_avgpool2d_backward(L,IN) = (
22756      IN#_deriv_out = correlate(L#_deriv_out,
22757                                L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
22758                                nn_avgpool2d_kernel_backward,2,2,1,1,
22759                                0,0,1,
22760                                0,0,0,
22761                                0,0,0,
22762                                IN#_out_width - 1,IN#_out_height - 1,IN#_out_depth - 1,
22763                                0.5,0.5,1,
22764                                1,1,1,
22765                                0);
22766    );
22767
22768    # batchnorm: Batch-normalization layer.
22769    #---------------------------------------
22770    nn_layer_batchnorm_init_forward(L,IN) = (
22771      const L#_ind = $L#;
22772      const L#_out_width = IN#_out_width;
22773      const L#_out_height = IN#_out_height;
22774      const L#_out_depth = IN#_out_depth;
22775      const L#_out_spectrum = IN#_out_spectrum;
22776      const L#_out_whd = L#_out_width*L#_out_height*L#_out_depth;
22777      L#_mu = crop(##L#_ind,0,0,1,h##L#_ind);
22778      L#_sigma = crop(##L#_ind,1,0,1,h##L#_ind);
22779      L#_gamma = crop(##L#_ind,2,0,1,h##L#_ind);
22780      L#_beta = crop(##L#_ind,3,0,1,h##L#_ind);
22781      L#_out = vector(#size(IN#_out));
22782      L#_mu_whds = resize(L#_mu,size(L#_out),1);
22783      L#_sigma_whds = resize(sqrt(L#_sigma + 1e-9),size(L#_out),1);
22784      L#_gamma_whds = resize(L#_gamma,size(L#_out),1);
22785      L#_beta_whds = resize(L#_beta,size(L#_out),1);
22786    );
22787
22788    nn_layer_batchnorm_forward(L,IN) = (
22789      L#_xhat = IN#_out;
22790      L#_xhat-=L#_mu_whds;
22791      L#_xhat/=L#_sigma_whds;
22792      L#_out = L#_gamma_whds*L#_xhat;
22793      L#_out+=L#_beta_whds;
22794    );
22795
22796    nn_layer_batchnorm_init_backward(L) = (
22797      L#_threads_nbs = vector(#nn_nb_threads_max);
22798      L#_threads_mus = vector(#nn_nb_threads_max*L#_out_spectrum);
22799      L#_threads_sigmas = vector(#nn_nb_threads_max*L#_out_spectrum);
22800      L#_batch_deriv_gamma = vector(##L#_out_spectrum);
22801      L#_batch_deriv_beta = vector(##L#_out_spectrum);
22802    );
22803
22804    nn_layer_batchnorm_backward(L,IN,learning_mode) = (
22805      L#_nb1 = L#_threads_nbs[nn_thread];
22806      L#_mu1 = L#_threads_mus[nn_thread*L#_out_spectrum,L#_out_spectrum];
22807      L#_sigma1 = L#_threads_sigmas[nn_thread*L#_out_spectrum,L#_out_spectrum];
22808      L#_mu2 = vector(##L#_out_spectrum);
22809      L#_sigma2 = vector(##L#_out_spectrum);
22810      repeat (L#_out_spectrum,_k,
22811        L#_feature = IN#_out[_k*L#_out_whd,L#_out_whd];
22812        L#_mu2[_k] = avg(L#_feature);
22813        L#_sigma2[_k] = var(L#_feature);
22814      );
22815      L#_nbc = ++L#_threads_nbs[nn_thread];
22816      L#_in_mean = L#_mu1 + (L#_mu2 - L#_mu1)/L#_nbc;
22817      L#_in_variance = L#_sigma1 + (L#_sigma2 - L#_sigma1 +
22818                                    L#_nb1*(L#_mu1 - L#_in_mean)^2 + (L#_mu2 - L#_in_mean)^2)/L#_nbc;
22819      copy(L#_threads_mus[nn_thread*L#_out_spectrum],L#_in_mean);
22820      copy(L#_threads_sigmas[nn_thread*L#_out_spectrum],L#_in_variance);
22821
22822      learning_mode&1?( # Learn gamma
22823        L#_tmp = L#_deriv_out*L#_xhat;
22824        repeat(L#_out_spectrum,_k,L#_batch_deriv_gamma[_k]+=sum(L#_tmp[_k*L#_out_whd,L#_out_whd]));
22825      );
22826      learning_mode&2?( # Learn beta
22827        repeat(L#_out_spectrum,_k,L#_batch_deriv_beta[_k]+=sum(L#_deriv_out[_k*L#_out_whd,L#_out_whd]));
22828      );
22829
22830      IN#_deriv_out = L#_deriv_out*L#_gamma_whds;
22831      IN#_deriv_out/=L#_sigma_whds;
22832    );
22833
22834    nn_layer_batchnorm_end_backward(L) = (
22835      merge(L#_batch_deriv_gamma,+);
22836      merge(L#_batch_deriv_beta,+);
22837      L#_batch_deriv_gamma/=nn_nb_samples;
22838      L#_batch_deriv_beta/=nn_nb_samples;
22839
22840      merge(L#_threads_nbs,+);
22841      merge(L#_threads_mus,+);
22842      merge(L#_threads_sigmas,+);
22843      L#_nbc = 0;
22844      L#_in_mean = 0;
22845      L#_in_variance = 0;
22846      repeat (size(L#_threads_nbs),_k,
22847        L#_nb1 = L#_nbc;
22848        L#_mu1 = L#_in_mean;
22849        L#_sigma1 = L#_in_variance;
22850        L#_nb2 = L#_threads_nbs[_k];
22851        L#_mu2 = L#_threads_mus[_k*size(L#_mu2),size(L#_mu2)];
22852        L#_sigma2 = L#_threads_sigmas[_k*size(L#_mu2),size(L#_mu2)];
22853        L#_nbc = L#_nb1 + L#_nb2;
22854        L#_in_mean = (L#_nb1*L#_mu1 + L#_nb2*L#_mu2)/L#_nbc;
22855        L#_in_variance = (L#_nb1*(L#_sigma1 + (L#_mu1 - L#_in_mean)^2) +
22856                          L#_nb2*(L#_sigma2 + (L#_mu2 - L#_in_mean)^2))/L#_nbc;
22857      );
22858    );
22859
22860    nn_layer_batchnorm_update(L,learning_mode) = (
22861      L#_momentum = lerp(1,1e-5,tanh(nn_iteration/100));
22862      draw(##L#_ind,L#_in_mean,0,0,0,0,1,h##L#_ind,1,1,L#_momentum);
22863      draw(##L#_ind,L#_in_variance,1,0,0,0,1,h##L#_ind,1,1,L#_momentum);
22864      learning_mode&1?(
22865        nn_optimizer_update_layer(L#_gamma,L#_batch_deriv_gamma);
22866        draw(##L#_ind,L#_gamma,2,0,0,0,1,h##L#_ind);
22867      );
22868      learning_mode&2?(
22869        nn_optimizer_update_layer(L#_beta,L#_batch_deriv_beta);
22870        draw(##L#_ind,L#_beta,3,0,0,0,1,h##L#_ind);
22871      );
22872    );
22873
22874    # clone: Duplicate input as two new outputs.
22875    #-------------------------------------------
22876    nn_layer_clone_init_forward(L0,L1,IN) = (
22877      const L0#_out_width = IN#_out_width;
22878      const L0#_out_height = IN#_out_height;
22879      const L0#_out_depth = IN#_out_depth;
22880      const L0#_out_spectrum = IN#_out_spectrum;
22881      const L1#_out_width = IN#_out_width;
22882      const L1#_out_height = IN#_out_height;
22883      const L1#_out_depth = IN#_out_depth;
22884      const L1#_out_spectrum = IN#_out_spectrum;
22885    );
22886
22887    nn_layer_clone_forward(L0,L1,IN) = (
22888      L0#_out = IN#_out;
22889      L1#_out = IN#_out;
22890    );
22891
22892    nn_layer_clone_backward(L0,L1,IN) = (
22893      IN#_deriv_out = L0#_deriv_out + L1#_deriv_out;
22894    );
22895
22896    # conv2d: 2D convolutional layer.
22897    #--------------------------------
22898    nn_layer_conv2d_init_forward(L,IN,size,stride,dilation) = (
22899      const L#_ind = $L#;
22900      const L#_out_width = max(1,int(IN#_out_width/stride));
22901      const L#_out_height = max(1,int(IN#_out_height/stride));
22902      const L#_out_depth = IN#_out_depth;
22903      const L#_out_spectrum = h##L#_ind;
22904      const L#_kernel_size = size;
22905      const L#_kernel_center = L#_kernel_size - 1 - int(L#_kernel_size/2);
22906      const L#_stride = stride;
22907      const L#_dilation = dilation;
22908      L#_weights = crop(##L#_ind,0,0,w##L#_ind - 1,h##L#_ind);
22909      L#_biases = crop(##L#_ind,w##L#_ind - 1,0,1,h##L#_ind);
22910      L#_out = vector(##L#_out_width*L#_out_height*L#_out_depth*h##L#_ind);
22911    );
22912
22913    nn_layer_conv2d_forward(L,IN) = (
22914      unref(_sizl);
22915      const _sizl = size(L#_out)/L#_out_spectrum; # Size of output plane
22916      L#_out = convolve(IN#_out,
22917                        IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
22918                        L#_weights,
22919                        L#_kernel_size,L#_kernel_size,1,IN#_out_spectrum*L#_out_spectrum,
22920                        1,0,2,
22921                        L#_kernel_center,L#_kernel_center,0,
22922                        0,0,0,
22923                        L#_out_width - 1,L#_out_height - 1,L#_out_depth - 1,
22924                        L#_stride,L#_stride,1,
22925                        L#_dilation,L#_dilation,1);
22926      repeat (L#_out_spectrum,_l,
22927        copy(L#_out[_l*_sizl],L#_biases[_l],_sizl,1,0,-1); # Add biases
22928      );
22929    );
22930
22931    nn_layer_conv2d_init_backward(L,IN) = (
22932      L#_batch_deriv_weights = vector(#size(L#_weights));
22933      L#_batch_deriv_biases = vector(#size(L#_biases));
22934    );
22935
22936    nn_layer_conv2d_backward(L,IN) = (
22937      unref(_sizk,_sizl,_sizw);
22938      const _sizk = size(IN#_out)/IN#_out_spectrum; # Size of input plane
22939      const _sizl = size(L#_out)/L#_out_spectrum;   # Size of output plane
22940      const _sizw = L#_kernel_size^2;               # Size of kernel^2
22941
22942      # Compute dL/dxk(x,y).
22943      L#_weights_mirrored = vector(#size(L#_weights));
22944      repeat (IN#_out_spectrum,_k,
22945        repeat (L#_out_spectrum,_l,
22946          copy(L#_weights_mirrored[_k*L#_out_spectrum*_sizw + _l*_sizw + _sizw - 1],
22947               L#_weights[_l*(w##L#_ind - 1) + _k*_sizw],_sizw,-1,1);
22948        );
22949      );
22950      IN#_deriv_out = convolve(L#_deriv_out,
22951                               L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
22952                               L#_weights_mirrored,
22953                               L#_kernel_size,L#_kernel_size,1,L#_out_spectrum*IN#_out_spectrum,
22954                               0,0,2,
22955                               L#_kernel_size - 1 - L#_kernel_center,
22956                               L#_kernel_size - 1 - L#_kernel_center,0,
22957                               0,0,0,IN#_out_width - 1,IN#_out_height - 1,IN#_out_depth - 1,
22958                               1/L#_stride,1/L#_stride,1,
22959                               L#_dilation/L#_stride,L#_dilation/L#_stride,1);
22960
22961      # Compute dL/dbl.
22962      L#_deriv_biases = vector(#size(L#_biases));
22963      fill(L#_deriv_biases,_l,sum(L#_deriv_out[_l*_sizl,_sizl]));
22964      L#_batch_deriv_biases+=L#_deriv_biases;
22965
22966      # Compute dL/dwlk.
22967      L#_xk_mirrored = vector(#_sizk);
22968      L#_deriv_weights = vector(#size(L#_weights));
22969      repeat (L#_out_spectrum,_l,
22970        repeat (IN#_out_spectrum,_k,
22971          copy(L#_xk_mirrored[_sizk - 1],IN#_out[_k*_sizk],_sizk,-1,1);
22972          L#_dwlk = convolve(L#_xk_mirrored,
22973                             IN#_out_width,IN#_out_height,IN#_out_depth,1,
22974                             L#_deriv_out[_l*_sizl,_sizl],
22975                             L#_out_width,L#_out_height,L#_out_depth,1,
22976                             0,0,1,
22977                             1/L#_stride*(IN#_out_width - 1 - L#_dilation*L#_kernel_center),
22978                             1/L#_stride*(IN#_out_height - 1 - L#_dilation*L#_kernel_center),0,
22979                             0,0,0,L#_kernel_size - 1,L#_kernel_size - 1,0,
22980                             L#_dilation,L#_dilation,1,
22981                             L#_stride,L#_stride,1);
22982          copy(L#_deriv_weights[_l*(w##L#_ind - 1) + _k*_sizw],L#_dwlk,_sizw);
22983        );
22984      );
22985      L#_batch_deriv_weights+=L#_deriv_weights;
22986    );
22987
22988    nn_layer_conv2d_end_backward(L) = (
22989      merge(L#_batch_deriv_weights,+);
22990      merge(L#_batch_deriv_biases,+);
22991      L#_batch_deriv_weights/=nn_nb_samples;
22992      L#_batch_deriv_biases/=nn_nb_samples;
22993    );
22994
22995    nn_layer_conv2d_update(L) = (
22996      nn_optimizer_update_layer(L#_weights,L#_batch_deriv_weights);
22997      nn_optimizer_update_layer(L#_biases,L#_batch_deriv_biases);
22998      draw(##L#_ind,L#_weights,0,0,0,0,w##L#_ind - 1,h##L#_ind);
22999      draw(##L#_ind,L#_biases,w##L#_ind - 1,0,0,0,1,h##L#_ind);
23000    );
23001
23002    # crop : Crop layer.
23003    #--------------------
23004    nn_layer_crop_init_forward(L,IN,x0,y0,z0,x1,y1,z1) = (
23005      const L#_out_width = abs(x1 - x0) + 1;
23006      const L#_out_height = abs(y1 - y0) + 1;
23007      const L#_out_depth = abs(z1 - z0) + 1;
23008      const L#_out_spectrum = IN#_out_spectrum;
23009    );
23010
23011    nn_layer_crop_forward(L,IN,x0,y0,z0,x1,y1,z1) = (
23012      L#_out = convolve(IN#_out,IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
23013                        [1],1,1,1,1,
23014                        0,0,1,
23015                        0,0,0,
23016                        x0,y0,z0,
23017                        x1,y1,z1);
23018    );
23019
23020    nn_layer_crop_backward(L,IN,x0,y0,z0,x1,y1,z1) = (
23021      IN#_deriv_out = convolve(L#_deriv_out,L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
23022                               [1],1,1,1,1,
23023                               0,0,1,
23024                               0,0,0,
23025                               -x0,-y0,-z0,
23026                               -x0 + IN#_out_width - 1,-y0 + IN#_out_height - 1,-z0 + IN#_out_depth - 1);
23027    );
23028
23029    # fc: Fully-connected layer.
23030    #---------------------------
23031    nn_layer_fc_init_forward(L) = (
23032      const L#_ind = $L#;
23033      const L#_out_width = 1;
23034      const L#_out_height = 1;
23035      const L#_out_depth = 1;
23036      const L#_out_spectrum = h##L#_ind;
23037      L#_weights = crop(##L#_ind,0,0,w##L#_ind - 1,h##L#_ind);
23038      L#_biases = crop(##L#_ind,w##L#_ind - 1,0,1,h##L#_ind);
23039    );
23040
23041    nn_layer_fc_forward(L,IN) = (
23042      L#_out = mul(L#_weights,IN#_out);
23043      L#_out+=L#_biases;
23044    );
23045
23046    nn_layer_fc_init_backward(L) = (
23047      L#_batch_deriv_weights = vector(#size(L#_weights));
23048      L#_batch_deriv_biases = vector(#size(L#_biases));
23049    );
23050
23051    nn_layer_fc_backward(L,IN) = (
23052      IN#_deriv_out = mul(transpose(L#_weights,size(IN#_out)),L#_deriv_out);
23053      L#_deriv_weights = mul(L#_deriv_out,IN#_out,size(IN#_out));
23054      L#_batch_deriv_weights+=L#_deriv_weights;
23055      L#_batch_deriv_biases+=L#_deriv_out;
23056    );
23057
23058    nn_layer_fc_end_backward(L) = (
23059      merge(L#_batch_deriv_weights,+);
23060      merge(L#_batch_deriv_biases,+);
23061      L#_batch_deriv_weights/=nn_nb_samples;
23062      L#_batch_deriv_biases/=nn_nb_samples;
23063    );
23064
23065    nn_layer_fc_update(L) = (
23066      nn_optimizer_update_layer(L#_weights,L#_batch_deriv_weights);
23067      nn_optimizer_update_layer(L#_biases,L#_batch_deriv_biases);
23068      draw(##L#_ind,L#_weights,0,0,0,0,w##L#_ind - 1,h##L#_ind);
23069      draw(##L#_ind,L#_biases,w##L#_ind - 1,0,0,0,1,h##L#_ind);
23070    );
23071
23072    # input: Input image or vector data.
23073    #-----------------------------------
23074    nn_layer_input_init_forward(L,w,h,d,s) = (
23075      const L#_out_width = w;
23076      const L#_out_height = h;
23077      const L#_out_depth = d;
23078      const L#_out_spectrum = s;
23079      ref(#L,L#_out);
23080    );
23081
23082    # maxpool2d : 2D max pooling.
23083    #----------------------------
23084    nn_layer_maxpool2d_init_forward(L,IN) = (
23085      const L#_out_width = int(IN#_out_width/2);
23086      const L#_out_height = int(IN#_out_height/2);
23087      const L#_out_depth = IN#_out_depth;
23088      const L#_out_spectrum = IN#_out_spectrum;
23089      nn_avgpool2d_kernel_forward = [ 0.25,0.25,0.25,0.25 ];
23090      nn_avgpool2d_kernel_backward = [ 1,0,0,0 ];
23091    );
23092
23093    nn_layer_maxpool2d_forward(L,IN) = (
23094      L#_out = vector(##L#_out_width*L#_out_height*L#_out_depth*L#_out_spectrum);
23095      L#_from = vector(#size(L#_out));
23096      repeat (size(L#_out),_l,
23097        _x = _l%L#_out_width;
23098        _y = int(_l/L#_out_width)%L#_out_height;
23099        _z = int(_l/(L#_out_width*L#_out_height))%L#_out_depth;
23100        _c = int(_l/(L#_out_width*L#_out_height*L#_out_depth));
23101        _k = 2*_x + IN#_out_width*(2*_y + IN#_out_height*(_z + IN#_out_depth*_c));
23102        _k+=arg0(argmax([ IN#_out[_k,2],IN#_out[_k + IN#_out_width,2] ]),0,1,IN#_out_width,IN#_out_width + 1);
23103        L#_from[_l] = _k;
23104        L#_out[_l] = IN#_out[_k];
23105      );
23106    );
23107
23108    nn_layer_maxpool2d_backward(L,IN) = (
23109      IN#_deriv_out = vector(#size(IN#_out),0);
23110      repeat (size(L#_out),_l,
23111        IN#_deriv_out[L#_from[_l]] = L#_deriv_out[_l];
23112      );
23113    );
23114
23115    # nl: Non-linearity.
23116    #-------------------
23117    nn_layer_nl_init_forward(L,IN) = (
23118      const L#_out_width = IN#_out_width;
23119      const L#_out_height = IN#_out_height;
23120      const L#_out_depth = IN#_out_depth;
23121      const L#_out_spectrum = IN#_out_spectrum;
23122    );
23123
23124    nn_layer_nl_forward(L,IN,activation) = (
23125      L#_out = IN#_out;
23126      nn_activate(L#_out,#activation);
23127    );
23128
23129    nn_layer_nl_backward(L,IN,activation) = (
23130      IN#_deriv_out = IN#_out;
23131      nn_activate(IN#_deriv_out,deriv_#activation);
23132      IN#_deriv_out*=L#_deriv_out;
23133    );
23134
23135    # rename: Rename input.
23136    #----------------------
23137    nn_layer_rename_init_forward(L,IN) = (
23138      const L#_out_width = IN#_out_width;
23139      const L#_out_height = IN#_out_height;
23140      const L#_out_depth = IN#_out_depth;
23141      const L#_out_spectrum = IN#_out_spectrum;
23142      ref(IN#_out,L#_out);
23143    );
23144
23145    nn_layer_rename_backward(L,IN) = (
23146      ref(L#_deriv_out,IN#_deriv_out);
23147    );
23148
23149    # reshape: Reshape input to output with compatible size.
23150    #-------------------------------------------------------
23151    nn_layer_reshape_init_forward(L,IN,w,h,d,s) = (
23152      const L#_out_width = w;
23153      const L#_out_height = h;
23154      const L#_out_depth = d;
23155      const L#_out_spectrum = s;
23156      ref(IN#_out,L#_out);
23157    );
23158
23159    nn_layer_reshape_init_backward(L,IN) = (
23160      ref(L#_deriv_out,IN#_deriv_out);
23161    );
23162
23163    # resize: Resize input.
23164    #----------------------
23165    nn_layer_resize_init_forward(L,IN,w,h,d,s) = (
23166      const L#_out_width = w;
23167      const L#_out_height = h;
23168      const L#_out_depth = d;
23169      const L#_out_spectrum = s;
23170    );
23171
23172    nn_layer_resize_forward(L,IN,interpolation) = (
23173      L#_out = resize(IN#_out,
23174                      IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
23175                      L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
23176                      interpolation);
23177    );
23178
23179    nn_layer_resize_backward(L,IN) = (
23180      IN#_deriv_out = resize(L#_deriv_out,
23181                             L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
23182                             IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
23183                             3);
23184    );
23185
23186    # run: Transform input with G'MIC command.
23187    #-----------------------------------------
23188    nn_layer_run_init_forward(L,IN,command,w,h,d,s) = (
23189      const L#_out_width = w;
23190      const L#_out_height = h;
23191      const L#_out_depth = d;
23192      const L#_out_spectrum = s;
23193      L#_varname = 'nn_#L#_out';
23194      L#_pipeline = string('l[] $nn_#IN#_out ',command,' k. r ',
23195                           L#_out_width,',',L#_out_height,',',L#_out_depth,',',L#_out_spectrum,
23196                           ',3 k. store ',L#_varname,' endl');
23197    );
23198
23199    nn_layer_run_forward(L,IN) = (
23200      critical(
23201        store(IN#_out,'nn_#IN#_out',IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum);
23202        run(L#_pipeline);
23203        L#_out = get(L#_varname,L#_out_width*L#_out_height*L#_out_depth*L#_out_spectrum);
23204      );
23205    );
23206
23207    nn_layer_run_backward(L,IN) = (
23208      IN#_deriv_out = resize(L#_deriv_out,
23209                             L#_out_width,L#_out_height,L#_out_depth,L#_out_spectrum,
23210                             IN#_out_width,IN#_out_height,IN#_out_depth,IN#_out_spectrum,
23211                             3);
23212    );
23213
23214    # split: Split input as two outputs.
23215    #-----------------------------------
23216    nn_layer_split_init_forward(L0,L1,IN,nb_channels0) = (
23217      const L0#_out_width = IN#_out_width;
23218      const L0#_out_height = IN#_out_height;
23219      const L0#_out_depth = IN#_out_depth;
23220      const L0#_out_spectrum = nb_channels0;
23221      const L1#_out_width = IN#_out_width;
23222      const L1#_out_height = IN#_out_height;
23223      const L1#_out_depth = IN#_out_depth;
23224      const L1#_out_spectrum = IN#_out_spectrum - nb_channels0;
23225    );
23226
23227    nn_layer_split_forward(L0,L1,IN) = (
23228      unref(_siz0);
23229      const siz0 = L0#_out_width*L0#_out_height*L0#_out_depth*L0#_out_spectrum;
23230      L0#_out = IN#_out[0,siz0];
23231      L1#_out = IN#_out[siz0,L1#_out_width*L1#_out_height*L1#_out_depth*L1#_out_spectrum];
23232    );
23233
23234    nn_layer_split_backward(L0,L1,IN) = (
23235      IN#_deriv_out = vector(#size(IN#_out));
23236      copy(IN#_deriv_out,L0#_deriv_out,size(L0#_out));
23237      copy(IN#_deriv_out[size(L0#_out)],L1#_deriv_out,size(L1#_out));
23238    );
23239
23240    #----------------
23241    # Loss functions.
23242    #----------------
23243
23244    # mse : Mean-squared error.
23245    #--------------------------
23246    nn_loss_mse_init_backward(L) = (
23247      L#_batch_out = 0;
23248    );
23249
23250    nn_loss_mse_backward(L,IN,TRUTH) = (
23251      IN#_deriv_out = 2*(IN#_out - TRUTH);
23252      L#_out = norm(IN#_deriv_out/2)^2/size(IN#_deriv_out);
23253      L#_batch_out+=L#_out;
23254    );
23255
23256    nn_loss_mse_end_backward(L) = (
23257      merge(L#_batch_out,+);
23258      L#_batch_out/=nn_nb_samples;
23259      L#_out = L#_batch_out;
23260    );
23261    "
23262
23263#@cli nn_init
23264#@cli : Initialize a new network.
23265nn_init :
23266  e[^-1] "[nn_lib] Initialize new network."
23267  _nn_forward,_nn_backward,_nn_parameters=
23268  _nn_init="$0 "
23269
23270#@cli nn_check_layer : name
23271#@cli : Check that the layer with specified name exists in the network.
23272nn_check_layer :
23273  if !isvarname('"$1"')
23274    error[0--3] "nn_check_layer: Invalid layer name '$1'."
23275  elif narg($_nn_$1_out_size)!=4
23276    error[0--3] "nn_check_layer: Layer with name '$1' does not exist."
23277  fi
23278
23279#@cli nn_layer_input : name,width,_height,_depth,_spectrum
23280#@cli : Add an 'input' layer to the network.
23281#@cli : Default values: 'height=1', 'depth=1' and 'spectrum=1'.
23282nn_layer_input : check "isvarname('$1') && isint($2) && $2>0 && isint(${3=1}) && $3>0 && isint(${4=1}) && $4>0 && "\
23283                       "isint(${5=1}) && $5>0"
23284  e[^-1] "[nn_lib] Add input layer '$1', with size ($2,$3,$4,$5)."
23285  _nn_$1_out_size=$2,$3,$4,$5
23286  _nn_forward.="begin(nn_layer_input_init_forward($1,$2,$3,$4,$5));"
23287  _nn_init.="$0 $* "
23288
23289#@cli nn_layer_add : name,in0,in1
23290#@cli : Add an 'add' layer to the network.
23291nn_layer_add : nn_check_layer "$2" nn_check_layer "$3" check "isvarname('$1') && [$_nn_$2_out_size]==[$_nn_$3_out_size]"
23292  e[^-1] "[nn_lib] Add 'add' layer '$1', with inputs '$2' and '$3'."
23293  _nn_$1_out_size=$_nn_$2_out_size
23294  _nn_forward.="begin(nn_layer_add_init_forward($1,$2));"\
23295               "nn_layer_add_forward($1,$2,$3);"
23296  _nn_backward..="nn_layer_add_backward($1,$2,$3);"
23297  _nn_init.="$0 $* "
23298
23299#@cli nn_layer_append : name,in0,in1
23300#@cli : Add an 'append' layer to the network.
23301nn_layer_append : check "isvarname('$1')" nn_check_layer "$2" nn_check_layer "$3"
23302  e[^-1] "[nn_lib] Add 'append' layer '$1', with inputs '$2' and '$3'."
23303  if s1=[$_nn_$2_out_size];s2=[$_nn_$3_out_size];s1[0,3]!=s2[0,3]
23304    error "nn_layer_append: Cannot append inputs $2("$_nn_$2_out_size") and $3("$_nn_$3_out_size") together."
23305  fi
23306  _nn_$1_out_size={[$_nn_$2_out_size]+[0,0,0,[$_nn_$3_out_size][3]]}
23307  _nn_forward.="begin(nn_layer_append_init_forward($1,$2,$3));"\
23308               "nn_layer_append_forward($1,$2,$3);"
23309  _nn_backward..="nn_layer_append_backward($1,$2,$3);"
23310  _nn_init.="$0 $* "
23311
23312#@cli nn_layer_avgpool2d : name,in
23313#@cli : Add a 'avgpool2d' layer (2d average pooling) to the network.
23314nn_layer_avgpool2d : check "isvarname('$1')" nn_check_layer "$2"
23315  e[^-1] "[nn_lib] Add 'avgpool2d' layer '$1', with input '$2'."
23316  _nn_$1_out_size={s=[$_nn_$2_out_size];[int(s[0,2]/2),s[2,2]]}
23317  _nn_forward.="begin(nn_layer_avgpool2d_init_forward($1,$2));"\
23318               "nn_layer_avgpool2d_forward($1,$2);"
23319  _nn_backward..="nn_layer_avgpool2d_backward($1,$2);"
23320  _nn_init.="$0 $* "
23321
23322#@cli nn_layer_batchnorm : name,in,_learning_mode.
23323#@cli : Add a 'batchnorm' layer to the network.
23324#@cli : 'learning_mode' can be { 0=learn no parameters | 1=learn gamma | 2=learn beta | 3=learn gamma and beta}.
23325#@cli : Default value: 'learning_mode=3'.
23326nn_layer_batchnorm : nn_check_layer "$2" check "isvarname('$1') && isint(${3=3}) && inrange($3,0,3)"
23327  s0,s1,s2,s3="no parameters","gamma-only","beta-only","gamma & beta"
23328  e[^-1] "[nn_lib] Add 'batchnorm' layer '$1' to the network, with input '$2' and learning mode '"${s$3}"'."
23329  _nn_$1_out_size=$_nn_$2_out_size
23330  if !isint($$1) 4,{[$_nn_$2_out_size][3]} f. 0,1,1,0 nm. $1 fi
23331  _nn_forward.="begin(nn_layer_batchnorm_init_forward($1,$2));"\
23332               "nn_layer_batchnorm_forward($1,$2);"
23333  _nn_backward..="begin(nn_layer_batchnorm_init_backward($1));"\
23334                 "nn_layer_batchnorm_backward($1,$2,$3);"\
23335                 "end(nn_layer_batchnorm_end_backward($1));"
23336  _nn_update..="end(nn_layer_batchnorm_update($1,$3));"
23337  _nn_init.="$0 $* "
23338  _nn_parameters.="$1,"
23339
23340#@cli nn_layer_clone : name0,name1,in
23341#@cli : Add a 'clone' layer to the network.
23342nn_layer_clone : nn_check_layer "$3" check "isvarname('$1') && isvarname('$2')"
23343  e[^-1] "[nn_lib] Add 'clone' layer with input '$3' and outputs '$1' and '$2'."
23344  _nn_$1_out_size=$_nn_$3_out_size
23345  _nn_$2_out_size=$_nn_$3_out_size
23346  _nn_forward.="begin(nn_layer_clone_init_forward($1,$2,$3));"\
23347               "nn_layer_clone_forward($1,$2,$3);"
23348  _nn_backward..="nn_layer_clone_backward($1,$2,$3);"
23349  _nn_init.="$0 $* "
23350
23351#@cli nn_layer_conv2d : name,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation,_is_updated={ 0 | 1 }
23352#@cli : Add a 'conv2d' layer (2D convolutional layer) to the network.
23353#@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1' and 'is_updated=1'.
23354nn_layer_conv2d : nn_check_layer "$2" check "isvarname('$1') && isint($3) && $3>0 && isint(${4=3}) && $4>0 && "\
23355                                            "${5=1}>0 && isbool(${7=1})" skip "${6=1}"
23356  e[^-1] "[nn_lib] Add 'conv2d' layer '$1', with input '$2', $3 channels, $4x$4 kernels, stride $5 and dilation $6."
23357  _nn_$1_out_size={s=[$_nn_$2_out_size];[max(1,int(s[0]/$5)),max(1,int(s[1]/$5)),s[2],$3]}
23358  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
23359  _nn_forward.="begin(nn_layer_conv2d_init_forward($1,$2,$4,$5,$6));"\
23360               "nn_layer_conv2d_forward($1,$2);"
23361  _nn_backward..="begin(nn_layer_conv2d_init_backward($1,$2));"\
23362                 "nn_layer_conv2d_backward($1,$2);"\
23363                 "end(nn_layer_conv2d_end_backward($1));"
23364  if $7 _nn_update..="end(nn_layer_conv2d_update($1));" fi
23365  _nn_init.="$0 $* "
23366  _nn_parameters.="$1,"
23367
23368#@cli nn_layer_conv2dbnnl : name,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation>0,_activation,\
23369# _is_updated={ 0 | 1 }
23370#@cli : Add a 'con2dbnnl' (2D convolutional layer followed by a batchnorm, then a non-linearity), to the network.
23371#@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1', 'activation=leakyrelu' and 'is_updated=1'.
23372nn_layer_conv2dbnnl : nn_check_layer "$2"
23373                      check "isvarname('$1') && isint($3) && $3>0 && isint(${4=3}) && $4>0 && ${5=1}>0 && "\
23374                            "isbool(${8=1})"
23375                      skip "${6=1}","${7=leakyrelu}"
23376  e[^-1] "[nn_lib] Add 'conv2d+bn+nl' layer '$1', with input '$2', $3 channels, $4x$4 kernels, "\
23377         "stride $5, dilation $6 and '$7' activation."
23378  nn_layer_conv2d $1_0,$2,$3,$4,$5,$6,$8
23379  nn_layer_batchnorm $1_1,$1_0
23380  nn_layer_nl $1,$1_1,$7
23381
23382#@cli nn_layer_conv2dnl : name,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation>0,_activation,\
23383# _is_updated={ 0 | 1 }
23384#@cli : Add a 'con2dnl' (2D convolutional layer followed by a non-linearity), to the network.
23385#@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1', 'activation=leakyrelu' and 'is_updated=1'.
23386nn_layer_conv2dnl : nn_check_layer "$2"
23387                    check "isvarname('$1') && isint($3) && $3>0 && isint(${4=3}) && $4>0 && ${5=1}>0 && isbool(${8=1})"
23388                    skip "${6=1}","${7=leakyrelu}"
23389  e[^-1] "[nn_lib] Add 'conv2d+nl' layer '$1', with input '$2', $3 channels, $4x$4 kernels, "\
23390         "stride $5, dilation $6 and '$7' activation."
23391  nn_layer_conv2d $1_0,$2,$3,$4,$5,$6,$8
23392  nn_layer_nl $1,$1_0,$7
23393
23394#@cli nn_layer_crop : name,in,x0,y0,z0,x1,y1,z1
23395#@cli : Add a 'crop' layer to the network.
23396nn_layer_crop : nn_check_layer "$2" check "min(isint([${3-8}]))"
23397  e[^1] "[nn_lin] Add 'crop' layer '$1', with input '$2' and coordinates (${3-5})-(${6-8})."
23398  cmin={vmin([${3-5}],[${6-8}])}
23399  cmax={vmax([${3-5}],[${6-8}])}
23400  _nn_$1_out_size={[[$cmax]-[$cmin]+1,arg(4,$_nn_$2_out_size)]}
23401  _nn_forward.="begin(nn_layer_crop_init_forward($1,$2,${3-8}));"\
23402               "nn_layer_crop_forward($1,$2,${3-8});"
23403  _nn_backward..="nn_layer_crop_backward($1,$2,${3-8});"
23404  _nn_init.="$0 $* "
23405
23406#@cli nn_layer_fc : name,in,nb_channels>0,_is_updated={ 0 | 1 }
23407#@cli : Add a 'fc' layer (fully connected layer) to the network.
23408#@cli : Default value: 'is_updated=1'.
23409nn_layer_fc : nn_check_layer "$2" check "isvarname('$1') && isint($3) && $3>0 && isbool(${4=1})"
23410  e[^-1] "[nn_lib] Add 'fc' layer '$1', with input '$2' and $3 channels."
23411  _nn_$1_out_size=1,1,1,$3
23412  if !isint($$1) {prod($_nn_$2_out_size)+1},$3 f. 4*g/(w-1) nm. $1 fi
23413  _nn_forward.="begin(nn_layer_fc_init_forward($1));"\
23414               "nn_layer_fc_forward($1,$2);"
23415  _nn_backward..="begin(nn_layer_fc_init_backward($1));"\
23416                 "nn_layer_fc_backward($1,$2);"\
23417                 "end(nn_layer_fc_end_backward($1));"
23418  if $4 _nn_update..="end(nn_layer_fc_update($1));" fi
23419  _nn_init.="$0 $* "
23420  _nn_parameters.="$1,"
23421
23422#@cli nn_layer_fcbnnl : name,in,nb_neurons>0,_activation,_is_updated={ 0 | 1 }
23423#@cli : Add a 'fcbnnl' layer (fully connected layer followed by batchnorm, then a non-linearity), to the network.
23424#@cli : Default value: 'activation=leakyrelu' and 'is_updated=1'.
23425nn_layer_fcbnnl : nn_check_layer "$2" check "isvarname('$1') && isint($3) && $3>0 && isbool(${5=1})" skip ${4=leakyrelu}
23426  e[^-1] "[nn_lib] Add 'fc+bn+nl' layer '$1' to the network, with input '$2', $3 channels and '$4' activation."
23427  nn_layer_fc $1_0,$2,$3,$5
23428  nn_layer_batchnorm $1_1,$1_0
23429  nn_layer_nl $1,$1_1,$4
23430
23431#@cli nn_layer_fcnl : name,in,nb_neurons>0,_activation,_is_updated={ 0 | 1 }
23432#@cli : Add a 'fcnl' layer (fully connected layer followed by a non-linearity), to the network.
23433#@cli : Default value: 'activation=leakyrelu' and 'is_updated=1'.
23434nn_layer_fcnl : nn_check_layer "$2" check "isvarname('$1') && isint($3) && $3>0 && isbool(${5=1})" skip ${4=leakyrelu}
23435  e[^-1] "[nn_lib] Add 'fc+nl' layer '$1' to the network, with input '$2', $3 channels and '$4' activation."
23436  nn_layer_fc $1_0,$2,$3,$5
23437  nn_layer_nl $1,$1_0,$4
23438
23439#@cli nn_layer_maxpool2d : name,in
23440#@cli : Add a 'maxpool2d' layer (2d max pooling) to the network.
23441nn_layer_maxpool2d : nn_check_layer "$2" check "isvarname('$1')"
23442  e[^-1] "[nn_lib] Add 'maxpool2d' layer '$1', with input '$2'."
23443  _nn_$1_out_size={s=[$_nn_$2_out_size];[int(s[0,2]/2),s[2,2]]}
23444  _nn_forward.="begin(nn_layer_maxpool2d_init_forward($1,$2));"\
23445               "nn_layer_maxpool2d_forward($1,$2);"
23446  _nn_backward..="nn_layer_maxpool2d_backward($1,$2);"
23447  _nn_init.="$0 $* "
23448
23449#@cli nn_layer_nl : name,in,_activation
23450#@cli : Add a 'nl' layer (non-linearity) to the network.
23451#@cli : Default value: 'activation=leakyrelu'.
23452nn_layer_nl : nn_check_layer "$2" check "isvarname('$1')" skip ${3=leakyrelu}
23453  e[^-1] "[nn_lib] Add 'nl' layer '$1', with input '$2' and '$3' activation."
23454  _nn_$1_out_size=$_nn_$2_out_size
23455  _nn_forward.="begin(nn_layer_nl_init_forward($1,$2));"\
23456               "nn_layer_nl_forward($1,$2,$3);"
23457  _nn_backward..="nn_layer_nl_backward($1,$2,$3);"
23458  _nn_init.="$0 $* "
23459
23460#@cli nn_layer_rename : name,in
23461#@cli : Add a 'rename' layer to the network.
23462nn_layer_rename : nn_check_layer "$2" check "isvarname('$1')"
23463  e[^-1] "[nn_lib] Add 'rename' layer '$1', with input '$2'."
23464  _nn_$1_out_size=$_nn_$2_out_size
23465  _nn_forward.="begin(nn_layer_rename_init_forward($1,$2));"
23466  _nn_backward..="nn_layer_rename_backward($1,$2);"
23467  _nn_init.="$0 $* "
23468
23469#@cli nn_layer_reshape : name,in,width>0,height>0,depth>0,spectrum>0
23470#@cli : Add a 'reshape' layer to the network.
23471nn_layer_reshape : nn_check_layer "$2" check "isvarname('$1')"
23472  e[^-1] "[nn_lib] Add 'reshape' layer '$1', with input '$2' and size ($3,$4,$5,$6)."
23473  _nn_$1_out_size=$3,$4,$5,$6
23474  if prod($_nn_$1_out_size)!=prod($_nn_$2_out_size)
23475    error "nn_layer_reshape: Cannot reshape input ("$_nn_$2_out_size") to output ("$_nn_$1_out_size")."
23476  fi
23477  _nn_forward.="begin(nn_layer_reshape_init_forward($1,$2,$3,$4,$5,$6));"
23478  _nn_backward..="begin(nn_layer_reshape_init_backward($1,$2));"
23479  _nn_init.="$0 $* "
23480
23481#@cli nn_layer_resize : name,in,width[%]>0,_height[%]>0,_depth[%]>0,_spectrum[%]>0,_interpolation
23482#@cli : Add a 'resize' layer to the network.
23483#@cli : Default values: 'height=depth=spectrum=100%'.
23484nn_layer_resize : nn_check_layer "$2" check "isvarname('$1') && $3>0 && ${4=100%}>0 && ${5=100%}>0 && ${6=100%}>0"
23485                  skip ${7=3}
23486  if ${"is_percent $3"} w={max(1,round([$_nn_$2_out_size][1]*$3))} else w=$3 fi
23487  if ${"is_percent $4"} h={max(1,round([$_nn_$2_out_size][1]*$4))} else h=$4 fi
23488  if ${"is_percent $5"} d={max(1,round([$_nn_$2_out_size][2]*$5))} else d=$5 fi
23489  if ${"is_percent $6"} s={max(1,round([$_nn_$2_out_size][3]*$6))} else s=$6 fi
23490  e[^-1] "[nn_lib] Add 'resize' layer '$1', with input '$2' and output size ("$w","$h","$d","$s")."
23491  _nn_$1_out_size=$w,$h,$d,$s
23492  _nn_forward.="begin(nn_layer_resize_init_forward($1,$2,"$w","$h","$d","$s"));"\
23493               "nn_layer_resize_forward($1,$2,$7);"
23494  _nn_backward..="nn_layer_resize_backward($1,$2);"
23495  _nn_init.="$0 $* "
23496
23497#@cli nn_layer_run : name,in,"command",_width[%]>0,_height[%]>0,_depth[%]>0,_spectrum[%]>0
23498#@cli : Add a 'run' layer to the network.
23499#@cli : Default values: 'width=height=depth=spectrum=100%'.
23500nn_layer_run : nn_check_layer "$2" check "isvarname('$1') && ${4=100%}>0 && ${5=100%}>0 && ${6=100%}>0 && ${7=100%}>0"
23501  if ${"is_percent $4"} w={max(1,round([$_nn_$2_out_size][0]*$4))} else w=$4 fi
23502  if ${"is_percent $5"} h={max(1,round([$_nn_$2_out_size][1]*$5))} else h=$5 fi
23503  if ${"is_percent $6"} d={max(1,round([$_nn_$2_out_size][2]*$6))} else d=$6 fi
23504  if ${"is_percent $7"} s={max(1,round([$_nn_$2_out_size][3]*$7))} else s=$7 fi
23505  e[^-1] "[nn_lib] Add 'run' layer '$1', with input '$2', command '$3' and output size ("$w","$h","$d","$s")."
23506  _nn_$1_out_size=$w,$h,$d,$s
23507  _nn_forward.="begin(nn_layer_run_init_forward($1,$2,'$3',"$w","$h","$d","$s"));"\
23508               "nn_layer_run_forward($1,$2);"
23509  _nn_backward..="nn_layer_run_backward($1,$2);"
23510  _nn_init.="$0 $* "
23511
23512#@cli nn_layer_split : name0,name1,in,nb_channels0
23513#@cli : Add a 'split' layer to the network.
23514nn_layer_split : nn_check_layer "$3" check "isvarname('$1') && isvarname('$2')"
23515  e[^-1] "[nn_lib] Add 'split' layer, with input '$3' and outputs '$1' and '$2'."
23516  _nn_$1_out_size={[[$_nn_$3_out_size][0,3],$4]}
23517  _nn_$2_out_size={[$_nn_$3_out_size]-[0,0,0,$4]}
23518  _nn_forward.="begin(nn_layer_split_init_forward($1,$2,$3,$4));"\
23519               "nn_layer_split_forward($1,$2,$3);"
23520  _nn_backward..="nn_layer_split_backward($1,$2,$3);"
23521  _nn_init.="$0 $* "
23522
23523#@cli nn_loss_mse : name,in,ground_truth
23524#@cli : Add a 'mse' loss to the network.
23525#@cli : Default value: 'learning_rate=1e-6' and 'learning_rate_scheduler=adaptive'.
23526nn_loss_mse : nn_check_layer "$2" check "isvarname('$1') && isvarname('$3')"
23527  e[^-1] "[nn_lib] Add MSE loss '$1', with input '$2' and ground truth '$3'."
23528  _nn_$1_out_size=1,1,1,1
23529  _nn_backward..="begin(nn_loss_mse_init_backward($1));"\
23530                 "nn_loss_mse_backward(${1-3});"\
23531                 "end(nn_loss_mse_end_backward($1));"
23532  _nn_init.="$0 $* "
23533
23534#@cli nn_trainer : name,loss,_learning_rate>0,_optimizer,_scheduler
23535#@cli : Add a network trainer to the network.
23536#@cli : 'optimizer' can be { sgd | rmsprop | adam | adamax }.
23537#@cli : 'scheduler' can be { constant | linear | exponential | adaptive }.
23538#@cli : Default value: 'learning_rate=1e-6', 'optimizer=adam' and 'scheduler=constant'.
23539nn_trainer : nn_check_layer "$2"
23540             check "isvarname('$1') && ${3=1e-6}>0 && isvarname('${4=adam}') && isvarname('${5=constant}')"
23541  e[^-1] "[nn_lib] Add trainer '$1' for loss '$2', with learning rate '$3', $4 optimizer and $5 scheduler."
23542  _nn_$1_out_size=0,0,0,0
23543  if !isint($$1)
23544    # (iteration,learning_rate,initial_learning_rate,previous_loss,best_loss;
23545    # {optimizer variables};
23546    # {scheduler variables})
23547    l[] (0,$3,$3,inf,inf) _nn_optimizer_$4 _nn_scheduler_$5 a y nm $1 endl
23548  fi
23549  o_sgd,o_rmsprop,o_adam,o_adamax=0,1,2,3
23550  s_constant,s_linear,s_exponential,s_adaptive=0,1,2,3
23551  _nn_backward..="begin(nn_trainer_init_backward($1,$2,"${o_$4},${s_$5}"));"
23552  _nn_update.="end(nn_trainer_update($1,$2));" # <- Keep this as the last thing done in an 'end()' bloc!
23553  _nn_init.="$0 $* "
23554  _nn_parameters.="$1,"
23555
23556_nn_optimizer_sgd : 1
23557_nn_optimizer_rmsprop : 1
23558_nn_optimizer_adam : 1
23559_nn_optimizer_adamax : 1
23560
23561_nn_scheduler_constant : 1
23562_nn_scheduler_linear : 1
23563_nn_scheduler_exponential : 1
23564_nn_scheduler_adaptive : (0,0,0)
23565
23566#@cli nn_load : 'filename.gmz'
23567#@cli : Load and initialize network saved as a .gmz file.
23568#@cli : Neural network files can be only loaded in .gmz format.
23569nn_load : check "s=['$1']; find(s,'.gmz')==size(s) - 4"
23570  e[^-1] "[nn_lib] Load network from file '$1'."
23571  i "$1" run {t} rm.
23572
23573#@cli nn_save : 'filename.gmz'
23574#@cli : Save current network as a .gmz file.
23575#@cli : Neural network files can be only saved in .gmz format.
23576nn_save : check "s=['$1']; find(s,'.gmz')==size(s) - 4"
23577  e[^-1] "[nn_lib] Save current network as file '$1'."
23578  ('$_nn_parameters') autocrop. {','} sel={t} rm.
23579  ('$_nn_init') autocrop. {'" "'}
23580  nm. _nn_init
23581  o[$sel,-1] "$1"
23582  rm.
23583
23584#----------------------------------
23585#
23586#@cli :: Arrays, Tiles and Frames
23587#
23588#----------------------------------
23589
23590#@cli array : M>0,_N>0,_expand_type={ 0=min | 1=max | 2=all }
23591#@cli : Create MxN array from selected images.
23592#@cli : Default values: 'N=M' and 'expand_type=0'.
23593#@cli : $ image.jpg array 3,2,2
23594array : check "isint($1) && $1>0 && isint(${2=$1}) && $2>0" skip ${3=0}
23595  e[^-1] "Create $1x$2 array from image$?, with expand type $3."
23596  r0={100/max($1,$2)} r1={100/min($1,$2)} r2=100
23597  r ${r$3}%,${r$3}%,1,100%,2 r {$1*100}%,{$2*100}%,1,100%,0,2
23598
23599#@cli array_fade : M>0,_N>0,0<=_fade_start<=100,0<=_fade_end<=100,_expand_type={0=min | 1=max | 2=all}
23600#@cli : Create MxN array from selected images.
23601#@cli : Default values: 'N=M', 'fade_start=60', 'fade_end=90' and 'expand_type=1'.
23602#@cli : $ image.jpg array_fade 3,2
23603array_fade : skip ${2=$1},${3=60},${4=90},${5=1}
23604  e[^-1] "Create $1x$2 array of ($3%,$4%) faded tiles from image$?, with expand type $5."
23605  repeat $! l[$>] . shift.. {round(w/2)},{round(h/2)},1,1,2 fade_diamond $3,$4 endl done
23606  array $1,$2,$5
23607
23608#@cli array_mirror : N>=0,_dir={ 0=x | 1=y | 2=xy | 3=tri-xy },_expand_type={ 0 | 1 }
23609#@cli : Create 2^Nx2^N array from selected images.
23610#@cli : Default values: 'dir=2' and 'expand_type=0'.
23611#@cli : $ image.jpg array_mirror 2
23612array_mirror : skip ${2=2},${3=0}
23613  e[^-1] "Create a 2^$1x2^$1 mirrored-array from image$?, with expand type $2."
23614  repeat $1
23615    if $3==0
23616      if $2>=3 r 33%,33%,100%,100%,2
23617      else r 50%,50%,100%,100%,2
23618      fi
23619    fi
23620    repeat $! l[$>]
23621      if $2==0 +mirror x a x
23622      elif $2==1 +mirror y a y
23623      else +mirror x a x +mirror y a y if $2==3 r 150%,150%,1,100%,0,2,1,1 fi
23624      fi
23625    endl done
23626  done
23627
23628#@cli array_random : Ms>0,_Ns>0,_Md>0,_Nd>0
23629#@cli : Create MdxNd array of tiles from selected MsxNs source arrays.
23630#@cli : Default values: 'Ns=Ms', 'Md=Ms' and 'Nd=Ns'.
23631#@cli : $ image.jpg +array_random 8,8,15,10
23632array_random : skip ${2=$1},${3=$1},${4=$2}
23633  e[^-1] "Create $3x$4 array of tiles from $1x$2 array$?."
23634  repeat $! l[$>] nm={0,n}
23635    split_tiles $1,$2
23636    repeat $3 repeat $4 [{u($1*$2-1)}] done done
23637    rm[0-{$1*$2-1}] append_tiles $3,$4
23638  nm $nm endl done
23639
23640#@cli frame : eq. to 'frame_xy'.
23641frame : skip ${2=$1}>=0,${3=255},${4=$3},${5=$4},${6=255}
23642  _gmic_s="$?" v + _frame_xy ${1--1}
23643
23644#@cli frame_blur : _sharpness>0,_size>=0,_smoothness,_shading,_blur
23645#@cli : Draw RGBA-colored round frame in selected images.
23646#@cli : Default values: 'sharpness=10', 'size=30', 'smoothness=0', 'shading=1' and 'blur=3%'.
23647#@cli : $ image.jpg frame_blur 3,30,8,10%
23648frame_blur : skip ${1=10},${2=30},${3=0},${4=1},${5=3%}
23649  e[^-1] "Draw round frame on image$?, with sharpness $1, size $2, smoothness $3, shading $4 and blur $5."
23650  to_rgba repeat $! l[$>] nm={0,n}
23651    100%,100%,1,1,"-(abs(x/w-0.5)^$1 + abs(y/h-0.5)^$1)^(1/$1)" >=. $2%
23652    if $4 distance. 1 n. 0,1 *. -1 +. 1 ^. {1/$4} fi
23653    b. $3 +b.. $5 mv. -3 blend_fade[0,1] . rm.
23654  nm $nm endl done
23655
23656#@cli frame_cube : _depth>=0,_centering_x,_centering_y,_left_side={0=normal | 1=mirror-x | 2=mirror-y | 3=mirror-xy},\
23657# _right_side,_lower_side,_upper_side
23658#@cli : Insert 3D frames in selected images.
23659#@cli : Default values: 'depth=1', 'centering_x=centering_y=0' and 'left_side=right_side,lower_side=upper_side=0'.
23660#@cli : $ image.jpg frame_cube ,
23661frame_cube : check "${1=1}>=0" skip ${2=0},${3=0},${4=0},${5=0},${6=0},${7=0}
23662  e[^-1] "Insert 3D frame in image$?, with depth $1, centering point ($2,$3) and orientations (${4--1})."
23663  repeat $! l[$>] nm={0,n} split_opacity
23664    if $!==2 frame_cube ${1--1} a c  # Manage image with alpha-channel.
23665    else
23666      m={max(w,h)} w={w} h={h} s={s}
23667      imageplane3d c3d /3d. $w,$h,1
23668      +_frame_cube[0] $4 r3d. 0,1,0,-90 +3d. -0.5,0,-0.5  # Left side.
23669      +_frame_cube[0] $5 r3d. 0,1,0,90 +3d. 0.5,0,-0.5  # Right side.
23670      +_frame_cube[0] $6 r3d. 1,0,0,-90 +3d. 0,0.5,-0.5   # Lower side.
23671      +_frame_cube[0] $7 r3d. 1,0,0,90 +3d. 0,-0.5,-0.5 # Upper side.
23672      +3d 0,0,1 +3d *3d $w,$h,$m  # Append sides together.
23673      f=1000
23674      cx=$2*$w/2*($f+$m*$1)/$f
23675      cy=$3*$h/2*($f+$m*$1)/$f
23676      s3d r[2] 3,{{2,h}/3},1,1,-1
23677      f[2] "if(i(2,y)<0.5,i,i+if(x==0,"$cx",if(x==1,"$cy",($1-1)*"$m")))"
23678      y[2] a y
23679      *3d 2 {2*$w},{2*$h},1,$s f3d {2*$f}
23680      j3d. ..,50%,50%,0,1,2,0,0 rm..
23681      r $w,$h,1,100%,2
23682    fi
23683  nm $nm endl done
23684
23685_frame_cube :
23686  if $1==1 r3d. 0,1,0,180 rv3d.
23687  elif $1==2 r3d. 1,0,0,180 rv3d.
23688  elif $1==3 r3d. 0,0,1,180
23689  fi
23690
23691#@cli frame_fuzzy : size_x[%]>=0,_size_y[%]>=0,_fuzzyness>=0,_smoothness[%]>=0,_R,_G,_B,_A
23692#@cli : Draw RGBA-colored fuzzy frame in selected images.
23693#@cli : Default values: 'size_y=size_x', 'fuzzyness=5', 'smoothness=1' and 'R=G=B=A=255'.
23694#@cli : $ image.jpg frame_fuzzy 20
23695frame_fuzzy : skip ${2=$1},${3=5},${4=1},${5=255},${6=$5},${7=$6},${8=255}
23696  e[^-1] "Draw $1x$2 fuzzy frame on image$?, with fuzzyness $3, smoothness $4 and RGBA color ($5,$6,$7,$8)."
23697  to_rgba repeat $! l[$>]
23698    100%,100%,1,1,1
23699    padx={if(${"is_percent $1"},$1*(w-1)/2,$1)}
23700    pady={if(${"is_percent $2"},$2*(h-1)/2,$2)}
23701    rectangle. $padx,$pady,{w-1-$padx},{h-1-$pady}
23702    spread. $3 b. $4 100%,100%,1,4 fc. ${5-8}
23703    j[0] [2],0,0,0,0,1,[1] k[0]
23704  endl done
23705
23706#@cli frame_painting : _size[%]>=0,0<=_contrast<=1,_profile_smoothness[%]>=0,_R,_G,_B,_vignette_size[%]>=0,\
23707# _vignette_contrast>=0,_defects_contrast>=0,0<=_defects_density<=100,_defects_size>=0,_defects_smoothness[%]>=0,\
23708# _serial_number
23709#@cli : Add a painting frame to selected images.
23710#@cli : Default values: 'size=10%', 'contrast=0.4', 'profile_smoothness=6%', 'R=225', 'G=200', 'B=120', \
23711# 'vignette_size=2%', 'vignette_contrast=400', 'defects_contrast=50', 'defects_density=10', 'defects_size=1', \
23712# 'defects_smoothness=0.5%' and 'serial_number=123456789'.
23713#@cli : $ image.jpg frame_painting ,
23714frame_painting :
23715  check "${1=10%}>=0 && ${2=0.4}>=0 && $2<=1 && ${3=6%}>=0 && ${7=2%}>=0 && ${8=400}>=0 && ${9=50}>=0 &&
23716         ${10=10}>=0 && $10<=100 && ${11=1}>=0 && ${12=0.5%}>=0"
23717  skip ${4=225},${5=200},${6=120},${13=123456789}
23718  e[^-1] "Add painting frame to image$?, with size $1, contrast $2, profile smoothness $3, color (${4-6}),
23719          vignette size $7, vignette strength $8, defects contrast $9, defects density $10, defects size $11,
23720          defects smoothness $12 and serial number $13."
23721  if !$1 return fi
23722  repeat $! l[$>]
23723    $1,$1 s={max(w,h)} rm.                             # Determine size of the frame
23724    ('${dec2bin\ $13}') -. {'0'} r. $s                 # Generate frame profile from serial number
23725    transpose. b. $3 n. {1-$2},{1+$2}
23726    +r. {{-2,w}+2*$s},100%,1,1                         # Upper frame
23727    +mirror. y                                         # Lower frame
23728    mv... $! transpose. r. 100%,{-4,h+2*$s},1,1        # Left frame
23729    +mirror. x                                         # Right frame
23730    ...,...,1,1,1
23731    polygon. 3,0,0,{$s-1},{$s-1},0,{$s-1},1,0
23732    polygon. 3,100%,0,{w-$s},100%,100%,100%,1,0   # Upper/lower mask
23733    ..,..,1,1,1
23734    polygon. 3,1,0,100%,{$s-2},100%,0
23735    polygon. 3,1,100%,100%,{h-$s+1},100%,100%,1,0 # Left/right mask
23736    _frame_painting[-6--3] ${4-6},${9-12}         # Add colors + defects
23737
23738    # Build full frame picture.
23739    {-7,w+2*$s},{-7,h+2*$s},1,3
23740    j. [-7],0,0,0,0,1,...,1 rm[-7] mirror... y
23741    j. [-6],0,{h-$s},0,0,1,...,1 rm[-6,-3]
23742    j. [-4],0,0,0,0,1,..,1 rm[-4] mirror.. x
23743    j. ...,{w-$s},0,0,0,1,..,1 rm[-3,-2]
23744    ..,..,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.
23745    a[-2--1] c
23746    r.. .,.,1,100%,0,0,0.5,0.5 blend alpha # Insert initial image into frame picture.
23747  endl done
23748
23749_frame_painting : # Add color + texture to each frame part.
23750  repeat $! l[$>]
23751    +*. $2 +*.. $3 *... $1 a[-3--1] c
23752    100%,100%
23753    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"
23754    b. $7 g. +[-2,-1] n. -$4,$4
23755    +[-2,-1] c. 0,255
23756  endl done
23757
23758#@cli frame_pattern : M>=3,_constrain_size={ 0 | 1 } : M>=3,_[frame_image],_constrain_size={ 0 | 1 }
23759#@cli : Insert selected pattern frame in selected images.
23760#@cli : Default values: 'pattern=0' and 'constrain_size=0'.
23761#@cli : $ image.jpg frame_pattern 8
23762frame_pattern : check $1>=3 skip "${2=0},${3=}"
23763  to_colormode 0
23764  if ${"is_image_arg $2"} # Frame from specified image.
23765    e[^-1] "Insert $1x$1 pattern frame on image$?, using frame image$2."
23766    pass$2 0 repeat $!-1 l[$>,-1]
23767      wh={0,w},{0,h}
23768      +r[1] {0,max(1,w/($1-2))},{0,max(1,h/($1-2))},1,100%,2
23769      r[0] {{0,w}+2*w},{{0,h}+2*h},1,100%,0,0,0.5,0.5
23770      [-1]x{$1+2} a[{-$1-2}--1] x j[0] .,0,0 j[0] .,0,{{0,h}-1-h} rm.
23771      [-1]x{$1+1} a[{-$1-2}--1] y j[0] .,0,0 j[0] .,{{0,w}-1-w} rm.
23772      if $3 r[0] $wh,1,100%,2 fi
23773    endl done rm.
23774  else # Self-frame.
23775    e[^-1] "Insert $1x$1 self-pattern frame on image$?."
23776    repeat $! l[$>]
23777      wh={w},{h}
23778      +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
23779      [-1]x{$1+2} a[{-$1-2}--1] x j... .,0,0 j... .,0,{{-3,h}-1-h} rm.
23780      [-1]x{$1+1} a[{-$1-2}--1] y j.. .,0,0 j.. .,{{-2,w}-1-w} rm.
23781      if $3 r $wh,1,100%,2 fi
23782    endl done
23783  fi
23784
23785#@cli frame_round : _sharpness>0,_size>=0,_smoothness,_shading,_R,_G,_B,_A
23786#@cli : Draw RGBA-colored round frame in selected images.
23787#@cli : Default values: 'sharpness=10', 'size=10', 'smoothness=0', 'shading=0' and 'R=G=B=A=255'.
23788#@cli : $ image.jpg frame_round 10
23789frame_round : skip ${1=10},${2=10},${3=0},${4=0},${5=255},${6=$5},${7=$6},${8=255}
23790  e[^-1] "Draw round frame on image$?, with sharpness $1, size $2, smoothness $3, shading $4 and
23791          RGBA color ($5,$6,$7,$8)."
23792  to_rgba repeat $! l[$>] nm={0,n}
23793    100%,100%,1,1,"-(abs(x/w-0.5)^$1 + abs(y/h-0.5)^$1)^(1/$1)" >=. $2%
23794    if $4 distance. 1 n. 0,1 *. -1 +. 1 ^. {1/$4} fi
23795    b. $3 i... 100%,100%,1,4 fc... $5,$6,$7,$8 blend_fade[0,1] . rm.
23796  nm $nm endl done
23797
23798#@cli frame_seamless : frame_size>=0,_patch_size>0,_blend_size>=0,\
23799# _frame_direction={ 0=inner (preserve image size) | 1=outer }
23800#@cli : Insert frame in selected images, so that tiling the resulting image makes less visible seams.
23801#@cli : Default values: 'patch_size=7', 'blend_size=5' and 'frame_direction=1'.
23802#@cli : $ image.jpg +frame_seamless 30 array 2,2
23803frame_seamless : check "$1>=0 && isint(${2=7}) && $2>0 && isint(${3=5}) && $3>=0" skip ${4=1}
23804  s0="inner" s1="outer"
23805  e[^-1] "Insert "${s{!!$4}}" seamless frame in image$?, with size $1, patch size $2 and blend size $3."
23806  repeat $! l[$>]
23807    w2={round(w/2)} h2={round(h/2)}
23808    w4={round(w/4)} h4={round(h/4)}
23809    if !$4 r {max(1,w-$1)},{max(1,h-$1)},1,100%,0,0,0.5,0.5 fi
23810    100%,100%,1,1,-1 r[-2,-1] {w+$1},{h+$1},1,100%,0,0,0.5,0.5 n. 0,1
23811    shift -$w2,-$h2,0,0,2
23812    inpaint_matchpatch.. [1],0,$2,10,$3
23813    rectangle. $1,$1,{w-1-$1},{h-1-$1}
23814    shift -$w4,-$h4,0,0,2
23815    inpaint_matchpatch.. [1],0,$2,10,$3
23816    rm.
23817    shift {$w4+$w2},{$h4+$h2},0,0,2
23818  endl done
23819
23820#@cli frame_x : size_x[%],_col1,...,_colN
23821#@cli : Insert colored frame along the x-axis in selected images.
23822#@cli : Default values: 'col1=col2=col3=255' and 'col4=255'.
23823#@cli : $ image.jpg frame_x 20,255,0,255
23824frame_x : skip ${2=255},${3=$2},${4=$3},${5=255}
23825  e[^-1] "Insert $1 outer frame in image$? along the x-axis, with color (${2--1})."
23826  _frame $1,0,0,${2--1}
23827
23828#@cli frame_xy : size_x[%],_size_y[%],_col1,...,_colN
23829#@cli : Insert colored frame along the x-axis in selected images.
23830#@cli : Default values: 'size_y=size_x', 'col1=col2=col3=255' and 'col4=255'.
23831#@cli : (eq. to 'frame').
23832#@cli : $ image.jpg frame_xy 1,1,0 frame_xy 20,10,255,0,255
23833frame_xy : skip ${2=$1},${3=255},${4=$3},${5=$4},${6=255}
23834  _gmic_s="$?" v + _$0 ${1--1}
23835
23836_frame_xy :
23837  e[0--3] "Insert $1x$2 outer frame in image"$_gmic_s" along the xy-axes, with color (${3--1})."
23838  _frame $1,$2,0,${3--1}
23839
23840#@cli frame_xyz : size_x[%],_size_y[%],_size_z[%]_col1,...,_colN
23841#@cli : Insert colored frame along the x-axis in selected images.
23842#@cli : Default values: 'size_y=size_x=size_z', 'col1=col2=col3=255' and 'col4=255'.
23843frame_xyz : skip ${2=$1},${3=$2},${4=255},${5=$4},${6=$5},${7=255}
23844  e[^-1] "Insert $1x$2x$3 outer frame in image$? along the xyz-axes, with color (${4--1})."
23845  _frame $1,$2,$3,${4--1}
23846
23847#@cli frame_y : size_y[%],_col1,...,_colN
23848#@cli : Insert colored frame along the y-axis in selected images.
23849#@cli : Default values: 'col1=col2=col3=255' and 'col4=255'.
23850#@cli : $ image.jpg frame_y 20,255,0,255
23851frame_y : skip ${2=255},${3=$2},${4=$3},${5=255}
23852  e[^-1] "Insert $1 outer frame in image$? along the y-axis, with color (${2--1})."
23853  _frame 0,$1,0,${2--1}
23854
23855_frame :
23856  repeat $! l[$>]
23857    nm={0,n}
23858    w={round($1*if(${is_percent\ $1},w,1))}
23859    h={round($2*if(${is_percent\ $2},h,1))}
23860    d={round($3*if(${is_percent\ $3},d,1))}
23861    {w+2*$w},{h+2*$h},{d+2*$d},100% fc[1] ${4--1}
23862    j[1] [0],$w,$h,$d rm[0] nm $nm
23863  endl done
23864
23865#@cli img2ascii : _charset,_analysis_scale>0,_analysis_smoothness[%]>=0,_synthesis_scale>0,_output_ascii_filename
23866#@cli : Render selected images as binary ascii art.
23867#@cli : This command returns the corresponding the list of widths and heights (expressed as a number of characters)
23868#@cli : for each selected image.
23869#@cli : Default values: 'charset=[ascii charset]', 'analysis_scale=16', 'analysis_smoothness=20%', \
23870# 'synthesis_scale=16' and '_output_ascii_filename=[undefined]'.
23871#@cli : $ image.jpg img2ascii ,
23872img2ascii : check "${2=16}>0 && ${3=20%}>=0 && ${4=16}>0"
23873            skip "${1= !\042#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\133\\\135^_\140abcdefghijk"\
23874                 "lmnopqrstuvwxyz\173|\174~}","${5=}"
23875  e[^-1] "Render image$? as binary ascii art, with charset '$1', analysis scale $2, analysis smoothness $3,
23876          synthesis scale $4 and output ascii filename '$5'."
23877  is_multi={$!>1}
23878
23879  # Generate dictionaries for image analysis and synthesis.
23880  l[]
23881    ('"$1"') repeat w
23882      C={`92`}${dec2oct\ {0,@$>}}
23883      0 t. $C,0,0,$2,1,1
23884      0 t. $C,0,0,$4,1,1
23885    done rm[0]
23886    = -1 = -1,0,100% autocrop = 0 = 0,0,100%
23887    l[0--2:2] r {${-max_w}+1},100%,1,1,0,0,0.5 b $3 n 0,255 a z endl
23888    l[1--1] r {${-max_w}+1},100%,1,1,0,0,0.5 a z endl
23889  endl
23890  w={-2,w} h={-2,h}
23891
23892  # Transform selected images to ascii art.
23893  repeat $!-2 l[$>,-2,-1]
23894    luminance[0] n[0] 0,255
23895    nw={0,round(w/$w,1,1)}
23896    nh={0,round(h/$h,1,1)}
23897    if $> list_wh=$list_wh,$nw,$nh else list_wh=$nw,$nh fi
23898
23899    s[0] y,-$h s[0--3] x,-$w r[0--3] $w,$h,1,1,0,0
23900    repeat $!-2 l[$>,-2,-1]
23901      rprogress {$>*100/($!-2)}
23902      ri[0] [1] -[0] [1] sqr[0] r[0] 1,1,100%,1,2 y[0]
23903      C={0,ym} rm[0]
23904      +slices[1] $C mv. 0
23905      if narg("$5") +f[0] $C a[0,-1] c fi
23906    endl done
23907    append_tiles[0--2] $nw,$nh
23908
23909    if narg("$5") s[0] c l[1]  # Export as text file.
23910       r $nw,$nh,1,1,1
23911       ('"$1"') map[0] . k[0]
23912       s y i[1-$!] ('\n')
23913       a x
23914       if $is_multi filename=${filename\ "$5",$>} else filename="$5" fi
23915       ot $filename rm
23916    endl fi
23917
23918  endl done
23919  rm[-2,-1] u $list_wh
23920
23921#@cli imagegrid : M>0,_N>0
23922#@cli : Create MxN image grid from selected images.
23923#@cli : Default value: 'N=M'.
23924#@cli : $ image.jpg imagegrid 16
23925imagegrid : skip ${2=$1}
23926  e[^-1] "Create $1x$2 image grid from image$?."
23927  repeat $! l[$>]
23928    ({w},{h}) ($1,$2) /[-2,-1] round. 1 r.. {^},..,..,2 rm.
23929    ({w},{h}) ($1,$2) *[-2,-1] r.. {^},..,..,2 rm.
23930    $1,$2,1,.,1 shift. 1,1 ri. ..,0,2 *
23931  endl done
23932
23933#@cli imagegrid_hexagonal : _resolution>0,0<=_outline<=1
23934#@cli : Create hexagonal grids from selected images.
23935#@cli : Default values: 'resolution=32', 'outline=0.1' and 'is_antialiased=1'.
23936#@cli : $ image.jpg imagegrid_hexagonal 24
23937imagegrid_hexagonal : check "isint(${1=32}) && $1>0 && ${2=0.1}>=0 && $2<=1"
23938  e[^-1] "Create hexagonal grid(s) from image$?, with resolution $1 and outline $2."
23939  repeat $! l[$>]
23940
23941    # Generate hexagonal grid.
23942    l[]
23943      # Generate hexagon.
23944      ({'CImg3d'},6,6)
23945      (0;{2*pi}) -. {pi/2} r. 1,7,1,1,3 +sin. cos.. a[-2,-1] x rows. 0,5 z. 0,2
23946      2,6,1,1,3,0 1,100%,1,1,y ++. 1 %. 6 rv[-2,-1] a[-3--1] x
23947      3,100%,1,1,1 1,100%,1,1,1 y a y
23948      *3d. {1-$2}
23949
23950      # Generate minimal pattern (2x2 hexagons).
23951      ++3d {sqrt(3)} ++3d {sqrt(3)/2},1.5
23952      col3d... 2 col3d.. 3 col3d. 4 +3d
23953      /3d 1.5
23954    endl
23955
23956    # Duplicate it to get a grid with correct size.
23957    ny={1+round(0.5*$1,1,1)}
23958    nx={0,1+round($1*w/h*3/(sqrt(3)*4),1,1)}
23959    array3d. $nx,$ny,1,{4*sqrt(3)/3},2
23960    c3d. *3d. {0,h/$1}
23961
23962    # Fill grid with image colors.
23963    [0],[0] j3d. ..,50%,50%,0,1,2,0,0 rm..
23964    blend shapeaverage0
23965
23966  endl done
23967
23968#@cli imagegrid_triangular : pattern_width>=1,_pattern_height>=1,_pattern_type,0<=_outline_opacity<=1,\
23969# _outline_color1,...
23970#@cli : Create triangular grids from selected images.
23971#@cli : 'pattern type' can be { 0=horizontal | 1=vertical | 2=crossed | 3=cube | 4=decreasing | 5=increasing }.
23972#@cli : Default values: 'pattern_width=24', 'pattern_height=pattern_width', 'pattern_type=0', 'outline_opacity=0.1' \
23973# and 'outline_color1=0'.
23974#@cli : $ image.jpg imagegrid_triangular 6,10,3,0.5
23975imagegrid_triangular : check "$1>=1 && ${2=$1}>=1 && isint(${3=0}) && $3>=0 && $3<=5" skip ${4=0},${5=0}
23976  s0="horizontal" s1="vertical" s2="crossed" s3="cube"
23977  e[^-1] "Create triangular grid(s) from image$?, with pattern width $1, height $2, pattern type '"${s$3}"', "\
23978          "outline opacity $4 and outline color (${5--1})."
23979
23980  # Create triangular patterns and outlines (always square!).
23981  M={max($1,$2)}
23982  if $3==4" || "$3==5 # Decreasing/Increasing.
23983    $M,$M,1,1,x>y ++. 2 a[-2,-1] x ++. 4 a[-2,-1] y
23984    $M,$M,1,1,"!x || !y || x==y" r. 200%,200%,1,1,0,2
23985    a[-2,-1] c
23986    if $3==5 mirror. y fi
23987  elif $3==3 # Cube.
23988    $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
23989    ++. 4 =. 4,50%,50% =.. 2 a[-2,-1] x label. 0,0
23990    (2,2,2,0,1,2,1,1,3,3,3,1,1,0) map.. . rm.
23991    100%,100%,1,1
23992    line. 0,0,{$M-1},{$M-1},1,1 line. {$M-1},$M,0,100%,1,1
23993    line. {$M-1},{$M-1},{3*$M-1},{$M-1},1,1 line. {2*$M},0,0,0,1,1
23994    line. {2*$M},0,100%,100%,1,1 line. {2*$M},100%,100%,0,1,1
23995    a[-2,-1] c
23996  elif $3==2 # Horizontal + vertical.
23997    $M,$M,1,1,x>y ++. 2 mirror. x a[-2,-1] x ++. 4 mirror. y a[-2,-1] y
23998    100%,100%,1,1,"!x || !y || x==int(w/2) || y==int(h/2) || x==y || w-1-x==y"
23999    a[-2,-1] c
24000  elif $3==1 # Vertical.
24001    $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
24002    100%,100%,1,1,"!x || x==int(w/2) || x==y || w-1-x==y"
24003    a[-2,-1] c
24004  else # Horizontal.
24005    $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
24006    100%,100%,1,1,"!y || y==int(h/2) || x==y || w-1-x==y"
24007    a[-2,-1] c
24008  fi
24009
24010  # Apply grid on images.
24011  repeat $!-1
24012    wh={$>,w},{$>,h}
24013    if $1>$2 r[$>] 100%,{$>,$1*h/$2} elif $1<$2 r[$>] {$>,$2*w/$1} fi
24014    +r. [$>],[$>],1,2,0,2,0.5,0.5
24015    s. c
24016    blend[$>,-2] shapeaverage
24017    +fc[$>] ${5--1} j[$>] .,0,0,0,0,$4,.. rm[-2,-1]
24018    r[$>] $wh,1,100%,2
24019  done
24020  rm.
24021
24022#@cli linearize_tiles : M>0,_N>0
24023#@cli : Linearize MxN tiles on selected images.
24024#@cli : Default value: 'N=M'.
24025#@cli : $ image.jpg +linearize_tiles 16
24026linearize_tiles : check "$1>0 && ${2=$1}>0"
24027  e[^-1] "Linearize $1x$2 tiles on image$?."
24028  repeat $! l[$>] nm={0,n}
24029    s={s} split_tiles $1,$2 s c # Split as tiles for all channels.
24030    repeat $! l[$>]
24031      wh={w},{h}
24032      +f x +f. y +f. 1 y a[^0] x solve.. . rm.
24033      $wh,1,1,{@0}"*x + "{@1}"*y + "{@2} rm..
24034    endl done
24035    repeat int($!/$s) a[-$s--1] c mv. 0 done append_tiles $1,$2
24036  nm $nm endl done
24037
24038#@cli map_sprites : _nb_sprites>=1,_allow_rotation={ 0=none | 1=90 deg. | 2=180 deg. }
24039#@cli : Map set of sprites (defined as the 'nb_sprites' latest images of the selection) to other selected images,
24040#@cli : according to the luminosity of their pixel values.
24041#@cli : $ image.jpg resize2dy 48 repeat 16 ball {8+2*$>},${-rgb} mul[-1] {(1+$>)/16} done map_sprites 16
24042map_sprites : check "isint($1) && $1>0 && isint(${2=0}) && $2>=0 && $2<=2"
24043  e[^-1] "Map set of $1 sprites to image selection$?."
24044  norm[0--{$1+1}] quantize[0--{$1+1}] $1,0,1
24045  slices[-$1--1] 0 r[-$1--1] ${max_wh[-$1--1]},1,100%,0,0,0.5,0.5
24046  if $2==1
24047    N={4*$1}
24048    repeat $!-$1 *[$>] 4 +rand[$>] 0,3 round. +[$>,-1] done
24049    repeat $1 l[{1+$<}] +mirror xy +rotate 90 endl done
24050  elif $2==2
24051    N={2*$1}
24052    repeat $!-$1 *[$>] 2 +rand[$>] 0,1 round. +[$>,-1] done
24053    repeat $1 l[{1+$<}] +mirror xy endl done
24054  else N=$1 fi
24055  r[-$N--1] 100%,100%,1,${max_s[-$N--1]} w={w} h={h} a[-$N--1] x
24056  r[^-1] ${w}00%,${h}00%,1,1 *[^-1] $w
24057  (0,{$w-1};0,{$w-1}^0,0;{$h-1},{$h-1}) r. $w,$h,1,2,3 round.
24058  repeat $!-2 +r. [$>],[$>],1,2,0,2 r[$>] 100%,100%,1,2,0 +[$>,-1] +warp.. [$>],0,0 rv[$>,-1] rm. done rm[-2,-1]
24059
24060#@cli pack : is_ratio_constraint={ 0 | 1 },_sort_criterion
24061#@cli : Pack selected images into a single image.
24062#@cli : The returned status contains the list of new (x,y) offsets for each input image.
24063#@cli : Parameter 'is_ratio_constraint' tells if the resulting image must tend to a square image.
24064#@cli : Default values: 'is_ratio_constraint=0' and 'sort_criterion=max(w,h)'.
24065#@cli : $ image.jpg repeat 10 +resize2dx[-1] 75% balance_gamma[-1] ${-rgb} done pack 0
24066pack : skip ${1=0},${2=max(w,h)}
24067  e[^-1] "Pack image$? into a single image."
24068  if $!<2 return fi
24069  if ${-max_d}>1 error[0--3] "Command '$0': Selected images contain at least one volumetric image (depth>1).
24070                             Should all be 2D." fi
24071  nm={0,n} to_colormode 0
24072
24073  # Sort images by decreasing size.
24074  repeat $! nm$>={$>,n} nm[$>] ${nm$>}:$> done
24075  m "_pack : ('{n}') l. s +,{':'} u {t} rm endl"
24076  if ['$2']=='n' sort_list +,n else sort_list -,"$2" fi
24077
24078  # Start packing
24079  offsets${-_pack[0]}=0,0
24080  N=$!
24081  i[0] 0 # List of empty slots.
24082
24083  do l[0,1,2]
24084    w1,h1,w2,h2={[w#1,h#1,w#2,h#2]}
24085
24086    # Search an empty slot that fits.
24087    slot,min_slot_area=-1,inf
24088    repeat h#0
24089      x,y,w,h={0,crop(0,$>,4,1)}
24090      slot_area={$w*$h}
24091      if $w>=$w2" && "$h>=$h2" && "$slot_area<=$min_slot_area # Found a fit.
24092        slot,min_slot_area=$>,$slot_area
24093      fi
24094    done
24095
24096    if $slot>=0 # Empty slot found -> Use it.
24097      x,y,w,h={0,crop(0,$slot,4,1)}
24098      j[1] [2],$x,$y offsets${-_pack[2]}=$x,$y
24099      l[0]
24100        s y rm[$slot]
24101        area1={max(($w-$w2)*$h,$w2*($h-$h2))}
24102        area2={max(($w-$w2)*$h2,$w*($h-$h2))}
24103        if $area1>=$area2 # Split - type1
24104          if $w2<$w i[$slot] ({$x+$w2},$y,{$w-$w2},$h) fi
24105          if $h2<$h i[$slot] ($x,{$y+$h2},$w2,{$h-$h2}) fi
24106        else # Split - type 2
24107          if $w2<$w i[$slot] ({$x+$w2},$y,{$w-$w2},$h2) fi
24108          if $h2<$h i[$slot] ($x,{$y+$h2},$w,{$h-$h2}) fi
24109        fi
24110        a y if !$! 0 fi
24111      endl
24112      rm[2]
24113
24114    else # Empty slot not found -> Append horizontally or vertically.
24115      if $1
24116        metric_h={abs($w1+$w2-max($h1,$h2))}
24117        metric_v={abs($h1+$h2-max($w1,$w2))}
24118      else
24119        metric_h={if($h2<$h1,$w2*($h1-$h2),$w1*($h2-$h1))}
24120        metric_v={if($w2<$w1,($w1-$w2)*$h2,($w2-$w1)*$h1)}
24121      fi
24122
24123      if $metric_h<=$metric_v # Append horizontally.
24124        offsets${-_pack[2]}=$w1,0
24125        a[1,2] x,0
24126        if $h2<$h1 ($w1,$h2,$w2,{$h1-$h2}) a[0,-1] y
24127        elif $h2>$h1 (0,$h1,$w1,{$h2-$h1}) a[0,-1] y
24128        fi
24129      else # Append vertically.
24130        offsets${-_pack[2]}=0,$h1
24131        a[1,2] y,0
24132        if $w2<$w1 ($w2,$h1,{$w1-$w2},$h2) a[0,-1] y
24133        elif $w2>$w1 ($w1,0,{$w2-$w1},$h1) a[0,-1] y
24134        fi
24135      fi
24136    fi
24137
24138  endl while $!>2
24139  rm[0]
24140
24141  # Return offsets.
24142  status=
24143  repeat $N if narg($status) status=$status,${offsets$>} else status=${offsets$>} fi done
24144  nm $nm u $status
24145  um _pack
24146
24147#@cli puzzle : _width>0,_height>0,_M>=1,_N>=1,_curvature,_centering,_connectors_variability,_resolution>=1
24148#@cli : Input puzzle binary mask with specified size and geometry.
24149#@cli : Default values: 'width=height=512', 'M=N=5', 'curvature=0.5', 'centering=0.5', 'connectors_variability=0.5' \
24150# and 'resolution=64'.
24151#@cli : $ puzzle ,
24152puzzle : check "isint(${1=512}) && $1>0 && isint(${2=$1}) && $2>0 && isint(${3=5}) && $3>0 &&
24153                isint(${4=$3}) && $4>0 && isint(${8=64}) && $8>0"
24154         skip ${5=0.5},${6=0.5},${7=0.5}
24155  e[^-1] "Draw $3x$4 puzzle pattern on image$?, with curvature $5, centering $6, connectors variability $7
24156          and resolution $8."
24157  l[]
24158    if $4>=2 _puzzle[] $3,{$4-1},${5-8} +3d. 0,1 fi
24159    if $3>=2 _puzzle[] $4,{$3-1},${5-8} r3d. 0,0,1,-90 +3d. 1,$4 fi
24160    *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
24161    $1,$2 j3d. ..,0,0,0,1,1,0,0 rm..
24162  endl
24163
24164_puzzle :
24165  R={$6*$1}
24166  repeat $2
24167    ({'CImg3d'},$R,{$R-1})
24168    repeat $1
24169      sign={if(u<=0.5,-1,1)}
24170      center={$4*u(-0.25,0.25)}
24171      knob={$5*u(-0.05,0.12)}
24172      ($>,0;\
24173       {0.2+$center+$>},{-$sign*$3*0.1};\
24174       {0.4+$center+$>},0;\
24175       {0.35+$center+$>},{0.1*$sign};\
24176       {0.45+$center+$>},{(0.15+$knob)*$sign};\
24177       {0.55+$center+$>},{(0.15+$knob)*$sign};\
24178       {0.65+$center+$>},{0.1*$sign};\
24179       {0.6+$center+$>},0;\
24180       {0.8+$center+$>},{-$sign*$3*0.1})
24181    done
24182    ($1,0) a[-{$1+1}--1] y r. 2,$R,1,1,5 z. 0,2
24183    (2,0,1;2,{$R-2},{$R-1}) r. 3,{$R-1},1,1,3 round.
24184    3,{h},1,1,255 1,{h},1,1,255 y[-5--1] y a[-5--1] y +3d. 0,$>
24185  done +3d
24186
24187#@cli quadratize_tiles : M>0,_N>0
24188#@cli : Quadratize MxN tiles on selected images.
24189#@cli : Default value: 'N=M'.
24190#@cli : $ image.jpg +quadratize_tiles 16
24191quadratize_tiles : check "$1>0 && ${2=$1}>0"
24192  e[^-1] "Quadratize $1x$2 tiles on image$?."
24193  repeat $! l[$>] nm={0,n}
24194    s={s} split_tiles $1,$2 s c # Split as tiles for all channels.
24195    repeat $! l[$>]
24196      wh={w},{h}
24197      +f x^2 +f. y^2 +f. x*y +f. x +f. y +f. 1 y a[^0] x
24198      solve.. . rm.
24199      $wh,1,1,{@0}"*x^2 + "{@1}"*y^2 + "{@2}"*x*y +"{@3}"*x + "{@4}"*y + "{@5} rm..
24200    endl done
24201    repeat int($!/$s) a[-$s--1] c mv. 0 done append_tiles $1,$2
24202  nm $nm endl done
24203
24204#@cli rotate_tiles : angle,_M>0,N>0
24205#@cli : Apply MxN tiled-rotation effect on selected images.
24206#@cli : Default values: 'M=8' and 'N=M'.
24207#@cli : $ image.jpg to_rgba rotate_tiles 10,8 drop_shadow 10,10 display_rgba
24208rotate_tiles : skip ${2=8},${3=$2}
24209  e[^-1] "Apply $2x$3 tiled-rotation effect on image$?, with angle $1 deg."
24210  split_tiles $2,$3,1 rotate $1 append_tiles $2,$3
24211
24212#@cli shift_tiles : M>0,_N>0,_amplitude
24213#@cli : Apply MxN tiled-shift effect on selected images.
24214#@cli : Default values: 'N=M' and 'amplitude=20'.
24215#@cli : $ image.jpg +shift_tiles 8,8,10
24216shift_tiles : check "${2=$1}>=0" skip ${3=20}
24217  e[^-1] "Apply $1x$2 tiled-shift effect on image$?, with amplitude $3."
24218  repeat $! l[$>]
24219    $1,$2,1,2 noise. $3 r. ..,..,1,2 warp.. .,1,1,0 rm.
24220  endl done
24221
24222#@cli taquin : M>0,_N>0,_remove_tile={ 0=none | 1=first | 2=last | 3=random },_relief,_border_thickness[%],\
24223# _border_outline[%],_outline_color
24224#@cli : Create MxN taquin puzzle from selected images.
24225#@cli : Default value: 'N=M', 'relief=50', 'border_thickness=5', 'border_outline=0' and 'remove_tile=0'.
24226#@cli : $ image.jpg +taquin 8
24227taquin : check "isint($1) && $1>0 & isint(${2=$1}) && $2>0"
24228         skip ${3=0},${4=50},${5=5%},${6=0},${7=0},${8=$7},${9=$8},${10=255}
24229 e[^-1] "Create $1x$2 taquin puzzle from image$?, with relief $4, border thickness $5, border outline $6 and
24230         outline color (${7--1})."
24231 repeat $! l[$>] nm={0,n}
24232   split_tiles $1,$2 r ${-min_wh},100%,100%,0
24233   100%,100%,1,1,1
24234   if ${"is_percent $5"} rectangle. {100*$5/2}%,{100*$5/2}%,{100-50*$5}%,{100-50*$5}%,1,0
24235   else rectangle. $5,$5,{w-1-$5},{h-1-$5},1,0 fi
24236   *. '1-2*(x/w<y/h)' *. $4
24237   repeat $!-1 l[$>,-1] split_opacity[0] +[0] . a[^-1] c endl done rm. c 0,255
24238   frame $6,$6,${7-10}
24239   if $3==3 f. 0 fi
24240   repeat $! mv[$>] {u($!)} done
24241   if $3==1 f[0] 0 elif $3==2 f. 0 fi
24242   append_tiles $1,$2
24243 nm $nm endl done
24244
24245#@cli tunnel : _level>=0,_factor>0,_centering_x,_centering_y,_opacity,_angle
24246#@cli : Apply tunnel effect on selected images.
24247#@cli : Default values: 'level=9', 'factor=80%', 'centering_x=centering_y=0.5', 'opacity=1' and 'angle=0'
24248#@cli : $ image.jpg tunnel 20
24249tunnel : check "${1=9}>=0 && ${2=80%}>0" skip ${3=0.5},${4=0.5},${5=0.1},${6=0}
24250  e[^-1] "Apply tunnel effect on image$?, with depth $1, factor $2, centering ($3,$4), opacity $5 and angle $6."
24251  repeat $! l[$>]
24252    repeat $1 +r. $2,$2,1,100%,5
24253    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]
24254    else j.. .,{({-2,w}-w)*$3},{({-2,h}-h)*$4},0,0,$5 rm. fi
24255    done
24256  endl done c 0,255
24257
24258#-----------------------------
24259#
24260#@cli :: Artistic
24261#
24262#-----------------------------
24263
24264#@cli boxfitting : _min_box_size>=1,_max_box_size>=0,_initial_density>=0,_nb_attempts>=1
24265#@cli : Apply box fitting effect on selected images, as displayed the web page:
24266#@cli : <http://www.complexification.net/gallery/machines/boxFittingImg/>.
24267#@cli : Default values: 'min_box_size=1', 'max_box_size=0', 'initial_density=0.1' and 'nb_attempts=3'.
24268#@cli : $ image.jpg boxfitting ,
24269boxfitting : check "isint(${1=3}) && $1>=1 && isint(${2=0}) && $2>=0 && ${3=0.1}>=0 && isint(${4=3}) && $4>=1"
24270  e[^-1] "Apply box fitting effect on image$?, with box sizes ($1,$2), density $3 and $4 attempts."
24271  min_size=$1
24272  max_size={if($2,$2,max(w,h))}
24273  repeat $! l[$>]
24274    nb_attempts=0 prec=5
24275    100%,100%
24276    repeat 1e8
24277
24278      # Add random non-intersecting squares with min size.
24279      if $><1 # Takes random points for the first iteration.
24280        100%,100% noise. {max(1e-3,$3)},2 ==. 1
24281      else # Then, try to take points near the median axis of the distance function otherwise.
24282        +distance. 1 +rand. 0,1 *[-2,-1] max_patch. {round($prec*$min_size)}
24283        prec={max(1,$prec*0.9)}
24284      fi
24285
24286      # Discard new squares that intersect something.
24287      dilate. $min_size area_fg. 0,1 ==. {($min_size)^2}
24288      +dilate.. 3 ==. 0 *[-2,-1] area_fg. 0,1 ==. {($min_size)^2}
24289      if !iM nb_attempts+=1 if $nb_attempts>$4 rm. break fi # If no new squares have been placed.
24290      else nb_attempts=0 fi
24291      +[-2,-1]
24292
24293      # Make current squares grown until max square size is reached.
24294      repeat int(($max_size-$min_size)/2)
24295        +dilate. 3 area_fg. 0,1 ==. {($min_size+2*$>+2)^2}
24296        if !iM rm. break fi  # No more squares to grow.
24297        -|[-2,-1]
24298      done
24299
24300    done
24301    blend shapeaverage0
24302  endl done
24303
24304#@cli brushify : [brush],_brush_nb_sizes>=1,0<=_brush_min_size_factor<=1,_brush_nb_orientations>=1,\
24305# _brush_light_type,0<=_brush_light_strength<=1,_brush_opacity,_painting_density[%]>=0,\
24306# 0<=_painting_contours_coherence<=1,0<=_painting_orientation_coherence<=1,_painting_coherence_alpha[%]>=0,\
24307# _painting_coherence_sigma[%]>=0,_painting_primary_angle,0<=_painting_angle_dispersion<=1
24308#@cli : Apply specified brush to create painterly versions of specified images.
24309#@cli : 'brush_light_type' can be { 0=none | 1=flat | 2=darken | 3=lighten | 4=full }.
24310#@cli : Default values: 'brush_nb_sizes=3', 'brush_min_size_factor=0.66', 'brush_nb_orientations=12', \
24311# 'brush_light_type=0', 'brush_light_strength=0.25', 'brush_opacity=0.8', 'painting_density=20%', \
24312# 'painting_contours_coherence=0.9', 'painting_orientation_coherence=0.9', 'painting_coherence_alpha=1', \
24313# 'painting_coherence_sigma=1', 'painting_primary_angle=0', 'painting_angle_dispersion=0.2'
24314#@cli : $ image.jpg 40,40 gaussian[-1] 10,4 spread[-1] 10,0 brushify[0] [1],1
24315brushify : check ${"is_image_arg $1"}" &&"\         # $1: [brush]
24316                    "isint(${2=4}) && $2>=1 &&"\      # $2: brush_nb_sizes
24317                    "${3=0.25}>=0 && $3<=1 &&"\       # $3: brush_min_size_factor
24318                    "isint(${4=12}) && $4>=1 &&"\     # $4: brush_nb_orientations
24319                    "isint(${5=4}) && $5>=0 &&"\      # $5: brush_light_type
24320                    "${6=0.07}>=0 && $6<=1 &&"\       # $6: brush_light_strength
24321                    "isnum(${7=0.75}) &&"\         # $7: brush_opacity
24322                    "${8=40%}>=0 && $8>=0 &&"\        # $8: painting_density[%]
24323                    "${9=0.7}>=0 && $9<=1 &&"\        # $9: painting_contours_coherence
24324                    "${10=1}>=0 && $10<=1 &&"\        # $10: painting_orientation_coherence
24325                    "${11=1}>=0 && ${12=0.5%}>=0 &&"\ # $11 and $12: painting_coherence_alpha and sigma
24326                    "isnum(${13=45}) &&"\          # $13: painting_primary_angle
24327                    "${14=0.2}>=0 && $14<=1"          # $14: painting_angle_dispersion
24328  e[^-1] "Brushify image$?, with brush $1."
24329
24330  # Precompute the set of oriented/resized brushes.
24331  pass$1 0 l.
24332    slices 0 max 1e-8 norm n 0,1 threshold 0.1,1 autocrop.
24333    repeat $4 +rotate[0] {360*$>/$4} done
24334    rm[0] n 0,1 threshold 0.1,1
24335    autocrop r ${-max_wh},1,1,0,0,0.5,0.5
24336    a z nm brush
24337    wb={w} hb={h} whb={wh} ls={255*$6}
24338    if $5==0 +f. 0
24339    elif $5==1 +n. -$ls,0
24340    elif $5==2 +g xy +[-2,-1] min. 0 n. -$ls,0
24341    elif $5==3 +g xy +[-2,-1] max. 0 n. 0,$ls
24342    else +g xy +[-2,-1] n. -$ls,$ls
24343    fi
24344    nm. brushlight
24345
24346    repeat $2-1
24347      ratio={v=(1+$>)/max(1,$2-1);100*((1-v)+$3*v)}%
24348      +r[brush,brushlight] $ratio,$ratio,100%,1,2
24349      ri[-2,-1] [brush],0,0,0.5,0.5
24350    done
24351    a[^:2] z a[^0] z
24352  endl
24353
24354  # Generate images with brushes.
24355  repeat $!-2 l[$>,brush,brushlight]
24356    s={0,s} nm={0,n} to_rgb[0] nm[0] img
24357
24358    # Generate set of random points with orientations.
24359    +diffusiontensors[img] $9,$10,$11,$12 nm. geometry
24360    +channels[geometry] 0 sh[geometry] 2 +[-2,-1] ^. 0.3 quantize. $2,0 *. -1 +. $2 -. 1 nm. contours
24361    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
24362    +to_rgba[img] nm. res
24363
24364    # Render filter.
24365    f[pts] "*
24366      begin(
24367        S2 = round(0.5*["$wb","$hb"]);
24368        brush_r = brush_g = brush_b = brush_a = vector"$whb"(255);
24369        ang = $13*pi/180;
24370        cu = [ cos(ang),sin(ang) ];
24371        cv = [ -cu[1],cu[0] ];
24372        T = mul(cu,cu,2) + $14*mul(cv,cv,2);
24373      );
24374      P = I;
24375      G = I(#"$geometry",P);
24376      ang = u(pi);
24377      V = [ G[0],G[1],G[1],G[2] ]*(T*[ cos(ang),sin(ang) ]);
24378      amp = i(#"$contours",P); #cut($2-round(5*$2*sqrt(G[0] + G[2])),0,$2-1);
24379      ang = round(((atan2(V[1],V[0])%(2*pi))*$4/(2*pi)))%$4;
24380      col = I(#"$img",P);
24381      ind = amp*$4 + ang;
24382      ref(crop(#"$brush",0,0,ind,0,"$wb","$hb",1,1),brush);
24383      ref(crop(#"$brushlight",0,0,ind,0,"$wb","$hb",1,1),brushlight);
24384      brush_r = cut(col[0] + brushlight,0,255);
24385      brush_g = cut(col[1] + brushlight,0,255);
24386      brush_b = cut(col[2] + brushlight,0,255);
24387      draw(#"$res",[brush_r,brush_g,brush_b,brush_a],P - S2,"$wb","$hb",1,4,$7,brush,1);
24388      P"
24389    k[res,brush,brushlight] mv[res] 0 nm[0] $nm to_colormode[0] {$s+($s%2)}
24390  endl done rm[brush,brushlight]
24391
24392#@cli cartoon : _smoothness,_sharpening,_threshold>=0,_thickness>=0,_color>=0,quantization>0
24393#@cli : Apply cartoon effect on selected images.
24394#@cli : Default values: 'smoothness=3', 'sharpening=150', 'threshold=20', 'thickness=0.25', 'color=1.5' \
24395# and 'quantization=8'.
24396#@cli : $ image.jpg cartoon 3,50,10,0.25,3,16
24397cartoon : skip ${1=3},${2=150},${3=20},${4=0.25},${5=1.5},${6=8}
24398  e[^-1] "Apply cartoon effect on image$?, with smoothness $1, sharpening $2, threshold $3, thickness $4, color $5
24399          and quantization $6."
24400  repeat $! l[$>] split_opacity l[0] to_rgb
24401  b $1 sharpen $2,1 c 0,255 n 0,255
24402  if $4 +edges $3 b. $4 >=. 0.9 else 100%,100%,1,1,1 fi
24403  rgb2lab.. s.. c *[-3,-2] $5 a[-4--2] c lab2rgb..
24404  quantize.. $6,1,-1
24405  n.. 0,255 *
24406  endl a c endl done
24407
24408#@cli color_ellipses : _count>0,_radius>=0,_opacity>=0
24409#@cli : Add random color ellipses to selected images.
24410#@cli : Default values: 'count=400', 'radius=5' and 'opacity=0.1'.
24411#@cli : $ image.jpg +color_ellipses ,,0.15
24412color_ellipses : skip ${1=1400},${2=5},${3=0.1}
24413  e[^-1] "Add $1 random color ellipses to image$?, with maximum radius $2 and opacity $1."
24414  repeat $1
24415    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
24416  done
24417
24418#@cli cubism : _density>=0,0<=_thickness<=50,_max_angle,_opacity,_smoothness>=0
24419#@cli : Apply cubism effect on selected images.
24420#@cli : Default values: 'density=50', 'thickness=10', 'max_angle=75', 'opacity=0.7' and 'smoothness=0'.
24421#@cli : $ image.jpg cubism ,
24422cubism : check "${1=50}>=0 && ${2=10}>=0 && $2<=50 && ${5=0}>=0" skip ${3=75},${4=0.7}
24423  e[^-1] "Apply cubism effect on image$?, with density $1, thickness $2, maximum angle $3 deg., opacity $4 and
24424          smoothness $5."
24425  if "!$1 || !$2 || !$3 || !$4" return fi
24426  repeat $! l[$>]
24427    w={w} h={h} s={s}
24428    P={round($2*max(w,h)/200)}
24429    N={round(1.5*$1*w*h/(4*$P)/100)}
24430
24431    # Define Header + nb vertices / primitives.
24432    ('CImg3d') +. 0.5 ({4*$N};$N)
24433
24434    # Generate list of random points.
24435    1,$N rand. $P,{$w-1-$P} +rand. $P,{$h-1-$P} a[-2,-1] x round.
24436
24437    # Generate list of primitives.
24438    ++. '-$P,-$P' ++.. '$P,-$P' ++... '$P,$P' ++[-4] '-$P,$P'
24439    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})
24440    r.. 5,$N,1,1,3 round.. 1 a[-2,-1] x
24441
24442    # Generate list of vertices.
24443    1,$N rand. {225-$3},{225+$3} *. {pi/180}
24444    +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}
24445    r... 400%,100%,1,1,0,2 +[-3,-1]
24446
24447    # Generate materials.
24448    (-128;$w;$h;$s) +b[0] $5
24449    if $N>1 4,{$N-1},1,1,-128,0,0,0 fi
24450    1,$N,1,1,1
24451
24452    # Apply effect on current image.
24453    y[1--1] a[1--1] y rv3d.
24454    if $4>=1 j3d[0] [1],0,0,0,1,2,0,0 rm[1]
24455    else +j3d[0] [1],0,0,0,1,2,0,0 rm[1] blend alpha,$4
24456    fi
24457
24458  endl done
24459
24460#@cli draw_whirl : _amplitude>=0
24461#@cli : Apply whirl drawing effect on selected images.
24462#@cli : Default value: 'amplitude=100'.
24463#@cli : $ image.jpg draw_whirl ,
24464draw_whirl : skip ${1=100}
24465  e[^-1] "Apply whirl drawing effect on image$? with amplitude $1."
24466  repeat $! l[$>]
24467    100%,100% noise. 70,2 ==. 1 *. 255 ri. .. &[-1,-2] smooth. $1,0,1,2,2
24468    sqrt. n. 0,255 equalize.
24469  endl done
24470
24471#@cli drawing : _amplitude>=0
24472#@cli : Apply drawing effect on selected images.
24473#@cli : Default value: 'amplitude=200'.
24474#@cli : $ image.jpg +drawing ,
24475drawing : skip ${1=200}
24476  e[^-1] "Apply drawing effect on image$? with amplitude $1."
24477  repeat $! l[$>] split_opacity l[0] to_rgb
24478    smooth $1,0.2,1,3,3 b 2 sharpen 1000 [0]
24479    r[0] 20,20,1,3,2 equalize[0] index[1] [0],1,1
24480    nm[1] {0,n},1 rm[0]
24481  endl a c endl done
24482
24483#@cli drop_shadow : _offset_x[%],_offset_y[%],_smoothness[%]>=0,0<=_curvature<=1,_expand_size={ 0 | 1 }
24484#@cli : Drop shadow behind selected images.
24485#@cli : Default values: 'offset_x=20', 'offset_y=offset_x', 'smoothness=5', 'curvature=0' and 'expand_size=1'.
24486#@cli : $ image.jpg drop_shadow 10,20,5,0.5 expand_xy 20,0 display_rgba
24487drop_shadow : check "${3=5}>=0 && ${4=0}>=0 && $4<=1" skip ${1=20},${2=$1},${5=1}
24488  e[^-1] "Drop shadow behind image$?, with offsets ($1,$2), smoothness $3 and curvature $4."
24489  to_a repeat $! l[$>]
24490    nm={0,n}
24491    dx={if(${is_percent\ $1},w*$1,$1)}
24492    dy={if(${is_percent\ $2},h*$2,$2)}
24493    sigma={if(${is_percent\ $3},min(w,h)*$3,$3)}
24494    w,h,s={[w,h,s]} sh. 100% coords=${autocrop_coords.\ 0} rm. z $coords  # Crop part with opaque pixels.
24495    r {w+abs($dx)},{h+abs($dy)},1,100%,0,0,{if($dx>0,0,1)},{if($dy>0,0,1)}
24496    r. {w+4*$sigma},{h+4*$sigma},1,100%,0,0,0.5,0.5
24497    +channels. 100%
24498    if !$4 shift. $dx,$dy # Flat shadow.
24499    else # Curved shadow.
24500      (0;{pi}) ri. ..,3 sin. *. -$4 +. 1 *. $dx
24501      (0,{pi}) ri. ..,3 sin. *. -$4 +. 1 *. $dy
24502      a[-2,-1] c warp.. .,1,0,0 rm.
24503    fi
24504    b. $sigma,0
24505    r. 100%,100%,1,2,0,0,0,0,0,1 mv. 0 blend alpha
24506    +channels. 100% >=. 1 * autocrop 0
24507    if !$5 $w,$h,1,$s j. ..,{arg(1,$coords)},{arg(2,$coords)} rm.. fi
24508    nm $nm
24509  endl done
24510
24511#@cli ellipsionism : _R>0[%],_r>0[%],_smoothness>=0[%],_opacity,_outline>0,_density>0
24512#@cli : Apply ellipsionism filter to selected images.
24513#@cli : Default values: 'R=10', 'r=3', 'smoothness=1%', 'opacity=0.7', 'outline=8' and 'density=0.6'.
24514#@cli : $ image.jpg ellipsionism ,
24515ellipsionism : check "${1=10}>0 && ${2=3}>0 && ${5=8}>0 && ${6=0.6}>0" skip ${3=1%},${4=0.7}
24516  e[^-1] "Apply ellipsionism filter to image$?, with radii ($1,$2), smoothness $3, opacity $4 and outline $5."
24517  repeat $! l[$>] to_color
24518
24519    # Compute contour angle.
24520    +luminance g. xy a[-2,-1] c b. $3 orientation.
24521    sh. 0 sh.. 1 atan2. .. *. {180/pi} +. 90 rm[-2,-1] channels. 1,1
24522
24523    # Render ellipses.
24524    100%,100%,1,4
24525    eval "
24526      const interpolation = 1;
24527      const N = $6*wh/max($1,$2);
24528      repeat (N,n,
24529        x = round(u(w-1)); y = round(u(h-1));
24530        ellipse(x,y,$1,$2,i(#-2,x,y)°,$4,I(#0,x,y),255);
24531        ellipse(x,y,-$1,-$2,i(#-2,x,y)°,$4,0xFFFFFFFF,I(#0,x,y)/$5,255);
24532      )"
24533    rm.. blend alpha
24534  endl done
24535
24536#@cli fire_edges : _edges>=0,0<=_attenuation<=1,_smoothness>=0,_threshold>=0,_nb_frames>0,_starting_frame>=0,\
24537# frame_skip>=0
24538#@cli : Generate fire effect from edges of selected images.
24539#@cli : Default values: 'edges=0.7', 'attenuation=0.25', 'smoothness=0.5', 'threshold=25', 'nb_frames=1', \
24540# 'starting_frame=20' and 'frame_skip=0'.
24541#@cli : $ image.jpg fire_edges ,
24542fire_edges : check "${1=0.7}>=0 && ${2=0.25}>=0 && $2<=1 && ${3=0.5}>=0 && ${4=25}>=0 && ${5=1}>0 &&
24543                    ${6=20}>=0 && ${7=0}>=0"
24544  e[^-1] "Generate fire effect from edges of image$?, with edges $1, attenuation $2, smoothness $3, threshold $4,
24545          $5 frames, starting frame $6 and frame skip $7."
24546  repeat $! l[$>] nm={0,n}
24547    norm +gradient_norm n. 0,1 roundify. $1 f[0] 0
24548    (0,0,0;0,0,0;1,1,1;0,1,0) *. {(1-$2^4)/4}
24549    repeat $5*(1+$7)+$6
24550      {0,w},{0,h} rand. 0,255 *. [1]
24551      b. $3
24552      if $4 >=. $4% else equalize. fi
24553      n. 0,255
24554      j[0] .,0,0,0,0,1,[1],1 rm.
24555      correlate[0] [2]
24556      if $>>=$6" && "($>-$6)%($7+1)==0 [0] fi
24557    done rm[0-2]
24558  nm $nm endl done
24559  (0,255,255,255,255^0,0,255,255,255^0,0,0,128,255) r. 256,1,1,3,3
24560  map[^-1] . rm.
24561
24562#@cli fractalize : 0<=detail_level<=1
24563#@cli : Randomly fractalize selected images.
24564#@cli : Default value: 'detail_level=0.8'
24565#@cli : $ image.jpg fractalize ,
24566fractalize : check "${1=0.8}>=0 && $1<=1"
24567  e[^-1] "Randomly fractalize image$?, with detail level $1."
24568  xc=0.4433
24569  yc=0.2645
24570  delta=0.1
24571  c0r=0.317
24572  c0i=0.03
24573  repeat $! l[$>] nm={0,n}
24574    luminance equalize 256 b 0.25% n 0,255
24575    100%,100%
24576    dx={$delta*w/max(w,h)}
24577    dy={$delta*h/max(w,h)}
24578    x0={$xc-$dx/2}
24579    y0={$yc-$dy/2}
24580    x1={$xc+$dx/2}
24581    y1={$yc+$dy/2}
24582    mandelbrot. $x0,$y0,$x1,$y1,256,1,$c0r,$c0i
24583    +==. 0 inpaint.. . rm.
24584    n. 0,256
24585    16,1,1,3 rand. 0,255 r. 256,1,1,3,3 map.. . rm.
24586    s. c
24587    i[2,3] [0]
24588    s={0.1*(1-$1)}
24589    parallel "register_nonrigid[1] [0],"$s",5","register_nonrigid[3] [2],"$s",5","register_nonrigid[5] [4],"$s",5"
24590    rm[0,2,4] a c nm $nm
24591  endl done
24592
24593#@cli glow : _amplitude>=0
24594#@cli : Add soft glow on selected images.
24595#@cli : Default value: 'amplitude=1%'.
24596#@cli : $ image.jpg glow ,
24597glow : skip ${1=1%}
24598  e[^-1] "Add soft glow on image$?, with amplitude $1."
24599  repeat $! l[$>] split_opacity +b[0] $1 n. [0] blend_edges[0,-1] 1 a c endl done
24600
24601#@cli halftone : nb_levels>=2,_size_dark>=2,_size_bright>=2,_shape={ 0=square | 1=diamond | 2=circle | \
24602# 3=inv-square | 4=inv-diamond | 5=inv-circle },_smoothness[%]>=0
24603#@cli : Apply halftone dithering to selected images.
24604#@cli : Default values: 'nb_levels=5', 'size_dark=8', 'size_bright=8', 'shape=5' and 'smoothnesss=0'.
24605#@cli : $ image.jpg halftone ,
24606halftone : check "${1=5}>=2 && ${2=8}>=2 && ${3=8}>=2 && ${5=0}>=0" skip ${4=5}
24607  s0="square" s1="diamond" s2="circle" s3="inv-square" s4="inv-diamond" s5="inv-circle"
24608  e[^-1] "Apply halftone dithering to image$?, with $1 levels, dark size $3, bright size $4, "\
24609         ${s$4}" shape and smoothness $5."
24610  repeat $! l[$>] s c repeat $! l[$>]
24611    (0,255) a y quantize $1,0 rows 0,{h-2}
24612    repeat $1
24613      s={round(($2*$<+$3*$>)/($1-1))}
24614      $s,$s =. 1,50%,50% distance. 1,{$4%3} +shift. {round(w/2)},{round(h/2)},0,0,2 min[-2,-1]
24615      if $4>=3 <. {100*$</($1-1.1)}% *. {-255} +. {255-$>}
24616      else <. {100*$>/($1-1.1)}% *. 255 -. $>
24617      fi
24618      ri. ..,0,2 b. $5 +==.. $> *[-2,-1] +[-2,-1]
24619    done endl done a c
24620  endl done
24621
24622#@cli hardsketchbw : _amplitude>=0,_density>=0,_opacity,0<=_edge_threshold<=100,_is_fast={ 0 | 1 }
24623#@cli : Apply hard B&W sketch effect on selected images.
24624#@cli : Default values: 'amplitude=1000', 'sampling=3', 'opacity=0.1', 'edge_threshold=20' and 'is_fast=0'.
24625#@cli : $ image.jpg +hardsketchbw 200,70,0.1,10 median[-1] 2 +local reverse blur[-1] 3 blend[-2,-1] overlay endlocal
24626hardsketchbw : skip ${1=300},${2=50},${3=0.1},${4=20},${5=0}
24627  e[^-1] "Apply hard B&W sketch effect on image$?, with amplitude $1, density $2, opacity $3 and edge threshold $4."
24628  if !$2 channels 0 f 255 return fi
24629  luminance n 0,1
24630
24631  if $5 # Fast version.
24632    repeat $! l[$>] nm={0,n}
24633      g xy rv *.. -1 a c
24634      if $4 +norm >=. $4% * fi
24635      100%,100%,1,1,255 quiver. ..,{max(1,10-$2/6)},$1,0,$3 rm..
24636    nm $nm endl done
24637
24638  else # Slower version.
24639    repeat $! l[$>] nm={0,n}
24640
24641      # Isolate starting points and tangents.
24642      w={w} h={h} g xy rv *.. -1 a c * $1
24643      +norm >=. $4% 100%,100% noise. $2,2 ==. 1 *[-2,-1]
24644
24645      # Retrieve points coordinates and corresponding tangents.
24646      pointcloud3d. s3d. rm[-6--5,-3--1] r. 3,{h/3},1,1,-1 s. x rm. a[-2,-1] c
24647      warp.. .,0,0 +-. .. +[-3,-2] s[-2,-1] c
24648
24649      # Convert as a 3d object and render on white background.
24650      i... 1,{h} 1,{h} a[-6--1] x
24651      i.. ('CImg3d') i.. ({2*h},{h})
24652      1,{h},1,1,2 1,{h},1,1,2*y ++. 1 a[-3--1] x
24653      3,{h} 1,{h},1,1,$3 y[-6--1] a[-6--1] y
24654      $w,$h,1,1,255 j3d. ..,0,0,0,1,1,0,0 rm..
24655    nm $nm endl done
24656  fi
24657
24658#@cli hearts : _density>=0
24659#@cli : Apply heart effect on selected images.
24660#@cli : Default value: 'density=10'.
24661#@cli : $ image.jpg hearts ,
24662hearts : skip ${1=10}
24663  e[^-1] "Apply heart filter on image$?, with density $1."
24664  repeat $! l[$>]
24665    100%,100%,1 noise. $1,2 ==. 1 ri. .. n. 0,1 *[-1,-2] _heart9x7
24666    dilate.. . rm.
24667  endl done
24668
24669_heart9x7 :
24670  (9,7,1,1,0,1,-1,2,-3,2,-1,4,-1,13,-1,7,-3,5,-5,3,-7,1,-4)
24671  decompress_rle.
24672
24673#@cli houghsketchbw : _density>=0,_radius>0,0<=_threshold<=100,0<=_opacity<=1,_votesize[%]>0
24674#@cli : Apply hough B&W sketch effect on selected images.
24675#@cli : Default values: 'density=100', 'radius=3', 'threshold=100', 'opacity=0.1' and 'votesize=100%'.
24676#@cli : $ image.jpg +houghsketchbw ,
24677houghsketchbw : check "${1=100}>=0 && ${2=3}>=0 && ${3=100}>=0 && $3<=100 && ${4=0.1}>=0 && $4<=1 && ${5=100%}>0"
24678  e[^-1] "Apply hough B&W sketch effect on image$?, with density $1, radius $2, threshold $3, opacity $4
24679          and votesize $5."
24680  luminance repeat $! l[$>] nm={0,n}
24681
24682    # Compute normalized Hough transform.
24683    res={round(if(${is_percent\ $5},$5*max(w,h),$5))} w={w} h={h} rhomax={sqrt(w^2+h^2)/2}
24684    hough $res,$res n 0,255
24685
24686    # Retrieve coordinates of maximas in hough space.
24687    normalize_local. $1,$2 >=. $3% pointcloud3d.
24688    s3d. rm[-6--5,-3--1] r. 3,{h/3},1,1,-1 columns. 0,1
24689
24690    # Convert to (x0,y0)-(x1,y1) line coordinates.
24691    s. x,2
24692    *.. {2*pi/$res}                 # theta
24693    *. {$rhomax/$res}               # rho
24694    +cos.. *. .. +. {$w/2}          # x
24695    +sin... *. ... +. {$h/2}        # y
24696    rm...                           # Remove rho
24697    i... ...
24698    cos[-4] sin... *[-4,-3] 10000   # cos(t) sin(t)
24699    ++.. ...                        # x + sin(t)
24700    +-.. [-5]                       # y - cos(t)
24701    -[-4] [-5]                      # x - sin(t)
24702    +... [-6]                       # y + cos(t)
24703    rm[-6,-5]
24704
24705    # Transform as a 3D object.
24706    i... 1,{h} 1,{h} a[-6--1] x                 # Vertices
24707    i.. ('CImg3d') i.. ({2*h},{h})              # Header and size.
24708    1,{h},1,1,2 1,{h},1,1,2*y ++. 1 a[-3--1] x  # Primitives.
24709    3,{h},1,1,0 1,{h},1,1,$4                    # Colors and opacities
24710    y[-6--1] a[-6--1] y
24711
24712    # Render on a white image.
24713    $w,$h,1,1,255 j3d. ..,0,0,0,1,1,0,0 rm..
24714  nm $nm endl done
24715
24716#@cli lightrays : 100<=_density<=0,_center_x[%],_center_y[%],_ray_length>=0,_ray_attenuation>=0
24717#@cli : Generate ray lights from the edges of selected images.
24718#@cli : Default values: 'density=50%', 'center_x=50%', 'center_y=50%', 'ray_length=0.9' and 'ray_attenuation=0.5'.
24719#@cli : $ image.jpg +lightrays , + cut 0,255
24720lightrays : check "${1=50}>=0 && $1<=100 && ${4=1}>=0 && ${5=1}>=0" skip ${2=50%},${3=50%}
24721  e[^-1] "Generate ray lights from image$?, with density $1, center point ($2,$3), ray length $4 and attenuation $5."
24722  repeat $! l[$>]
24723    gradient_norm >= $1% euclidean2polar $2,$3
24724    repeat log2(w) +shift. {2^$>} +[-2,-1] done
24725    function1d 0.5,0,1,{$4*w},1,{1+($4+1-$5)*w},0 r. {-2,w},1,1,1,0
24726    (1,{w}) r. {-2,w},1,1,1,3 /[-2,-1]
24727    ri. .. *[-2,-1] polar2euclidean $2,$3 n 0,255
24728  endl done
24729
24730#@cli light_relief : _ambient_light,_specular_lightness,_specular_size,_darkness,_light_smoothness,_xl,_yl,_zl,\
24731# _zscale,_opacity_is_heightmap={ 0 | 1 }
24732#@cli : Apply relief light to selected images.
24733#@cli : Default values(s) : 'ambient_light=0.3', 'specular_lightness=0.5', 'specular_size=0.2', 'darkness=0', \
24734# 'xl=0.2', 'yl=zl=0.5',
24735#@cli : 'zscale=1', 'opacity=1' and 'opacity_is_heightmap=0'.
24736#@cli : $ image.jpg blur 2 light_relief 0.3,4,0.1,0
24737light_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}
24738  e[^-1] "Apply relief light to image$?."
24739  repeat $! l[$>]
24740    ({-$6},{1-$6};{-$6},{1-$6}^{-$7},{-$7};{1-$7},{1-$7}^$8,$8;$8,$8) r. ..,..,1,3,3  # Create light vector field.
24741    if $10 +channels.. 3 to_rgb... else +to_rgb.. norm. fi
24742    b. $5% g. xy 100%,100%,1,1,$9 a[-3--1] c                                          # Create normal vector field.
24743    orientation[-2,-1] *[-2,-1] s. c +[-3--1]                                         # Normalized scalar product.
24744    100%,100% =. 1,{$6*100}%,{$7*100}% distance. 1 sqr. *. -1                         # Compute specular attenuation.
24745    /. {($3*max(w,h))^2} exp. *. $2 +. $1
24746    *[-2,-1] -. $4 *. {-2,iM}
24747    split_opacity[0] +[0,-1] a c c 0,255
24748  endl done
24749
24750#@cli linify : 0<=_density<=100,_spreading>=0,_resolution[%]>0,_line_opacity>=0,_line_precision>0,\
24751# _mode={ 0=subtractive | 1=additive }
24752#@cli : Apply linify effect on selected images.
24753#@cli : The algorithm is inspired from the one described on the webpage <http://linify.me/about>.
24754#@cli : Default values: 'density=50', 'spreading=2', 'resolution=40%', 'line_opacity=10', 'line_precision=24' \
24755# and 'mode=0'.
24756#@cli : $ image.jpg linify 60
24757linify : check "${1=40}>=0 && $1<=100 && ${2=2}>=0 && ${3=40%}>0 && ${4=10}>=0 && isint(${5=24}) && $5>0 &&
24758                isbool(${6=0})"
24759  e[^-1] "Apply linify effect on image$?, with density $1, spreading $2, resolution $3, line opacity $4,
24760          line precision $5 and "${"-arg 1+$6,subtractive,additive"}" mode."
24761  repeat $! l[$>] remove_opacity nm={n}
24762    100%,100%,1,{s},$6?0:255
24763    if {0,w>h} r2dx[0] {${"-is_percent $3"}?max(1,$3*w):min(w,$3)}
24764    else r2dy[0] {${"-is_percent $3"}?max(1,$3*h):min(h,$3)}
24765    fi
24766    n[0] 0,100
24767    if narg($_debug)" && "!{*,w} w[] ${-fitscreen[]\ {1,[w,h]}} fi
24768    eval "
24769      is_in(ind,P) = (P[0]>=0 && P[0]<w#ind && P[1]>=0 && P[1]<h#ind);
24770      const add = $6;
24771      const density = add?100 - $1:$1;
24772      const spreading = max(0.1,$2);
24773      const opacity = $4;
24774      const precision = $5;
24775      const om2add = 1 - 2*add;
24776
24777      fact = [ w/w#0, h/h#0,1,1 ];
24778      nb_lines = 0;
24779      ref0 = add?iM#0:im#0;
24780      do (
24781        S = stats(#0);
24782        P0 = add?[ S[8],S[9],0,S[11] ]:[ S[4],S[5],0,S[7] ]; # coords of min or max intensity
24783        ref = S[add];
24784        best_ang = best_avg = add?0:inf;
24785        repeat (precision,k,
24786          ang = u(360)*pi/180;
24787          dP = [ cos(ang), sin(ang),0,0 ];
24788          dP/=max(abs(dP));
24789          N = avg = 0;
24790          P = P0; while (is_in(#0,P), avg+=i(#0,P); ++N; P+=dP);
24791          P = P0; while (is_in(#0,P), avg+=i(#0,P); ++N; P-=dP);
24792          avg/=N;
24793          if (add?(avg>best_avg):(avg<best_avg), best_avg = avg; best_ang = ang);
24794        );
24795        dP = [ cos(best_ang), sin(best_ang),0,0 ];
24796        dP/=max(abs(dP));
24797        P = P0; while (is_in(#0,P), i(#0,P)+=om2add*spreading; P+=dP);
24798        P = P0; while (is_in(#0,P), i(#0,P)+=om2add*spreading; P-=dP);
24799        P = P0*fact; while (is_in(#1,P), i(#1,P)-=om2add*opacity*spreading; P+=dP);
24800        P = P0*fact; while (is_in(#1,P), i(#1,P)-=om2add*opacity*spreading; P-=dP);
24801        if (!(nb_lines%250),
24802          progress = density==ref0?100:round(100*(ref - ref0)/(density - ref0));
24803          run('progress ',vtos(progress));
24804          if (narg("$_debug"),
24805            run('+c[1] 0,255 r. {*,w},{*,h},1,3,2 to. ',vtos(progress),'%,1%,1%,5%,1 w. -1,-1,0 rm.')
24806          );
24807        );
24808        ++nb_lines;
24809        breakpoint();
24810      _(while), add?(ref>density):(ref<density))"
24811    k. c 0,255 nm $nm
24812  endl done
24813
24814#@cli mosaic : 0<=_density<=100
24815#@cli : Create random mosaic from selected images.
24816#@cli : Default values: 'density=30'.
24817#@cli : $ image.jpg mosaic , +fill "I!=J(1) || I!=J(0,1)?[0,0,0]:I"
24818mosaic : check "${1=30}>=0"
24819  e[^-1] "Apply mosaic effect on image$?, with density $1."
24820  repeat $! l[$>]
24821    100%,100%,1,2,'u<0.25*($1%)^4?[u,1]' s. c
24822    distance. 1 *. -1 watershed.. . rm.
24823    blend shapeaverage
24824  endl done
24825
24826#@cli old_photo
24827#@cli : Apply old photo effect on selected images.
24828#@cli : $ image.jpg old_photo
24829old_photo :
24830  e[^-1] "Apply old photo effect on image$?."
24831  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
24832
24833#@cli pencilbw : _size>=0,_amplitude>=0
24834#@cli : Apply B&W pencil effect on selected images.
24835#@cli : Default values: 'size=0.3' and 'amplitude=60'.
24836#@cli : $ image.jpg pencilbw ,
24837pencilbw : skip ${1=0.3},${2=60}
24838  e[^-1] "Apply B&W pencil effect on image$?, with size $1 and amplitude $2."
24839  repeat $! l[$>] split_opacity l[0]
24840    norm b $1 sharpen 4000 smooth $2,0,1 equalize sqrt n 0,255
24841  endl a c endl done
24842
24843#@cli pixelsort : _ordering={ + | - },_axis={ x | y | z | xy | yx },_[sorting_criterion],_[mask]
24844#@cli : Apply a 'pixel sorting' algorithm on selected images, as described in the page :
24845#@cli : <http://satyarth.me/articles/pixel-sorting/>.
24846#@cli : Default values: 'ordering=+', 'axis=x' and 'sorting_criterion=mask=(undefined)'.
24847#@cli : $ image.jpg +norm +ge[-1] 30% +pixelsort[0] +,y,[1],[2]
24848pixelsort : check "(str1='${1=+}'; str1=='+' || str1=='-') && "\
24849                  "(str2='${2=x}'; str2=='x' || str2=='y' || str2=='z' || str2=='xy' || str2=='yx') && "\
24850                  "('${3=}'==0 || "${"is_image_arg $3"}") && "\
24851                  "('${4=}'==0 || "${"is_image_arg $4"}")"
24852  s0="descending" s1="ascending"
24853  if '$3'!=0" && "'$4'!=0
24854    e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2,
24855            with sorting criterion $3 and mask $4."
24856  elif '$3'!=0" && "'$4'==0
24857    e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2,
24858            with sorting criterion $3."
24859  elif '$3'==0" && "'$4'!=0
24860    e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2,
24861            with mask $4."
24862  else
24863    e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2."
24864  fi
24865  repeat $!
24866    if '$3'!=0 pass$3 0 else +compose_channels[$>] + fi
24867    if '$4'!=0 pass$4 0 else [$>],[$>],[$>],1,1 fi
24868    l[$>,-2,-1]
24869      nm={0,n} >=. 50% mv.. 0 a c
24870      if '$2'=='x';
24871        _pixelsort $1 channels 1,{s-2}
24872      elif '$2'=='y';
24873        permute yxzc _pixelsort $1 channels 1,{s-2} permute yxzc
24874      elif '$2'=='z';
24875        permute zxyc _pixelsort $1 channels 1,{s-2} permute yzxc
24876      elif '$2'=='xy';
24877        _pixelsort $1 permute yxzc _pixelsort $1 channels 1,{s-2} permute yxzc
24878      elif '$2'=='yx';
24879        permute yxzc _pixelsort $1 permute yxzc _pixelsort $1 channels 1,{s-2}
24880      fi
24881      nm $nm
24882    endl
24883  done
24884
24885_pixelsort :
24886  eval. ">begin(ret = [ 0 ]; const m = s - 1);
24887          i(x,y,z,m) && !i(#-1,x - 1,y,z,m)?(
24888            for (x1 = x + 1, x1<w && i(x1,y,z,m), ++x1);
24889            --x1>x?run('+z[0] ',x,',',y,',',z,',',0,',',x1,',',y,',',z,',',m - 1,' sort. $1,x j.. .,',x,',',y,' rm.');
24890          ); ret"
24891
24892#@cli polaroid : _size1>=0,_size2>=0
24893#@cli : Create polaroid effect in selected images.
24894#@cli : Default values: 'size1=10' and 'size2=20'.
24895#@cli : $ image.jpg to_rgba polaroid 5,30 rotate 20 drop_shadow , drgba
24896polaroid : check "${1=10}>=0 && ${2=20}>=0"
24897  e[^-1] "Create polaroid effect in image$?, with borders sizes $1 and $2."
24898  - 255 r {100+$1}%,{100+$1}%,1,100%,0,0,0.5,0.5 r 100%,{100+$2}%,1,100%,0,0,0 + 255
24899
24900#@cli polygonize : _warp_amplitude>=0,_smoothness[%]>=0,_min_area[%]>=0,_resolution_x[%]>0,_resolution_y[%]>0
24901#@cli : Apply polygon effect on selected images.
24902#@cli : Default values: 'warp_amplitude=300', 'smoothness=2%', 'min_area=0.1%', 'resolution_x=resolution_y=10%'.
24903#@cli : $ image.jpg image.jpg polygonize 100,10 +fill "I!=J(1) || I!=J(0,1)?[0,0,0]:I"
24904polygonize : check "${1=300}>=0 && ${2=2%}>=0 && ${3=0.1%}>=0 && ${4=10%}>0 && ${5=$4}>0"
24905  e[^-1] "Polygonize image$? with warp amplitude $1, smoothness $2, minimal area $3 and resolutions ($4,$5)."
24906   repeat $! l[$>]
24907    +b $2 gradient_norm. g. a[-2,-1] c channels. 0,2 *. {1/0.1+max(abs(im),abs(iM))}
24908    resx={max(1,round(if(${is_percent\ $4},w*$4,w/$4)-1))}
24909    resy={max(1,round(if(${is_percent\ $5},h*$5,h/$5)-1))}
24910    plane3d 1,1,$resx,$resy *3d. {0,w-1},{0,h-1},1
24911    s3d. rm.. i.. (0;{h-1}) r.. 3,{h},1,1,3 round.. y..
24912    [-4] a[-7--2] y r. 3,{h/3},1,1,-1 z. 0,1 permute. yzcx
24913    repeat $1 +warp[1] .,0,0 +[-2,-1] done
24914    permute. cxyz z. 0,2 y. j[2] .,0,8 rm[-3,-1]
24915    [0],[0] j3d. [1],0,0,0,1,2 rm[1]
24916    if $3>0
24917      min_area={0,if(${is_percent\ $3},$3*w*h,$3)}
24918      +area. 0,1 >=. $min_area +.. 1 *.. . distance. 1 *. -1 watershed.. . rm.
24919    fi
24920    blend shapeaverage
24921  endl done
24922
24923#@cli poster_edges : 0<=_edge_threshold<=100,0<=_edge_shade<=100,_edge_thickness>=0,_edge_antialiasing>=0,\
24924# 0<=_posterization_level<=15,_posterization_antialiasing>=0
24925#@cli : Apply poster edges effect on selected images.
24926#@cli : Default values: 'edge_threshold=40', 'edge_shade=5', 'edge_thickness=0.5', 'edge_antialiasing=10', \
24927# 'posterization_level=12' and 'posterization_antialiasing=0'.
24928#@cli : $ image.jpg poster_edges ,
24929poster_edges : check "${1=40}>=0 && $1<=100 && ${2=5}>=0 && $2<=100 && ${3=0.5}>=0 && ${4=10}>=0 &&
24930                      ${5=12}>=0 && $5<=15 && ${6=0}>=0"
24931  e[^-1] "Apply poster edge on image$?, with edge threshold $1, edge shade $2, edge thickness $3,
24932          edge antialiasing $4, $5 level of posterization and posterization antialiasing $6."
24933  repeat $! l[$>] split_opacity l[0]
24934    +g xy,1 a[-2,-1] c norm. b. $3 n. 0,255
24935    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
24936    if $4 smooth. {min(50,$4)},0,1,{$4/40},{$4/40},0.8,90 fi
24937    if $5 autoindex[0] {round((4-sqrt($5+1))*32+2)} fi
24938    if $6 smooth[0] {min(50,$6)},0,1,{$6/40},{$6/40},0.8,90 fi
24939    *
24940  endl a c endl done
24941
24942#@cli poster_hope : _smoothness>=0
24943#@cli : Apply Hope stencil poster effect on selected images.
24944#@cli : Default value: 'smoothness=3'.
24945#@cli : $ image.jpg poster_hope ,
24946poster_hope : check "${1=3}>=0"
24947  e[^-1] "Apply Hope stencil poster effect on image$?, with smoothness $1."
24948  repeat $! l[$>] to_rgb
24949    apc "smooth 200,0,1,$1,1"
24950    quantize 7,0 f 'if(i!=5,i,i+1-2*(y%2))'
24951    (0,32,47;0,32,47;209,1,23;209,1,23;90,141,145;-1,-1,-1;253,221,138) permute. yzcx
24952    map[0] [1] rm[1]
24953  endl done
24954
24955#@cli rodilius : 0<=_amplitude<=100,_0<=thickness<=100,_sharpness>=0,_nb_orientations>0,_offset,\
24956# _color_mode={ 0=darker | 1=brighter }
24957#@cli : Apply rodilius (fractalius-like) filter on selected images.
24958#@cli : Default values: 'amplitude=10', 'thickness=10', 'sharpness=400', 'nb_orientations=7', 'offset=0' \
24959# and 'color_mode=1'.
24960#@cli : $ image.jpg rodilius 12,10,300,10 normalize_local 10,6
24961#@cli : $ image.jpg normalize_local 10,16 rodilius 10,4,400,16 smooth 60,0,1,1,4 normalize_local 10,16
24962rodilius : check "${1=10}>=0 && $1<=200 && ${2=10}>=0 && $2<=100 && ${3=400}>=0 && ${4=7}>0" skip ${5=0},${6=1}
24963  e[^-1] "Apply rodilius filter on image$? with amplitude $1, thickness $2, sharpness $3, $4 orientations,
24964          offset $5 and "${arg\ 1+!$6,brighter,darker}" color mode."
24965  repeat $! l[$>] split_opacity rv
24966    if !$6 negate. fi
24967    +f. 0 nm. {-2,n}
24968    repeat round($4)
24969      angle={$5+$>*180/round($4)}
24970      +blur_linear.. $1%,{$1*$2/100}%,$angle,1 b. 0.7 sharpen. $3 max[-2,-1]
24971    done rm..
24972    if !$6 negate. fi
24973  rv a c endl done
24974
24975#@cli sketchbw : _nb_angles>0,_start_angle,_angle_range>=0,_length>=0,_threshold>=0,_opacity,_bgfactor>=0,\
24976# _density>0,_sharpness>=0,_anisotropy>=0,_smoothness>=0,_coherence>=0,_is_boost={ 0 | 1 },_is_curved={ 0 | 1 }
24977#@cli : Apply sketch effect to selected images.
24978#@cli : Default values: 'nb_angles=2', 'start_angle=45', 'angle_range=180', 'length=30', 'threshold=3', \
24979# 'opacity=0.03', 'bgfactor=0', 'density=0.6', 'sharpness=0.1', 'anisotropy=0.6', 'smoothness=0.25', 'coherence=1', \
24980# 'is_boost=0' and 'is_curved=1'.
24981#@cli : $ image.jpg +sketchbw 1 reverse blur[-1] 3 blend[-2,-1] overlay
24982sketchbw :
24983  check "${1=2}>0 && ${3=180}>=0 && ${4=30}>=0 && ${5=3}>=0 && ${7=0}>=0 && ${8=0.6}>0 && ${9=0.1}>=0 &&
24984         ${10=0.6}>=0 && ${11=0.25}>=0 && ${12=1}>=0"
24985  skip ${2=45},${6=0.03},${13=0},${14=0}
24986  e[^-1] "Apply B&W sketch effect on image$?."
24987  nb_angles,start_angle,angle_range,length,threshold,opacity,bgfactor,density,sharpness,\
24988  anisotropy,smoothness,coherence,is_boost,is_curved=${1-14}
24989  length={max($length,1)}
24990  repeat $! l[$>]
24991    {0,[w,h,1,1,0]}                                                    # [1] = canvas to draw onto
24992    +gradient_norm[0] sqrt.
24993    diffusiontensors[0] $sharpness,$anisotropy,$smoothness,$coherence
24994    a[0,-1] c                                                          # [0] = field of stroke tensors + gradient norm
24995    1,{$density*wh/sqrt($length)},1,2,round(u([w#0,h#0]-1))            # [2] = set of random points
24996
24997    repeat $nb_angles
24998
24999      # Compute vector field for considered orientation.
25000      [0],[0],1,2,"
25001        const angle = ("$start_angle" + "$>"*"$angle_range"/"$nb_angles")*pi/180;
25002        const ca = cos(angle);
25003        const sa = sin(angle);
25004        T = I(#0);
25005        U = [ T[0]*ca + T[1]*sa, T[1]*ca + T[2]*sa ];
25006        if ("$is_boost",U/=(1e-8 + norm(U)));
25007        U"
25008
25009      # Draw curved or straight strokes.
25010      if $is_curved
25011        f[2] "*
25012          oub = ovb = ouf = ovf = 0;
25013          oixb = xb = xf = i0;
25014          oiyb = yb = yf = i1;
25015          oixf = oiyf = -1;
25016          op = "$opacity" * (i(#0,xf,yf,0,3)<"$threshold"?"$bgfactor":1);
25017          omop = 1 - op;
25018
25019          if (op>0, repeat ("$length",dl,
25020
25021            # Forward
25022            ixf = round(xf);
25023            iyf = round(yf);
25024            if (ixf!=oixf || iyf!=oiyf, (i(#1,ixf,iyf)*=omop)+=op; oixf = ixf; oiyf = iyf);
25025            uf = i(#-1,xf,yf,0,0,1,1);
25026            vf = i(#-1,xf,yf,0,1,1,1);
25027            if (ouf*uf + ovf*vf<0, uf*=-1; vf*=-1);
25028            xf+=uf;
25029            yf+=vf;
25030            ouf = uf;
25031            ovf = vf;
25032
25033            # Backward
25034            ub = i(#-1,xb,yb,0,0,1,1);
25035            vb = i(#-1,xb,yb,0,1,1,1);
25036            if (oub*ub + ovb*vb<0, ub*=-1; vb*=-1);
25037            xb-=ub;
25038            yb-=vb;
25039            oub = ub;
25040            ovb = vb;
25041            ixb = round(xb);
25042            iyb = round(yb);
25043            if (ixb!=oixb || iyb!=oiyb, (i(#1,ixb,iyb)*=omop)+=op; oixb = ixb; oiyb = iyb);
25044          ));
25045          I"
25046      else
25047        f[2] "*
25048          const l = "$length";
25049          x = i0;
25050          y = i1;
25051          u = i(#-1,x,y,0,0);
25052          v = i(#-1,x,y,0,1);
25053          op = "$opacity" * (i(#0,x,y,0,3)<"$threshold"?"$bgfactor":1);
25054          omop = 1 - op;
25055          polygon(#1,2,x - l*u,y - l*v,x + l*u,y + l*v,op,1);
25056          I"
25057      fi
25058      rm.
25059    done
25060    k.. * -1 n 0,255
25061  endl done
25062
25063#@cli sponge : _size>0
25064#@cli : Apply sponge effect on selected images.
25065#@cli : Default value: 'size=13'.
25066#@cli : $ image.jpg sponge ,
25067sponge : skip ${1=13}
25068  e[^-1] "Apply sponge filter on image$?, with brush size $1."
25069  repeat $! l[$>]
25070    100%,100%,1,1 noise. 20,2 ==. 1 ri. .. n. 0,1 *[-1,-2]
25071    _circle $1 dilate.. . rm.
25072  endl done
25073
25074_circle :
25075  if $1%2==0 2,2 else 1 fi
25076  +. 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
25077
25078#@cli stained_glass : _edges[%]>=0, shading>=0, is_thin_separators={ 0 | 1 }
25079#@cli : Generate stained glass from selected images.
25080#@cli : Default values: 'edges=40%', 'shading=0.2' and 'is_precise=0'.
25081#@cli : $ image.jpg stained_glass 20%,1 cut 0,20
25082stained_glass : check "${1=40%}>=0 && ${2=0.2}>=0" skip ${3=0}
25083  e[^-1] "Apply stained glass effect on image$?, with edges $1, shading $2 and thin-separators "\
25084         ${arg\ 1+!$3,enabled,disabled}"."
25085  repeat $! l[$>]
25086    im={im-1} - $im  # Ensure strict positiveness of image labels.
25087    +gradient_norm >=. $1 *.. .
25088    distance. 1 sharpen. 1e10 !=. 0
25089    if $3 skeleton. 0 fi
25090    distance. 1 watershed.. . +.. $im
25091    n. 0,1  ^. $2 *
25092  endl done
25093
25094#@cli stars : _density[%]>=0,_depth>=0,_size>0,_nb_branches>=1,0<=_thickness<=1,_smoothness[%]>=0,_R,_G,_B,_opacity
25095#@cli : Add random stars to selected images.
25096#@cli : Default values: 'density=10%', 'depth=1', 'size=32', 'nb_branches=5', 'thickness=0.38', 'smoothness=0.5', \
25097# 'R=G=B=200' and 'opacity=1'.
25098#@cli : $ image.jpg stars ,
25099stars : check "${1=10%}>=0 && ${2=1}>=0 && ${3=32}>0 && ${4=5}>=1 && ${5=0.38}>=0 && $5<=1 && ${6=0.5}>=0"
25100        skip ${7=200},${8=$7},${9=$8},${10=1}
25101  e[^-1] "Add $1 random stars to image$?, with depth $2, size $3, $4 branches, thickness $5, smoothness $6,
25102          color ($7,$8,$9) and opacity $10."
25103  if !$1 return fi
25104
25105  # Generate star sprites.
25106  star3d $4,$5 col3d. 255 *3d. $3
25107  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
25108  autocrop[-4--1] 0 r2dy[-4--1] $3 b[-4--1] $6,0 r[-4--1] 100%,100%,1,4
25109  repeat 4 sh[{-1-$>}] 0,2 fc. $7,$8,$9 rm. done
25110
25111  # Draw stars on selected images.
25112  repeat $!-1 [-4--1] l[$>,-4--1]
25113    N={round(if(${is_percent\ $1},w*h*$1,$1)/4,1,1)}
25114    repeat 4
25115      2,$N rand. -1,1 1,$N rand. 0,1 a[-2,-1] x
25116      i.. ('CImg3d') +.. 0.5 i.. ($N;$N)
25117      (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
25118      rv[-2,-1] sprites3d.. .,1 rm. *3d. {0.75*{0,w}},{0.75*{0,h}},{1000*$2}
25119      j3d[0] .,50%,50%,0,$10,0,0,0 rm.
25120    done
25121  endl done
25122  rm[-4--1]
25123
25124#@cli stencil : _radius[%]>=0,_smoothness>=0,_iterations>=0
25125#@cli : Apply stencil filter on selected images.
25126#@cli : Default values: 'radius=3', 'smoothness=1' and 'iterations=8'.
25127#@cli : $ image.jpg +norm stencil. 2,1,4 +mul rm[0]
25128stencil : check "${1=3}>=0 && ${2=1}>=0 && ${3=8}>=0"
25129  e[^-1] "Apply stencil filter on image$?, with radius $1, smoothness $2 and $3 iterations."
25130  n 0,1 repeat $3 b $1 unsharp {$1+$2},1000 c 0,255 done
25131
25132#@cli stencilbw : _edges>=0,_smoothness>=0
25133#@cli : Apply B&W stencil effect on selected images.
25134#@cli : Default values: 'edges=15' and 'smoothness=10'.
25135#@cli : $ image.jpg +stencilbw 40,4
25136stencilbw : skip ${1=15},${2=10}
25137  e[^-1] "Apply B&W stencil effect on image$?, with edges $1 and smoothness $2."
25138  repeat $! l[$>] split_opacity luminance[0] n[0] 0,255
25139    +edges[0] $1 quantize[0] 3,0,1 b[0] $2
25140    sharpen[0] 1000000 n[0] 0,1 *[0,-1] n[0] 0,255
25141  a c endl done
25142
25143#@cli stylize : [style_image],_fidelity_finest,_fidelity_coarsest,_fidelity_smoothness_finest>=0,\
25144# _fidelity_smoothnes_coarsest>=0,0<=_fidelity_chroma<=1,_init_type,_init_resolution>=0,init_max_gradient>=0,\
25145# _patchsize_analysis>0,_patchsize_synthesis>0,_patchsize_synthesis_final>0,_nb_matches_finest>=0,\
25146# _nb_matches_coarsest>=0,_penalize_repetitions>=0,_matching_precision>=0,_scale_factor>1,_skip_finest_scales>=0,\
25147# _"image_matching_command"
25148#@cli : Transfer colors and textures from specified style image to selected images, using a multi-scale \
25149# patch-mathing algorithm.
25150#@cli : If instant display window[0] is opened, the steps of the image synthesis are displayed on it.
25151#@cli : 'init_type' can be { 0=best-match | 1=identity | 2=randomized }.
25152#@cli : Default values: 'fidelity_finest=0.5', 'fidelity_coarsest=2', 'fidelity_smoothness_finest=3', \
25153# 'fidelity_smoothness_coarsest=0.5', 'fidelity_chroma=0.1', 'init_type=0', 'init_resolution=16', \
25154# 'init_max_gradient=0', 'patchsize_analysis=5', 'patchsize_synthesis=5', 'patchsize_synthesis_final=5', \
25155# 'nb_matches_finest=2', 'nb_matchesc_coarsest=30', 'penalize_repetitions=2', 'matching_precision=2', \
25156# 'scale_factor=1.85', 'skip_finest_scales=0' and \
25157# '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 \
25158# a[0,1] c a[1,2] c"'.
25159stylize :
25160  check ${"is_image_arg $1"}" && isnum(${2=0.5}) && isnum(${3=2}) && ${4=3}>=0 && ${5=0.5}>=0 && ${6=0.1}>=0 && "\
25161        "$6<=1 && isint(${7=0}) && $7>=0 && $7<=3 && isint(${8=16}) && $8>=0 && ${9=0}>=0 && isint(${10=5}) && "\
25162        "$10>0 && isint(${11=5}) && $11>0 && isint(${12=$11}) && $12>0 && isint(${13=2}) && isint(${14=30}) && "\
25163        "${15=2}>=0 && ${16=2}>=0 && ${17=1.85}>1 && isint(${18=0})>=0"
25164  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}"
25165  e[^-1] "Stylize image$? with style image $1."
25166  fidelity_finest,\ # $2
25167  fidelity_coarsest,\ # $3
25168  fidelity_smoothness_finest,\ # $4
25169  fidelity_smoothness_coarsest,\ # $5
25170  fidelity_chroma,\ # $6
25171  init_type,\ # $7
25172  init_resolution,\ # $8
25173  init_max_gradient,\ # $9
25174  patchsize_analysis,\ # $10
25175  patchsize_synthesis,\ # $11
25176  patchsize_synthesis_final,\ # $12
25177  nb_matches_finest,\ # $13
25178  nb_matches_coarsest,\ # $14
25179  penalize_repetitions,\ # $15
25180  matching_precision,\ # $16
25181  scale_factor,\ # $17
25182  skip_finest_scales=${2-18} \ # $18
25183  m "stylize_match : $19"
25184
25185  init_resolution={max(2*$patchsize_analysis,$init_resolution)}
25186  mprec0={round(2+1.5*$matching_precision)}
25187  mprec1={1+round(4*$matching_precision)}
25188  is_window={*}
25189
25190  pass$1 repeat $!-1 l[$>,-1]
25191    to_colormode.. {s}
25192    nb_scales={1+round(log(min(w#0,h#0,w#1,h#1)/$init_resolution)/log($scale_factor),1,-1)}
25193    if {*} wsiz=${"fitscreen "{0,[w,h]}} w[0] $wsiz,0,"[G'MIC Stylize]" fi
25194
25195    repeat $nb_scales,scale
25196      size_factor={100/($scale_factor^$<)}
25197      if !$scale
25198
25199        # Initialization.
25200        +r[0,1] $size_factor%,$size_factor%,100%,100%,2 ws,hs={-2,[w,h]}
25201        +to_color[0,1] channels[-2,-1] 0,2 gradient_norm[-2,-1]
25202        r. [-3],[-3],[-3],1,2 r.. [-4],[-4],[-4],1,2
25203        a[-3,-1] c a[-3,-1] c # Append gradient information as last channel
25204        stylize_match[-2,-1]
25205        if $init_type==0
25206          +matchpatch.. .,3,3,1,{2*$mprec0},{2*$mprec1},$penalize_repetitions # Initial image match with 3x3 patches
25207        else
25208          ..,..,1,2,"round([x,y]*([w#-1,h#-1]-1)/([w,h]-1))" # Identity
25209          if d#-2>1 channels. 0,2 fi
25210          if $init_type==2 eval. ">P = u([w,h]-1); tmp = I(P); I(P) = I; I() = tmp" fi # Randomize
25211        fi
25212        rm[-3,-2]
25213        if $init_max_gradient>0 # Keep only high gradients of target image
25214          +gradient_norm[0] r. ..,..,1,1,2 gt. $init_max_gradient
25215          +.. 1 *[-2,-1] _inpaint_warping2d. --. 1
25216        fi
25217        if $is_window" && "!{*} break fi
25218
25219      else
25220
25221        # Upscale.
25222        factor={1-($scale-1)/max(1,$nb_scales-2)} # Linear scale factor from 1 to 0
25223        +r[0,1] $size_factor%,$size_factor%,100%,100%,2
25224        +to_color[-2,-1] channels[-2,-1] 0,2 gradient_norm[-2,-1]
25225
25226        a[-3,-1] c a[-3,-1] c # Append gradient information as last channel
25227        stylize_match[-2,-1] mv[-2,-1] -3
25228        sh. 0 *. {-3,w/$ws} rm. sh. 1 *. {-3,h/$hs} rm. round.
25229
25230        # Smart upscale of displacement field.
25231        +. 1 r. ...,...,1,100%,4 -. 1
25232        do
25233          f. "begin(const boundary = 1; nx = ny = vectors(); nx[0] = ny[1] = 1);
25234              i>=0?I:(
25235                j(-1)>=0?J(-1) + nx:
25236                j(0,-1)>=0?J(0,-1) + ny:
25237                j(1)>0?J(1) - nx:
25238                j(0,1)>0?J(0,1) - ny:I)"
25239        while im<0
25240        ws,hs={-2,[w,h]}
25241
25242        if $<<$skip_finest_scales rm[-3,-2] continue fi  # Skip finest scales
25243        +warp_patch.. .,$patchsize_synthesis,$patchsize_synthesis,1
25244        if {*} w. fi
25245
25246        # Inject gradients from target (in Lab colorspace).
25247        fidelity={max(0,$fidelity_finest+($fidelity_coarsest-$fidelity_finest)*$factor)}
25248        fidelity_smoothness={$fidelity_smoothness_finest+\
25249                             ($fidelity_smoothness_coarsest-$fidelity_smoothness_finest)*$factor}
25250
25251        if $fidelity>0.1
25252          sh. 0,2 sh[-5] 0,2 +gradient_norm[-2,-1] rm[-4,-3] # Gradient norm on colors only
25253          *. $fidelity argmax[-2,-1] b. xy,$fidelity_smoothness n. 0,{min(1,$fidelity)}
25254          sh[-5,-2] 0,2 srgb2lab[-2,-1] rm[-2,-1]
25255          +*. $fidelity_chroma r. 100%,100%,1,2,1 a[-2,-1] c j.. [-5],0,0,0,0,1,. rm.
25256          sh. 0,2 lab2srgb. rm.
25257        fi
25258        rm[-4]
25259
25260        # Iterate patch-matching steps.
25261        nb_matches={max(0,round($nb_matches_finest+($nb_matches_coarsest-$nb_matches_finest)*$factor^2))}
25262        nb_scales1={$nb_scales-1}
25263        nb_matches1={$nb_matches-1}
25264        if {*} +r. $wsiz,1,100% to. "Scale "$scale/$nb_scales1": 0%",5,2,24 w. -1,-1,0 rm. fi
25265
25266        repeat $nb_matches
25267          matchpatch. ...,$patchsize_analysis,$patchsize_analysis,1,$mprec0,$mprec1,$penalize_repetitions,0,..
25268          -.. . abs.. diff={-2,ia} rm..
25269          +warp_patch.. .,$patchsize_synthesis,$patchsize_synthesis,1
25270          if {*}" && "(!($>%5)" || "$nb_matches<=10)
25271            +r. $wsiz,1,100%
25272            to. "Scale "$scale/$nb_scales1": "{round(100*($>+1)/$nb_matches)}%,5,2,24 w. -1,-1,0 rm.
25273          fi
25274          if $is_window" && "!{*} break fi
25275          if $diff<1 break fi
25276        done
25277        rm[-3,-1]
25278      fi
25279      if $is_window" && "!{*} break fi
25280    done
25281    if $is_window" && "!{*} k[0,1] break fi
25282
25283    # Do final rendering.
25284    +warp_patch[1] .,$patchsize_synthesis_final,$patchsize_synthesis_final,1 c. 0,255
25285    rv[0,-1] rm[-2,-1]
25286  endl done rm.
25287  um stylize_match
25288
25289#@cli tetris : _scale>0
25290#@cli : Apply tetris effect on selected images.
25291#@cli : Default value: 'scale=10'.
25292#@cli : $ image.jpg +tetris 10
25293tetris : skip ${1=10}
25294  e[^-1] "Apply tetris effect on image$?, with scale $1."
25295  repeat $! l[$>]
25296    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
25297  endl done
25298
25299#@cli warhol : _M>0,_N>0,_smoothness>=0,_color>=0
25300#@cli : Create MxN Andy Warhol-like artwork from selected images.
25301#@cli : Default values: 'M=3', 'N=M', 'smoothness=2' and 'color=20'.
25302#@cli : $ image.jpg warhol 3,3,3,40
25303warhol : skip ${1=3},${2=$1},${3=2},${4=20}
25304  e[^-1] "Create $1x$2 Andy Warhol-like artwork from image$?."
25305  r0={100/max($1,$2)}
25306  repeat $! l[$>]
25307    norm b $3 r $r0%,$r0%,1,100%,2 quantize 6 n 0,5 round 1
25308    repeat $1 repeat $2
25309      (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..
25310    done done append_tiles[^0] $1,$2 nm[1] {0,n} rm[0]
25311  endl done
25312
25313#@cli weave : _density>=0,0<=_thickness<=100,0<=_shadow<=100,_shading>=0,_fibers_amplitude>=0,_fibers_smoothness>=0,\
25314# _angle,-1<=_x_curvature<=1,-1<=_y_curvature<=1
25315#@cli : Apply weave effect to the selected images.
25316#@cli : 'angle' can be { 0=0 deg. | 1=22.5 deg. | 2=45 deg. | 3=67.5 deg. }.
25317#@cli : Default values: 'density=6', 'thickness=65', 'shadow=40', 'shading=0.5', 'fibers_amplitude=0', _\
25318# 'fibers_smoothness=0', 'angle=0' and 'curvature_x=curvature_y=0'
25319#@cli : $ image.jpg weave ,
25320weave : check "${1=6}>=0 && ${2=65}>=0 && $2<=100 && ${3=40}>=0 && $3<=100 && ${4=0.5}>=0"
25321        check "${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 && $7<=3 && ${8=0}>=-1 && $8<=1 && ${9=0}>=-1 && $9<=1"
25322  e[^-1] "Apply weave effect to image$?, with $1 strips, thickness $2, shadow $3, shading $4, "\
25323          "fibers amplitude $5 and fibers smoothness $6, angle "{$7*22.5}" deg. and curvatures ($8,$9)."
25324  repeat $! l[$>] split_opacity l[0]
25325    w={round(max(w,h)/$1,1,1)} h=$w s={(100-$3)*255%} p={max(0.01,$4)}
25326
25327    # Create patterns.
25328    1,$h =. 1,0,50% distance. 1 ^. $p c. 50%,100% r. {max(1,round($2*$w%))},100%
25329    $w,1 =. 1,50% distance. 1 ^. $p c. 50%,100% *. -1 r. 100%,{max(1,round($2*$h%))}
25330    +*. -1 +*... -1 n[-4,-2] 0,$s n[-3,-1] $s,255
25331    {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]
25332    +f... 255 a[-4,-1] c +f. 255 a[-2,-1] c
25333
25334    amp_x={$8*($w-w)/2} amp_y={$9*($w-w)/2}
25335    r[-4--1] $w,$h,1,100%,0,0,0.5,0.5
25336    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)'
25337    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)'
25338    blend[-4,-3] alpha blend[-2,-1] alpha c[-2,-1] 0,255
25339
25340    # Render full pattern and merge.
25341    /[-2,-1] 255 . ... a[-4,-2] x a[-2,-1] x a[-2,-1] y rotate_tileable. {$7*22.5}
25342    r. ..,..,1,1,0,2 *[-2,-1]
25343  endl a c endl done
25344
25345#@cli whirls : _texture>=0,_smoothness>=0,_darkness>=0,_lightness>=0
25346#@cli : Add random whirl texture to selected images.
25347#@cli : Default values: 'texture=3', 'smoothness=6', 'darkness=0.5' and 'lightness=1.8'.
25348#@cli : $ image.jpg whirls ,
25349whirls : skip ${1=3},${2=6},${3=0.5},${4=1.8}
25350  e[^-1] "Add random whirl texture to image$?, with texture $1, smoothness $2, darkness $3 and lightness $4."
25351  repeat $! l[$>]
25352    100%,100% noise. 0.3,2 ==. 1 repeat $1 b. $2 +. 0.1 gradient_norm. ^. 0.2 done
25353    n. $3,$4 ri. .. * c 0,255
25354  endl done
25355
25356#------------------------------------
25357#
25358#@cli :: Warpings
25359#
25360#------------------------------------
25361
25362#@cli deform : _amplitude>=0,_interpolation
25363#@cli : Apply random smooth deformation on selected images.
25364#@cli : 'interpolation' can be { 0=none | 1=linear | 2=bicubic }.
25365#@cli : Default value: 'amplitude=10'.
25366#@cli : $ image.jpg +deform[0] 10 +deform[0] 20
25367deform : skip ${1=10},${2=1}
25368  e[^-1] "Apply random smooth deformation on image$?, with amplitude $1."
25369  repeat $! l[$>]
25370    2%,2%,1,2 noise. $1 r. ..,..,1,2,5 warp.. .,1,$2,1 rm.
25371  endl done
25372
25373#@cli euclidean2polar : _center_x[%],_center_y[%],_stretch_factor>0,\
25374# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25375#@cli : Apply euclidean to polar transform on selected images.
25376#@cli : Default values: 'center_x=center_y=50%', 'stretch_factor=1' and 'boundary_conditions=1'.
25377#@cli : $ image.jpg +euclidean2polar ,
25378euclidean2polar : skip ${1=50%},${2=50%} check "${3=1}>0 && isint(${4=1}) && $4>=0 && $4<=3"
25379  e[^-1] "Apply euclidean to polar transform on image$?, with center point ($1,$2), stretch factor $3 and "\
25380         ${"arg 1+$4,dirichlet,neumann,periodic,mirror"}" boundary conditions."
25381  repeat $! l[$>]
25382    cx={if(${is_percent\ $1},$1*(w-1),$1)}
25383    cy={if(${is_percent\ $2},$2*(h-1),$2)}
25384    R={sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2))}
25385    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)'
25386  endl done
25387
25388#@cli equirectangular2nadirzenith
25389#@cli : Transform selected equirectangular images to nadir/zenith rectilinear projections.
25390equirectangular2nadirzenith :
25391  e[^-1] "Transform equirectangular image$? to nadir/zenith rectilinear projections."
25392  repeat $! l[$>]
25393    100%,100%,1,2
25394    sh. 100%
25395    f. "
25396      X = 2*x/(w-1) - 1;
25397      Y = y/(h-1) - 0.5;
25398      if (X<0,
25399        sinphi1 = 1; X+=0.5,
25400        sinphi1 = -1; X-=0.5
25401      );
25402      rr = sqrt(X*X + Y*Y);
25403      cc = atan(2*rr);
25404      phi = rr==0?0:asin(cos(cc)*sinphi1);
25405      X = atan2(X,-Y*sinphi1)/pi;
25406      Y = phi/pi;
25407      (++X)*=0.5*w;
25408      (Y+=0.5)*=h;
25409      i(#-2) = X; Y;"
25410    warp[0] [1],0,0,1 k...
25411  endl done
25412
25413#@cli fisheye : _center_x,_center_y,0<=_radius<=100,_amplitude>=0
25414#@cli : Apply fish-eye deformation on selected images.
25415#@cli : Default values: 'x=y=50', 'radius=50' and 'amplitude=1.2'.
25416#@cli : $ image.jpg +fisheye ,
25417fisheye : skip ${1=50},${2=50},${3=50},${4=1.2}
25418  e[^-1] "Apply Fish-eye effect on image$?, centered at ($1%,$2%) with radius $3% and amplitude $4."
25419  if $4==0 return fi
25420  repeat $! l[$>]
25421    100%,100%,1,1 =. 1,$1%,$2% distance. 1 c. 0,$3% *. -1 n. 0,1 ^. {1/$4}
25422    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
25423    n. 0,{max(w,h)} *[-2,-1]
25424    warp.. .,1,1,1 rm.
25425  endl done
25426
25427#@cli flower : _amplitude,_frequency,_offset_r[%],_angle,_center_x[%],_center_y[%],\
25428# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror}
25429#@cli : Apply flower deformation on selected images.
25430#@cli : Default values: 'amplitude=30', 'frequency=6', 'offset_r=0', 'angle=0', 'center_x=center_y=50%' \
25431# and 'boundary_conditions=3'.
25432#@cli : $ image.jpg +flower ,
25433flower : skip ${1=30},${2=6},${3=0},${4=0},${5=50%},${6=50%},${7=3}
25434  e[^-1] "Apply flower deformation on image$?, with amplitude $1, frequency $2, offset $3, angle $4 deg. and
25435          center point ($1,$2)."
25436  if ${"is_percent $3"}
25437    transform_polar "r + (R*$3) + R*$1/100*cos(a*$2+$4*pi/180)","a",$5,$6,$7
25438  else
25439    transform_polar "r + $3 + R*$1/100*cos(a*$2+$4*pi/180)","a",$5,$6,$7
25440  fi
25441
25442#@cli kaleidoscope : _center_x[%],_center_y[%],_radius,_angle,\
25443# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25444#@cli : Create kaleidoscope effect from selected images.
25445#@cli : Default values: 'center_x=center_y=50%', 'radius=100', 'angle=30' and 'boundary_conditions=3'.
25446#@cli : $ image.jpg kaleidoscope ,
25447kaleidoscope : skip ${1=50%},${2=50%},${3=100},${4=30},${5=3}
25448  e[^-1] "Create kaleidoscope effect from image$?, with center point ($1,$2), radius $3, angle $4 deg."
25449  euclidean2polar $1,$2,1,$5 repeat $! l[$>]
25450    +columns 0,$3% rows. 0,$4% ri. ..,0,2 nm[1] {0,n} rm[0]
25451  endl done polar2euclidean $1,$2,1,$5
25452
25453#@cli map_sphere : _width>0,_height>0,_radius,_dilation>0,_fading>=0,_fading_power>=0
25454#@cli : Map selected images on a sphere.
25455#@cli : Default values: 'width=height=512', 'radius=100', 'dilation=0.5', 'fading=0' and 'fading_power=0.5'.
25456#@cli : $ image.jpg map_sphere ,
25457map_sphere : check "${1=512}>0 && ${2=512}>0 && ${5=0}>=0 && ${6=0.5}>=0" skip ${3=100},${4=0.5}
25458   e[^-1] "Map image$? on spheres in $1x$2 images, with radius $3, dilation $4 and fading $5."
25459   r2={($3*min($1,$2)/200)^2} # Compute squared radius.
25460   repeat $! l[$>]
25461     i.. 100%,1,1,100%,0 nm[0] {1,n} a y # Add one border line to have a sphere exterior.
25462     ({-$1/2},{$1/2}) ({-$2/2};{$2/2}) r[-2,-1] $1,$2,1,1,3 atan2. .. rm.. # Compute theta angle.
25463     $1,$2 =. 1,50%,50% distance. 1,3 /. $r2 sqrt. c. 0,1
25464     asin. # Compute phi angle.
25465     +.. {pi} *.. {({-3,w}-1)/(2*pi)} # Normalize theta to X-coordinates
25466     *. {2/pi} ^. $4 *. {{-3,h}-1} *. -1 +. {{-3,h}-1} # Normalize phi to Y-coordinates
25467     if $5 +>=. 1 distance. 1 c. 0,$5% n. 0,1 ^. $6 c.. 1,100% -[-2,-1] fi
25468     r[-1,-2] 100%,100%,{-3,d}
25469     +f. z a[-3--1] c
25470     warp.. .,0,1,1 rm. # Apply image warping
25471   endl done
25472
25473#@cli nadirzenith2equirectangular
25474#@cli : Transform selected nadir/zenith rectilinear projections to equirectangular images.
25475nadirzenith2equirectangular :
25476  e[^-1] "Transform nadir/zenith rectilinear projection$? to equirectangular images."
25477  repeat $! l[$>]
25478    100%,100%,1,2
25479    sh. 100%
25480    f. "
25481      X = 2*x/(w-1) - 1;
25482      Y = y/(h-1) - 0.5;
25483      output = 1;
25484      if (Y>0.125,
25485        sinphi1 = 1; xc = -0.5,
25486      if (Y<-0.125,
25487        sinphi1 = -1; xc = 0.5,
25488        output = 0
25489      ));
25490      cosc = sinphi1*sin(Y*pi);
25491      xx = cos(Y*pi)*sin(X*pi)/cosc;
25492      yy = -sinphi1*cos(Y*pi)*cos(X*pi)/cosc;
25493      if (abs(xx)>1, output=0);
25494      (xx*=0.5)+=xc;
25495      yy*=0.5;
25496      if (!output, xx = yy = -1);
25497      (++xx)*=0.5*w;
25498      (yy+=0.5)*=h;
25499      i(#-2) = xx; yy;"
25500    to_a[0] warp[0] [1],0,0,0 k...
25501  endl done
25502
25503#@cli polar2euclidean : _center_x[%],_center_y[%],_stretch_factor>0,\
25504# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25505#@cli : Apply euclidean to polar transform on selected images.
25506#@cli : Default values: 'center_x=center_y=50%', 'stretch_factor=1' and 'boundary_conditions=1'.
25507#@cli : $ image.jpg +euclidean2polar ,
25508polar2euclidean : skip ${1=50%},${2=50%} check "${3=1}>0 && isint(${4=1}) && $4>=0 && $4<=3"
25509  e[^-1] "Apply polar to euclidean transform on image$?, with center point ($1,$2), stretch factor $3 and "\
25510         ${"arg 1+$4,dirichlet,neumann,periodic,mirror"}" boundary conditions."
25511  repeat $! l[$>]
25512    cx={if(${is_percent\ $1},$1*(w-1),$1)}
25513    cy={if(${is_percent\ $2},$2*(h-1),$2)}
25514    R={sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2))}
25515    f "X = sqrt((x-"$cx")^2+(y-"$cy")^2);
25516       tmp = atan2((y-"$cy"),(x-"$cx"));
25517       Y = if(tmp<0,tmp+2*pi,tmp);
25518       i((X/"$R")^(1/$3)*(w-1),Y*(h-1)/(2*pi),z,c,1,$4)"
25519  endl done
25520
25521#@cli raindrops : _amplitude,_density>=0,_wavelength>=0,_merging_steps>=0
25522#@cli : Apply raindrops deformation on selected images.
25523#@cli : Default values: 'amplitude=80','density=0.1', 'wavelength=1' and 'merging_steps=0'.
25524#@cli : $ image.jpg +raindrops ,
25525raindrops : check "${2=0.1}>=0 && ${3=1}>=0 && isint(${4=0}) && $4>=0" skip ${1=80}
25526  e[^-1] "Apply raindrops deformation on image$?, with amplitude $1, density $2, wavelength $3 and $4 merging steps."
25527  repeat $! l[$>]
25528    100%,100% noise. $2,2 ==. 1 distance. 1 f. 'cos(i)/(1+i/(1e-8+$3))'
25529    if $4
25530      i.. (0,1,0;1,0,1;0,1,0) /.. 2 .
25531      repeat $4 +convolve. ...,1 -. ... rm... done rm[-3,-2]
25532    fi
25533    g. a[-2,-1] c *. {$1/(1e-5+max(abs(im),abs(iM)))}
25534    warp.. .,1 rm.
25535  endl done
25536
25537#@cli ripple : _amplitude,_bandwidth,_shape={ 0=bloc | 1=triangle | 2=sine | 3=sine+ | 4=random },_angle,_offset
25538#@cli : Apply ripple deformation on selected images.
25539#@cli : Default values: 'amplitude=10', 'bandwidth=10', 'shape=2', 'angle=0' and 'offset=0'.
25540#@cli : $ image.jpg +ripple ,
25541ripple : skip ${1=10},${2=20},${3=2},${4=0},${5=0}
25542  e[^-1] "Apply ripple deformation on image$?, with amplitude $1, bandwidth $2, shape $3, angle $4 deg. and offset $5."
25543  theta={$4*pi/180} C={cos($theta)} S={-sin($theta)}
25544  repeat $! l[$>]
25545    100%,100%,1,1,"x" -. {w/2} 100%,100%,1,1,'y'
25546    -. {h/2-$5} *.. $S *. $C +[-2,-1]      # Generate rotated Y.
25547    _ripple$3. $1,$2                       # Generate warp field.
25548    +*. {-$S} *.. $C a[-2,-1] c            # Rotate warp field.
25549    warp.. .,1 rm.
25550  endl done
25551
25552_ripple0 : f {$1/2}*"(1-2*(i%"{2*$2}"<$2))"
25553_ripple1 : f "I=(i%$2)/$2;$1*(2*if(I<0.5,I,1-I)-0.5)"
25554_ripple2 : f {-$1/2}*"cos(i*"{2*pi/$2}")"
25555_ripple3 : f {-$1/2}*"abs(cos(i*"{2*pi/$2}"))"
25556_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.
25557
25558#@cli rotoidoscope : _center_x[%],_center_y[%],_tiles>0,_smoothness[%]>=0,\
25559# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25560#@cli : Create rotational kaleidoscope effect from selected images.
25561#@cli : Default values: 'center_x=center_y=50%', 'tiles=10', 'smoothness=1' and 'boundary_conditions=3'.
25562#@cli : $ image.jpg +rotoidoscope ,
25563rotoidoscope : skip ${1=50%},${2=50%},${5=1} check "${3=10}>0 && ${4=3}>=0"
25564  e[^-1] "Create rotational kaleidoscope effect from image$?, with center point ($1,$2), $3 tiles and smoothness $4."
25565  repeat $! l[$>]
25566    repeat $3 +rotate[0] {360/$3},1,$5,$1,$2 blend_edges $4 done
25567  endl done
25568
25569#@cli spherize : _radius[%]>=0,_strength,_smoothness[%]>=0,_center_x[%],_center_y[%],_ratio_x/y>0,_angle,\
25570# _interpolation
25571#@cli : Apply spherize effect on selected images.
25572#@cli : Default values: 'radius=50%', 'strength=1', 'smoothness=0', 'center_x=center_y=50%', 'ratio_x/y=1', \
25573# 'angle=0' and 'interpolation=1'.
25574#@cli : $ image.jpg grid 5%,5%,0,0,0.6,255 spherize ,
25575spherize : check "${1=50%}>=0 && ${3=0}>=0 && ${6=1}>0 && isint(${8=1}) && $8>=0 && $8<=2"
25576           skip "${2=1},${4=50%},${5=50%},${7=0}"
25577  e[^-1] "Apply spherize effect on image$?, with radius $1, strength $2, smoothness $3, center ($4,$5),
25578          x/y-ratio $6, angle $7 and "${"arg 1+$8,nearest-neighbor,linear,cubic"}" interpolation."
25579  if !$1||!$2 return fi
25580  repeat $! l[$>]
25581    rmax={${"is_percent $1"}?0.5*sqrt((w-1)^2+(h-1)^2)*$1:$1}
25582    centerx={${"is_percent $4"}?(w-1)*$4:$4}
25583    centery={${"is_percent $5"}?(h-1)*$5:$5}
25584    strength={$2>0?$2:1-exp($2/5)}
25585    100%,100%,1,2,"
25586      begin(
25587        center = [ "$centerx","$centery" ];
25588        wh1 = [ w,h ] - 1;
25589        m2wh1 = 0.5*max(wh1);
25590        rmax = "$rmax"/m2wh1;
25591        const f = 1/"$strength";
25592        const ratio = $6;
25593        rotf = rot($7°);
25594        rotb = rot(-$7°);
25595      );
25596      xy = ([x,y] - center)/m2wh1;
25597      xy = rotf*xy;
25598      ratio>=1?(xy[1]*=ratio):(xy[0]/=ratio);
25599      r = norm2(xy);
25600      z = r<rmax?sign($2)*sqrt(rmax^2 - r^2):0;
25601      ratio>=1?(xy[1]/=ratio):(xy[0]*=ratio);
25602      xy = rotb*xy;
25603      xy = center + f*xy/(f + z)*m2wh1"
25604    b. $3
25605    warp.. .,0,$8,1 rm.
25606  endl done
25607
25608#@cli symmetrize : _x[%],_y[%],_angle,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror },\
25609# _is_antisymmetry={ 0 | 1 },_swap_sides={ 0 | 1 }
25610#@cli : Symmetrize selected images regarding specified axis.
25611#@cli : Default values: 'x=y=50%', 'angle=90', 'boundary_conditions=3', 'is_antisymmetry=0' and 'swap_sides=0'.
25612#@cli : $ image.jpg +symmetrize 50%,50%,45 +symmetrize[-1] 50%,50%,-45
25613symmetrize : skip ${1=50%},${2=50%},${3=90},${4=3},${5=0},${6=0}
25614  e[^-1] "Symmetrize image$?, regarding axis ($1,$2,$3 deg.)."
25615  theta={$3*pi/180} u={cos($theta)} v={sin($theta)}
25616  if $6 symmetry_cond=A<0 else symmetry_cond=A>0 fi
25617  repeat $! l[$>]
25618    x0={if(${is_percent\ $1},w*$1,$1)}
25619    y0={if(${is_percent\ $2},h*$2,$2)}
25620    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)'
25621    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)'
25622    fi
25623  endl done
25624
25625#@cli transform_polar : "expr_radius",_"expr_angle",_center_x[%],_center_y[%],\
25626# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25627#@cli : Apply user-defined transform on polar representation of selected images.
25628#@cli : Default values: 'expr_radius=R-r', 'expr_rangle=a', 'center_x=center_y=50%' and 'boundary_conditions=1'.
25629#@cli : $ image.jpg +transform_polar[0] R*(r/R)^2,a +transform_polar[0] r,2*a
25630transform_polar : skip "${1=R-r}","${2=a}",${3=50%},${4=50%},${5=1}
25631  e[^-1] "Apply custom polar transform with 'new_r = $1', 'new_a = $2', center point ($3%,$4%)."
25632  repeat $! l[$>]
25633    cx={if(${is_percent\ $3},$3*(w-1),$3)}
25634    cy={if(${is_percent\ $4},$4*(h-1),$4)}
25635    R={sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2))}
25636    f "R ="$R";
25637       r = sqrt((x-"$cx")^2 + (y-"$cy")^2);
25638       a = atan2(y-"$cy",x-"$cx");
25639       nr = ($1);
25640       na = ($2);
25641       i("$cx" + nr*cos(na), "$cy" + nr*sin(na), z, c,1,$5)"
25642  endl done
25643
25644#@cli twirl : _amplitude,_center_x[%],_center_y[%],\
25645# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25646#@cli : Apply twirl deformation on selected images.
25647#@cli : Default values: 'amplitude=1', 'center_x=center_y=50%' and 'boundary_conditions=3'.
25648#@cli : $ image.jpg twirl 0.6
25649twirl : skip ${1=1},${2=50%},${3=50%},${4=3}
25650  e[^-1] "Apply twirl deformation on image$?, with amplitude $1 and center point at ($2%,$3%)."
25651  euclidean2polar $2,$3,1,$4 repeat $!
25652    [$>],[$>],1,1,$1*x channels. -1,0 warp[$>] .,1,1,2 rm.
25653  done polar2euclidean $2,$3,1,1
25654
25655#@cli warp_perspective : _x-angle,_y-angle,_zoom>0,_x-center,_y-center,\
25656# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25657#@cli : Warp selected images with perspective deformation.
25658#@cli : Default values: 'x-angle=1.5', 'y-angle=0', 'zoom=1', 'x-center=y-center=50' and 'boundary_conditions=2'.
25659#@cli : $ image.jpg warp_perspective ,
25660warp_perspective : skip ${1=1.5},${2=0},${3=1},${4=50},${5=50},${6=2}
25661  e[^-1] "Apply perspective warp on image$?, with angles ($1 deg.,$2 deg.), zoom $3 and offsets ($4,$5)."
25662  repeat $! l[$>]
25663    (0,100) -. $4 /. 100 (0;100) -. $5 /. 100 r[-2,-1] ...,...,...,1,3
25664    +*.. $2 +*.. $1 +[-2,-1] +. $3 /... . /[-2,-1]
25665    *.. 100 +.. $4 /.. 100 *.. {-3,w}
25666    *. 100 +. $5 /. 100 *. {-3,h}
25667    a[-2,-1] c warp.. .,0,1,$6 rm.
25668  endl done
25669
25670#@cli water : _amplitude,_smoothness>=0,_angle
25671#@cli : Apply water deformation on selected images.
25672#@cli : Default values: 'amplitude=30', 'smoothness=1.5' and 'angle=45'.
25673#@cli : $ image.jpg water ,
25674water : check ${2=1.5}>=0 skip ${1=30},${3=1},${4=45}
25675  e[^-1] "Apply water deformation on image$?, with amplitude $1, smoothness $2 and angle $3."
25676  repeat $! l[$>]
25677    25%,25%,25%,1 noise. $1 g. xy *.. {-sin($3*pi/180)} *. {cos($3*pi/180)} +[-2,-1] b. $2 *. 2
25678    r. ..,..,1,2,3 warp.. .,1 rm.
25679  endl done
25680
25681#@cli wave : _amplitude>=0,_frequency>=0,_center_x,_center_y
25682#@cli : Apply wave deformation on selected images.
25683#@cli : Default values: 'amplitude=4', 'frequency=0.4' and 'center_x=center_y=50'.
25684#@cli : $ image.jpg wave ,
25685wave : skip ${1=4},${2=0.4},${3=50},${4=50}
25686  e[^-1] "Apply wave deformation on image$?, with amplitude $1, frequency $2 and center point at ($3%,$4%)."
25687  repeat $! l[$>]
25688    100%,100% =. 1,$3%,$4% distance. 1
25689    *. $2 +sin. cos.. a[-2,-1] c *. $1
25690    warp.. .,1 rm.
25691  endl done
25692
25693#@cli wind : _amplitude>=0,_angle,0<=_attenuation<=1,_threshold
25694#@cli : Apply wind effect on selected images.
25695#@cli : Default values: 'amplitude=20', 'angle=0', 'attenuation=0.7' and 'threshold=20'.
25696#@cli : $ image.jpg +wind ,
25697wind : check "isint(${1=20}) && $1>=0 && ${3=0.7}>=0 && $3<=1" skip "${2=0},${4=20}"
25698  e[^-1] "Apply wind effect on image$?, with amplitude $1, angle "{round($2/45)*45}" deg., attenuation $3
25699          and threshold $4."
25700  if !$1 return fi
25701  dxdy=${-_wind{round($2/45)%8}}
25702  fact={(1-$3)^(1/$1)}
25703  repeat $! l[$>]
25704    +gradient_norm >=. $4%
25705    r. 100%,100%,1,.. *. ..
25706    repeat $1
25707      shift. $dxdy,0,0,0 max.. . *. $fact
25708      remove_pixels. {100/$1}%
25709    done rm.
25710  endl done
25711
25712_wind0 : u 1,0
25713_wind1 : u 1,1
25714_wind2 : u 0,1
25715_wind3 : u -1,1
25716_wind4 : u -1,0
25717_wind5 : u -1,-1
25718_wind6 : u 0,-1
25719_wind7 : u 1,-1
25720
25721#@cli zoom : _factor,_cx,_cy,_cz,_boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
25722#@cli : Apply zoom factor to selected images.
25723#@cli : Default values: 'factor=1', 'cx=cy=cz=0.5' and 'boundary_conditions=0'.
25724#@cli : $ image.jpg +zoom[0] 0.6 +zoom[0] 1.5
25725zoom : skip ${1=2},${2=0.5},${3=0.5},${4=0.5},${5=0}
25726  e[^-1] "Apply zoom effect on image$?, with factor $1 and center ($2,$3)."
25727  repeat $! l[$>]
25728    if d==1 # 2D image.
25729       ({(w-1)*$2*(1-1/$1)},{(w-1)*($2+(1-$2)/$1)})
25730       ({({-2,h}-1)*$3*(1-1/$1)};{({-2,h}-1)*($3+(1-$3)/$1)})
25731       r[-2--1] ...,...,1,1,3 a[-2--1] c warp.. .,0,1,$5
25732    else # 3D image.
25733       ({(w-1)*$2*(1-1/$1)},{(w-1)*($2+(1-$2)/$1)})
25734       ({({-2,h}-1)*$3*(1-1/$1)};{({-2,h}-1)*($3+(1-$3)/$1)})
25735       ({({-3,d}-1)*$4*(1-1/$1)}/{({-3,d}-1)*($4+(1-$4)/$1)})
25736       r[-3--1] [-4],[-4],[-4],1,3 a[-3--1] c warp.. .,0,1,$5
25737    fi
25738    rm.
25739  endl done
25740
25741#-----------------------------
25742#
25743#@cli :: Degradations
25744#
25745#-----------------------------
25746
25747#@cli cracks : 0<=_density<=100,_is_relief={ 0 | 1 },_opacity,_color1,...
25748#@cli : Draw random cracks on selected images with specified color.
25749#@cli : Default values: 'density=25', 'is_relief=0', 'opacity=1' and 'color1=0'.
25750#@cli : $ image.jpg +cracks ,
25751cracks : check "${1=25}>=0" skip ${2=0},${3=1},${4=0}
25752  e[^-1] "Add random cracks to image$?, with density $1, opacity $3 and color (${4--1})."
25753  repeat $! l[$>] cut={[im,iM]}
25754    100%,100%,1,2,'u<0.25*($1%)^4?[u,1]:[0,0]'
25755    s. c distance. 1 *. -1 watershed.. . rm.
25756    +dilate. 3 -[-2,-1] !=. 0 # thinning. 1
25757    if $2
25758      f. "i?i:j(1)?2:j(-1)?0.5:i" n. 0,1
25759      +fc.. ${4--1} *. .. !=.. 0 j... .,0,0,0,0,$3,..
25760    else
25761      +fc.. ${4--1} j... .,0,0,0,0,$3,..
25762    fi
25763    k[0]
25764  endl done
25765
25766#@cli light_patch : _density>0,_darkness>=0,_lightness>=0
25767#@cli : Add light patches to selected images.
25768#@cli : Default values: 'density=10', 'darkness=0.9' and 'lightness=1.7'.
25769#@cli : $ image.jpg +light_patch 20,0.9,4
25770light_patch : skip ${1=10},${2=0.9},${3=1.7}
25771  e[^-1] "Apply light patches to image$?, with density $1, darkness $2 and lightness $3."
25772  repeat $! l[$>]
25773    n 0,255 $1,$1 noise. 40 ri. ..,5 c. 0,255
25774    n. $2,$3 * c 0,255
25775  endl done
25776
25777#@cli noise_hurl : _amplitude>=0
25778#@cli : Add hurl noise to selected images.
25779#@cli : Default value: 'amplitude=10'.
25780#@cli : $ image.jpg +noise_hurl ,
25781noise_hurl : skip ${1=10}
25782  e[^-1] "Add hurl noise to image$?, with amplitude $1%."
25783  repeat $! l[$>]
25784    +f 0 noise. 10 n. {-2,[im,iM]} 100%,100%
25785    noise. $1,2 >. 0 ri. ..
25786    *.. . *. -1 +. 1 *[-3,-1] +
25787  endl done
25788
25789#@cli pixelize : _scale_x>0,_scale_y>0,_scale_z>0
25790#@cli : Pixelize selected images with specified scales.
25791#@cli : Default values: 'scale_x=20' and 'scale_y=scale_z=scale_x'.
25792#@cli : $ image.jpg +pixelize ,
25793pixelize : skip ${1=20},${2=$1},${3=$1}
25794  e[^-1] "Pixelize image$? with scales ($1%,$2%,$3%)."
25795  repeat $! l[$>] whd={w},{h},{d} r $1%,$2%,$3%,100%,2 r $whd endl done
25796
25797#@cli scanlines : _amplitude,_bandwidth,_shape={ 0=bloc | 1=triangle | 2=sine | 3=sine+ | 4=random },_angle,_offset
25798#@cli : Apply ripple deformation on selected images.
25799#@cli : Default values: 'amplitude=60', 'bandwidth=2', 'shape=0', 'angle=0' and 'offset=0'.
25800#@cli : $ image.jpg +scanlines ,
25801scanlines : skip ${1=60},${2=2},${3=0},${4=0},${5=0}
25802  e[^-1] "Apply scanlines effect on image$?, with amplitude $1, bandwidth $2, shape $3, angle $4 deg. and offset $5."
25803  theta={$4*pi/180} C={cos($theta)} S={-sin($theta)}
25804  repeat $! l[$>]
25805    100%,100%,1,1,"x" -. {w/2} 100%,100%,1,1,'y'
25806    -. {h/2-$5} *.. $S *. $C +[-2,-1]      # Generate rotated Y.
25807    _ripple$3. $1,$2                            # Generate warp field.
25808    n. {-$1},$1
25809    + cut 0,255
25810  endl done
25811
25812#@cli shade_stripes : _frequency>=0,_direction={ 0=horizontal | 1=vertical },_darkness>=0,_lightness>=0
25813#@cli : Add shade stripes to selected images.
25814#@cli : Default values: 'frequency=5', 'direction=1', 'darkness=0.8' and 'lightness=2'.
25815#@cli : $ image.jpg +shade_stripes 30
25816shade_stripes : skip ${1=5},${2=1},${3=0.8},${4=2}
25817  e[^-1] "Add "${arg\ 1+!$2,vertical,horizontal}" shaded stripes to image$?, with frequency $1, darkness $3 and
25818          lightness $4."
25819  n 0,255 repeat $! l[$>]
25820    {max(1,w*($2!=0))},{max(1,h*($2==0))} noise. $1,2 ==. 1 distance. 1 ri. .. n. $3,$4 * c 0,255
25821  endl done
25822
25823#@cli shadow_patch : _opacity>=0
25824#@cli : Add shadow patches to selected images.
25825#@cli : Default value: 'opacity=0.7'.
25826#@cli : $ image.jpg +shadow_patch 0.4
25827shadow_patch : skip ${1=0.7}
25828  e[^-1] "Apply shadow patches to image$?, with opacity $1."
25829  repeat $! l[$>]
25830    100%,100%,1,1 shift. -2,-2 shift. 1,1
25831    plasma. 3,0.3,8 abs. b. 1 c. 3%,15% ri. ..
25832    n. $1,1 *
25833  endl done
25834
25835#@cli spread : _dx>=0,_dy>=0,_dz>=0
25836#@cli : Spread pixel values of selected images randomly along x,y and z.
25837#@cli : Default values: 'dx=3', 'dy=dx' and 'dz=0'.
25838#@cli : $ image.jpg +spread 3
25839spread : skip ${1=3},${2=$1},${3=0}
25840  e[^-1] "Spread pixel of image$? randomly, with amplitudes ($1,$2,$3)."
25841  repeat $! l[$>]
25842    100%,100%,100%,3
25843    sh. 0 rand. {-$1},$1 rm.
25844    sh. 1 rand. {-$2},$2 rm.
25845    sh. 2 rand. {-$3},$3 rm.
25846    warp.. .,1 rm.
25847  endl done
25848
25849#@cli stripes_y : _frequency>=0
25850#@cli : Add vertical stripes to selected images.
25851#@cli : Default value: 'frequency=10'.
25852#@cli : $ image.jpg +stripes_y ,
25853stripes_y : skip ${1=10}
25854  e[^-1] "Add vertical stripes to image$?, with frequency $1."
25855  repeat $! l[$>]
25856    100% noise. $1,2 ==. 1 *. 255 ri. ..
25857    *. 0.15 + c 0,255
25858  endl done
25859
25860#@cli texturize_canvas : _amplitude>=0,_fibrousness>=0,_emboss_level>=0
25861#@cli : Add paint canvas texture to selected images.
25862#@cli : Default values: 'amplitude=20', 'fibrousness=3' and 'emboss_level=0.6'.
25863#@cli : $ image.jpg +texturize_canvas ,
25864texturize_canvas : check "${1=20}>=0 && ${2=3}>=0 && ${3=0.6}>=0 && ${4=80}"
25865  e[^-1] "Add canvas texture to image$?, with amplitude $1, fibrousness $2 and emboss level $3."
25866  repeat $! l[$>]
25867    {w},{h} rand. 0,255 +blur_x. $2 blur_y.. $2 +[-2,-1] g. a[-2,-1] c
25868    +compose_channels. + orientation.. compose_channels.. + n.. $3,1 n. 0,255
25869    sharpen. 80 *[-2,-1] n. -$1,$1 + c 0,255
25870  endl done
25871
25872#@cli texturize_paper
25873#@cli : Add paper texture to selected images.
25874#@cli : $ image.jpg +texturize_paper
25875texturize_paper :
25876  e[^-1] "Add paper texture to image$?."
25877  repeat $! l[$>]
25878    . 30%,30% noise. 1,2 ==. 1 r. ..,..,..,1,0 ifft.
25879    rm. shift. {round(w/2)},{round(h/2)},{round(d/2)},0,2 sharpen. 1 n. 1,1.2 ri. ..
25880    *[-2,-1] c. ..,.. rm..
25881  endl done
25882
25883#@cli vignette : _strength>=0,0<=_radius_min<=100,0<=_radius_max<=100
25884#@cli : Add vignette effect to selected images.
25885#@cli : Default values: 'strength=100', 'radius_min=70' and 'radius_max=90'.
25886#@cli : $ image.jpg vignette ,
25887vignette : check "${1=100}>=0 && ${2=70}>=0 && $2<=100 && ${3=90}>=0 && $3<=100"
25888  e[^-1] "Add vignette effect to image$?, with strength $1 and size $2."
25889  repeat $! l[$>]
25890    mM={[im,iM]} d={max(w,h)}
25891    $d,$d =. 1,50%,50% distance. 1 ri. ..,2
25892    c. $2%,$3% n. 0,$1 - c $mM
25893  endl done
25894
25895#@cli watermark_visible : _text,0<_opacity<1,_size>0,_angle,_mode={ 0=remove | 1=add },_smoothness>=0
25896#@cli : Add or remove a visible watermark on selected images (value range must be [0,255]).
25897#@cli : Default values: 'text=(c) G'MIC', 'opacity=0.3', 'size=53', 'angle=25', 'mode=1' and 'smoothness=0'.
25898#@cli : $ image.jpg watermark_visible ,0.7
25899watermark_visible : check "${2=0.3}>0 && $2<1 && ${3=53}>0 && ${6=0.5}>=0"
25900                    skip "${1=\251\ G\47MIC}",${4=25},${5=1}
25901  e[^-1] ${arg\ 1+!$5,Add,Remove}" visible watermark '$1' on image$?, with opacity $2, size $3, angle $4 deg."
25902  repeat $! l[$>]
25903    0 t. "$1",0,0,$3,1,255 rotate. $4,0,0 b. $6 n. 0,255
25904    ri. ..,0,2 +. .. c. 0,255   # Generate opaque watermark image
25905    if $5 *. $2 *.. {1-$2} +  # Add watermark
25906    else *. $2 - / {1-$2}     # Remove watermark
25907    fi
25908    c 0,255
25909  endl done
25910
25911#--------------------------------------
25912#
25913#@cli :: Blending and Fading
25914#
25915#--------------------------------------
25916
25917#@cli blend : [layer],blending_mode,_opacity[%],_selection_is={ 0=base-layers | 1=top-layers } : \
25918# blending_mode,_opacity[%]
25919#@cli : Blend selected G,GA,RGB or RGBA images by specified layer or blend all selected images together,
25920#@cli : using specified blending mode.
25921#@cli : 'blending_mode' can be { add | alpha | and | average | blue | burn | darken | difference |
25922#@cli : divide | dodge | edges | exclusion | freeze | grainextract | grainmerge | green | hardlight |
25923#@cli : hardmix | hue | interpolation | lchlightness | lighten | lightness | linearburn | linearlight | luminance |
25924#@cli : multiply | negation | or | overlay | pinlight | red | reflect | saturation | seamless | seamless_mixed |
25925#@cli : screen | shapeareamax | shapeareamax0 | shapeareamin | shapeareamin0 | shapeaverage | shapeaverage0 |
25926#@cli : shapemedian | shapemedian0 | shapemin | shapemin0 | shapemax | shapemax0 | softburn | softdodge |
25927#@cli : softlight | stamp | subtract | value | vividlight | xor }.
25928#@cli : 'opacity' should be in '[0,1]', or '[0,100]' if expressed with a '%'.
25929#@cli : Default values: 'blending_mode=alpha', 'opacity=1' and 'selection_is=0'.
25930#@cli : $ image.jpg +drop_shadow , resize2dy[-1] 200 rotate[-1] 20 +blend alpha display_rgba[-2]
25931#@cli : $ image.jpg testimage2d {w},{h} blend overlay
25932#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
25933# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
25934# ex add,alpha,and,average,blue,burn,darken
25935#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
25936# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
25937# ex difference,divide,dodge,exclusion,freeze,grainextract,grainmerge
25938#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
25939# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
25940# ex green,hardlight,hardmix,hue,interpolation,lighten,lightness
25941#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
25942# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
25943# ex linearburn,linearlight,luminance,multiply,negation,or,overlay
25944#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
25945# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
25946# ex pinlight,red,reflect,saturation,screen,shapeaverage,softburn
25947#@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \
25948# text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \
25949# ex softdodge,softlight,stamp,subtract,value,vividlight,xor
25950blend : skip ${1=alpha},${2=1},${3=1},${4=0}
25951  if ${"is_image_arg $1"}
25952    n={narg($*)} mode=${arg\ 1+($n>=2),alpha,$2}
25953    e[^-1] "Blend image$? with "${arg\ 1+$4,base,top}" layer $1, using '"$mode"' mode and opacity $3."
25954    repeat $! pass$1 l[$>,-1] if $4 rv fi blend $mode,$3 endl done return
25955  fi
25956  e[^-1] "Blend all image$? together, using '$1' mode and opacity $2."
25957  repeat $!-1 l[0,1]
25958    r[1] [0],[0],[0],100%,0,0,0.5,0.5
25959    s={"s0 = s#0<3?1:3; s1 = s<3?1:3; max(s0,s1)"} # Target color format (G or RGB).
25960    to_colormode[0] {$s+1-(s#0%2)}                 # Target format (G,GA,RGB or RGBA).
25961    to_colormode[1] {$s+1-(s%2)}                   # Mask format (G,GA,RGB or RGBA).
25962    if {0,"s==2 || s==4"} # Target has alpha.
25963      if "s==2 || s==4" # Mask has alpha.
25964        sh[0,1] 0,{s-2} _blend_$1[2,3] rm[2,3]
25965        if ['"$1"']=='alpha' # Special blending code for alpha-mode.
25966          sh[0,1] 0,{{0,s}-2} sh[0,1] 100% *[2,4] *[3,4] rm[2,3]
25967          +channels[1] 100% sh[1] 100% f[3] 255 rm[3]
25968          j[0] [1],0,0,0,0,{max(0,min(1,$2))},[2],255 rm[1,2]
25969          sh[0] 0,{s-2} sh[0] 100% +[2] 1e-10 /[1,2] rm[1] c 0,255
25970        else
25971          sh[1] 0,{s-2} sh[1] 100% j[0] [2],0,0,0,0,{max(0,min(1,$2))},[3],255 rm[^0]
25972        fi
25973      else # Mask has no alpha.
25974        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]
25975      fi
25976    else # Target has no alpha.
25977      if "s==2 || s==4" # Mask has alpha.
25978        sh[1] 0,{s-2} _blend_$1[0,2] rm[2]
25979        sh[1] 100% j[0] [1],0,0,0,0,{max(0,min(1,$2))},[2],255 rm[^0]
25980      else # Mask has no alpha.
25981        _blend_$1 j[0] [1],0,0,0,0,{max(0,min(1,$2))} rm[1]
25982      fi
25983    fi
25984  endl done
25985
25986_blend_alpha :
25987_blend_normal :
25988_blend_and :
25989  &[1] [0]
25990_blend_add :
25991  +[1] [0] c[1] 0,255
25992_blend_average :
25993  +[1] [0] /[1] 2
25994_blend_blue :
25995  sh[0] 0,1 j[1] [2] rm[2]
25996_blend_burn :
25997  +-[0] 255 +[1] 0.1 /[2] [1] rm[1] +[1] 1 *[1] 255 c[1] 0,255
25998_blend_darken :
25999  min[1] [0]
26000_blend_difference :
26001  -[1] [0] abs[1]
26002_blend_divide :
26003  +[1] 0.1 ^[1] -1 *[1] [0] *[1] 255 c[1] 0,255
26004_blend_dodge :
26005  -[1] 255.1 ^[1] -1 *[1] [0] *[1] -255 c[1] 0,255
26006_blend_edges :
26007  +blend_edges 0.5 rm[1]
26008_blend_exclusion :
26009  +*[0,1] /[2] -127.5 +[1,2] +[1] [0]
26010_blend_freeze :
26011  *[1] -255 -[1] 0.1 +-[0] 255 sqr[2] /[2] [1] rm[1] +[1] 1 *[1] 255 c[1] 0,255
26012_blend_grainextract :
26013  -[1] [0] *[1] -1 +[1] 128 c[1] 0,255
26014_blend_grainmerge :
26015  +[1] [0] -[1] 128 c[1] 0,255
26016_blend_green :
26017  sh[0] 0 sh[0] 2 j[1] [2] j[1] [3],0,0,0,2 rm[2,3]
26018_blend_hardlight :
26019  +*[0,1] /[2] 127.5 ++[0,1] *[3] 2 -[3] 255 -[3] [2] >[1] 128
26020  j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255
26021_blend_hardmix :
26022  +[1] [0] >=[1] 255 *[1] 255
26023_blend_hue :
26024  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]
26025_blend_interpolation :
26026  +*[0] {pi/255} *[1] {pi/255} cos[1,2] +[1,2] -[1] 2 *[1] -63.75 c[1] 0,255
26027_blend_lighten :
26028  max[1] [0]
26029_blend_lightness :
26030  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]
26031_blend_lchlightness :
26032  _blend_lightness
26033_blend_luminance :
26034  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]
26035_blend_linearburn :
26036  +[1] [0] -[1] 255 c. 0,255
26037_blend_linearlight :
26038  *[1] 2 +[1] [0] -[1] 255 c[1] 0,255
26039_blend_multiply :
26040  *[1] [0] /[1] 255
26041_blend_negation :
26042  +[1] [0] -[1] 255 abs[1] *[1] -1 +[1] 255
26043_blend_or :
26044  -|[1] [0]
26045_blend_overlay :
26046  +*[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
26047_blend_pinlight :
26048  *[1] 2 +blend darken -[1] 256 +blend[0,1] lighten >=[1] 0
26049  j[2] [3],0,0,0,0,1,[1] rm[1,3]
26050_blend_reflect :
26051  -[1] 255.1 *[1] -1 +sqr[0] /[2] [1] rm[1] c[1] 0,255
26052_blend_red :
26053  sh[0] 1,100% j[1] [2],0,0,0,1 rm[2]
26054_blend_seamless :
26055  +blend_seamless 0 rm[1]
26056_blend_seamless_mixed :
26057  +blend_seamless 1 rm[1]
26058_blend_saturation :
26059  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
26060  hsv2rgb[2,3] rm[2,3]
26061_blend_screen :
26062  +-[0] 255 -[1] 255 *[1,2] /[1] 255 *[1] -1 +[1] 255
26063_blend_shapeareamax :
26064  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0
26065  +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0
26066  {1,iM+1},1,1,{0,s+1}
26067  f[1] ">area = i(#2); best = I[#3,i]; if (area>best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i"
26068  rm[2] channels[2] 0,{s-2} map[1] [2] rm[2]
26069_blend_shapeareamax0 :
26070  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
26071  +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0
26072  {1,iM+1},1,1,{0,s+1}
26073  f[1] ">area = i(#2); best = I[#3,i]; if (area>best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i"
26074  rm[2] channels[2] 0,{s-2} point[2] 0,0,0,1,0 map[1] [2] rm[2]
26075_blend_shapeareamin :
26076  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0
26077  +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0
26078  {1,iM+1},1,1,{0,s+1},inf
26079  f[1] ">area = i(#2); best = I[#3,i]; if (area<best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i"
26080  rm[2] channels[2] 0,{s-2} map[1] [2] rm[2]
26081_blend_shapeareamin0 :
26082  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
26083  +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0
26084  {1,iM+1},1,1,{0,s+1},inf
26085  f[1] ">area = i(#2); best = I[#3,i]; if (area<best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i"
26086  rm[2] channels[2] 0,{s-2} point[2] 0,0,0,1,0 map[1] [2] rm[2]
26087_blend_shapeaverage :
26088  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}
26089  f[0] ">i(#2,i(#1,x,y,z,0),0,0,c)+=i;i"
26090  +histogram[1] {w},0,{w-1} /[-2,-1] map[1] . rm.
26091_blend_shapeaverage0 :
26092  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}
26093  f[0] ">i(#2,i(#1,x,y,z,0),0,0,c)+=i;i"
26094  +histogram[1] {w},0,{w-1} /[-2,-1] point. 0,0,0,1,0 map[1] . rm.
26095_blend_shapemedian :
26096  f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0
26097  N={iM+1} $N,1,1,{s#0} $N,8,1,{s#0} s. x
26098  f[1] ">
26099    begin(siz = vector"$N"());
26100    k = i;
26101    k3 = k + 3;
26102    hk3 = h(#k3);
26103    copy(i[#k3,siz[k]++],I(#0),s#0,hk3,whd#0);
26104    if (siz[k]>=hk3,resize(#k3,1,round(1.5*hk3+1),1,s#0,0,0));
26105    end(repeat (size(siz),k, resize(#k+3,1,siz[k],1,s#0,0,0)));
26106    i"
26107  repeat s#0 sh[3--1] $> $N,1,1,1,"ic(#"$N"+3+x)" j[2] .,0,0,0,$> rm[-{$N+1}--1] done
26108  map[1] [2] k[0,1]
26109_blend_shapemedian0 :
26110  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
26111  N={iM} {$N+1},1,1,{s#0} $N,8,1,{s#0} s. x
26112  f[1] ">
26113    begin(siz = vector"$N"());
26114    k = i;
26115    if (k,
26116      k1 = k - 1;
26117      k2 = k + 2;
26118      hk2 = h(#k2);
26119      copy(i[#k2,siz[k1]++],I(#0),s#0,hk2,whd#0);
26120      if (siz[k1]>=hk2,resize(#k2,1,round(1.5*hk2+1),1,s#0,0,0));
26121    );
26122    end(repeat (size(siz),k, resize(#k+3,1,siz[k],1,s#0,0,0)));
26123    i"
26124  repeat s#0 sh[3--1] $> $N,1,1,1,"ic(#"$N"+3+x)" j[2] .,1,0,0,$> rm[-{$N+1}--1] done
26125  map[1] [2] k[0,1]
26126_blend_shapemin :
26127  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
26128  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"
26129  map[1] [2] rm.
26130_blend_shapemin0 :
26131  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
26132  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"
26133  point. 0,0,0,1,0 map[1] [2] rm.
26134_blend_shapemax :
26135  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
26136  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"
26137  map[1] [2] rm.
26138_blend_shapemax0 :
26139  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
26140  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"
26141  point. 0,0,0,1,0 map[1] [2] rm.
26142_blend_softburn :
26143  +-[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
26144  j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255
26145_blend_softdodge :
26146  +-[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
26147  j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255
26148_blend_softlight :
26149  +/[0] 255 /[1] 255 +sqr. *[2] [1] *[1] [3] *[1] -2 *[2] 2 +[1-3] *[1] 255 c[1] 0,255
26150_blend_stamp :
26151  *[1] 2 +[1] [0] -[1] 255 c[1] 0,255
26152_blend_subtract :
26153  -[1] [0] *[1] -1 c[1] 0,255
26154_blend_value :
26155  to_color sh 0,2 rgb2hsv[2,3] sh[2] 0,1 j[1] [4] rm[4] hsv2rgb[2,3] rm[2,3]
26156_blend_vividlight :
26157  *[1] 2 +blend burn -[1] 256 +blend[0,1] dodge >=[1] 0
26158  j[2] [3],0,0,0,0,1,[1] rm[1,3]
26159_blend_xor :
26160  xor[1] [0]
26161
26162#@cli blend_edges : smoothness[%]>=0
26163#@cli : Blend selected images togethers using 'edges' mode.
26164#@cli : $ image.jpg testimage2d {w},{h} +blend_edges 0.8
26165blend_edges : check {$1>=0}
26166  e[^-1] "Blend image$? using 'edges' mode, with smoothness $1."
26167  if $!>1 to_rgb ri[^0] [0],0,0,0.5,0.5 repeat $! l[$>]
26168    +gradient_norm +. 1 b. $1 n. 1,10 sqr. s.. c *[-4--2] . a[-4--1] c
26169  endl done ri[^0] [0],0,0,0.5,0.5 + s. c /[-4--2] . rm. a[-3--1] c fi
26170
26171#@cli blend_fade : [fading_shape]
26172#@cli : Blend selected images together using specified fading shape.
26173#@cli : $ image.jpg testimage2d {w},{h} 100%,100%,1,1,'cos(y/10)' normalize[-1] 0,1 +blend_fade[0,1] [2]
26174blend_fade :
26175  e[^-1] "Blend image$? together using fading pattern $1."
26176  r ${-max_whds},0
26177  pass$1 0 r. [0],[0],[0],100%,1 max. 0 min. {$!-2}
26178  repeat $!-1 +-. $> abs. -. 1 *. -1 max. 0 *[$>,-1] done rm.
26179  +
26180
26181_fade :
26182  ri.. ...,5 ri. ..,3 c. $1%,$2% n. 0,1 j... ..,0,0,0,0,1,. rm[-2,-1]
26183
26184#@cli blend_median
26185#@cli : Blend selected images together using 'median' mode.
26186#@cli : $ image.jpg testimage2d {w},{h} +mirror[0] y +blend_median
26187blend_median :
26188  e[^-1] "Blend image$? using 'median' mode."
26189  if $!<2 return fi
26190  to_colormode 0 r ${-max_whd},100%,0,0,0.5,0.5,0.5
26191  if $!==2 + / 2
26192  else
26193    whds={w},{h},{d},{s} r 100%,100%,{d*s},1,-1 a c
26194    100%,100%,100%,1,"med(I(#0))" k. r $whds,-1
26195  fi
26196
26197#@cli blend_seamless : _is_mixed_mode={ 0 | 1 },_inner_fading[%]>=0,_outer_fading[%]>=0
26198#@cli : Blend selected images using a seamless blending mode (Poisson-based).
26199#@cli : Default values: 'is_mixed=0', 'inner_fading=0' and 'outer_fading=100%'.
26200blend_seamless : check "${2=0}>=0 && ${3=100%}>=0" skip ${1=0}
26201  s0="non-mixed" s1="mixed"
26202  e[^-1] "Blend image$? using seamless mode (Poisson-based), in "${s{$1!=0}}" mode with inner fading $2 and
26203              outer fading $3."
26204  to_a[^0] r {0,w+32},{0,h+32},1,100%,0,0,0.5,0.5 # Avoid periodic boundaries problems.
26205
26206  if ['$3']!='100%' # With outer fading.
26207    repeat $!-1 l[0,1]
26208      +blend_seamless $1,$2,100% channels.. 100% !=.. 0 distance.. 1
26209      iM={-2,iM} ic={if(${is_percent\ $3},2*$3*$iM,1+$3)}
26210      if $ic<=$iM c.. 0,{max(1,$ic)} n.. 0,1
26211      else n.. 0,{max(0,2-$ic/$iM)}
26212      fi
26213      *.. -1 +.. 1
26214      j[0] [2],0,0,0,0,1,[1] rm[1,2]
26215    endl done
26216  else # Without outer fading.
26217
26218    repeat $!-1 l[0,1]
26219
26220      # Get background average color.
26221      +r[0] 1,1,1,100%,2 avg={^} rm.
26222
26223      # Compute mixed gradients of background and top layer.
26224      split_opacity. !=. 0 *.. . erode. 3
26225      g[0,1] xy,1
26226      *[-3,-2] .
26227
26228      # Modify mask if 'mixed' mode selected.
26229      if $1
26230        +a[0,1] c +a[2,3] c norm[-2,-1]
26231        <[-2,-1] *[-2,-1]
26232      fi
26233
26234      # Compute the desired gradient map.
26235      if $2
26236        distance. 0
26237        iM={iM} ic={if(${is_percent\ $2},2*$2*$iM,1+$2)}
26238        if $ic<=$iM c. 0,{max(1,$ic)} n. 0,1
26239        else n. 0,{max(0,2-$ic/$iM)}
26240        fi
26241      fi
26242
26243      j[-5] ...,0,0,0,0,1,.
26244      j[-4] ..,0,0,0,0,1,.
26245      rm[-3--1]
26246
26247      # Compute divergence (right-hand term of Poisson eq.)
26248      g[0] x,-1 g[1] y,-1 +
26249
26250      # Inverse Laplacian and renormalize
26251      ilaplacian 0
26252      +fc. $avg
26253      +[-2,-1]
26254      c 0,255
26255
26256    endl done
26257  fi
26258  z 16,16,{w-17},{h-17}
26259
26260#@cli fade_diamond : 0<=_start<=100,0<=_end<=100
26261#@cli : Create diamond fading from selected images.
26262#@cli : Default values: 'start=80' and 'end=90'.
26263#@cli : $ image.jpg testimage2d {w},{h} +fade_diamond 80,85
26264fade_diamond : skip ${1=70},${2=90}
26265  e[^-1] "Create ($1%,$2%) diamond-shaped fading from image$?."
26266  repeat int($!/2) l[$>,{$>+1}]
26267    (0,1,0;1,1,1;0,1,0) _fade $1,$2
26268  endl done
26269
26270#@cli fade_linear : _angle,0<=_start<=100,0<=_end<=100
26271#@cli : Create linear fading from selected images.
26272#@cli : Default values: 'angle=45', 'start=30' and 'end=70'.
26273#@cli : $ image.jpg testimage2d {w},{h} +fade_linear 45,48,52
26274fade_linear : skip ${1=45},${2=30},${3=70}
26275  e[^-1] "Create ($2%,$3%) linear fading from image$?, with angle $1 deg."
26276  repeat int($!/2) l[$>,{$>+1}]
26277     64,64,1,1,"x*cos($1*pi/180) + y*sin($1*pi/180)" _fade $2,$3
26278  endl done
26279
26280#@cli fade_radial : 0<=_start<=100,0<=_end<=100
26281#@cli : Create radial fading from selected images.
26282#@cli : Default values: 'start=30' and 'end=70'.
26283#@cli : $ image.jpg testimage2d {w},{h} +fade_radial 30,70
26284fade_radial : skip ${1=30},${2=70}
26285  e[^-1] "Create ($1%,$2%) radial fading from image$?."
26286  repeat int($!/2) l[$>,{$>+1}]
26287    100%,100% =. 1,50%,50% distance. 1 _fade $1,$2
26288  endl done
26289
26290#@cli fade_x : 0<=_start<=100,0<=_end<=100
26291#@cli : Create horizontal fading from selected images.
26292#@cli : Default values: 'start=30' and 'end=70'.
26293#@cli : $ image.jpg testimage2d {w},{h} +fade_x 30,70
26294fade_x : skip ${1=30},${2=70}
26295  e[^-1] "Create ($1%,$2%) horizontal fading from image$?."
26296  repeat int($!/2) l[$>,{$>+1}] (0,1) _fade $1,$2 endl done
26297
26298#@cli fade_y : 0<=_start<=100,0<=_end<=100
26299#@cli : Create vertical fading from selected images.
26300#@cli : Default values: 'start=30' and 'end=70'.
26301#@cli : $ image.jpg testimage2d {w},{h} +fade_y 30,70
26302fade_y : skip ${1=30},${2=70}
26303  e[^-1] "Create ($1%,$2%) vertical fading from image$?."
26304  repeat int($!/2) l[$>,{$>+1}] (0;1) _fade $1,$2 endl done
26305
26306#@cli fade_z : 0<=_start<=100,0<=_end<=100
26307#@cli : Create transversal fading from selected images.
26308#@cli : Default values: 'start=30' and 'end=70'.
26309fade_z : skip ${1=30},${2=70}
26310  e[^-1] "Create ($1%,$2%) transversal fading from image$?."
26311  repeat int($!/2) l[$>,{$>+1}] (0/1) _fade $1,$2 endl done
26312
26313#@cli sub_alpha : [base_image],_opacity_gain>=1
26314#@cli : Compute the minimal alpha-channel difference (opposite of alpha blending) between the selected images
26315#@cli : and the specified base image.
26316#@cli : The alpha difference A-B is defined as the image having minimal opacity, such that alpha_blend(B,A-B) = A.
26317#@cli : Default value: 'opacity_gain=1'.
26318#@cli : $ image.jpg testimage2d {w},{h} +sub_alpha[0] [1] display_rgba
26319sub_alpha : check "${2=1}>=1 && "${"is_image_arg $1"}
26320  e[^-1] "Compute minimal alpha-channel difference between image$? and base image $1, with opacity gain $2."
26321  remove_opacity repeat $! pass$1 0 l[$>,-1]
26322    to_colormode 0 r ${-max_whd},100%,0,0,0.5,0.5  # Normalize image dimensions.
26323
26324    # Estimate minimal alpha-channel.
26325    +>[0,1] *[2] 255
26326    -[0,2] [1] replace[2] 0,1
26327    i[2] [0] /[2,3]
26328    compose_channels[2] max
26329    *[2] $2 c[2] 0,1
26330
26331    # Synthetize alpha-difference image.
26332    +replace[2] 0,1 /[0,3] +[0,1] *[1] 255 a c
26333
26334  endl done
26335
26336#---------------------------------------------
26337#
26338#@cli :: Image Sequences and Videos
26339#
26340#---------------------------------------------
26341
26342#@cli animate : filter_name,"param1_start,...,paramN_start","param1_end,...,paramN_end",nb_frames>=0,\
26343# _output_frames={ 0 | 1 },_output_filename : delay>0,_back and forth={ 0 | 1 }
26344#@cli : Animate filter from starting parameters to ending parameters or animate selected images
26345#@cli : in a display window.
26346#@cli : Default value: 'delay=30'.
26347#@cli : $ image.jpg animate flower,"0,3","20,8",9
26348animate : skip ${1=30},${2=0},${3=""},${4=10},${5=1},"${6=}"
26349  if "isnum($1)"
26350    e[0--3] "Animate image$?, with a delay of $1 ms"${"if $2 u \", in back-and-forth mode\" else u \"\" fi"}.
26351    if !$! return fi
26352    speed,pause,direction,scale,frame=$1,-1,1,1,0
26353    is_same_size={"res = 1; s = [ w#0,h#0 ]; for (k = 1, k<l && res, ++k, res = (s==[ w#k,h#k ]))"}
26354
26355    w[0] ${fitscreen[]\ {0,[w,h]}},1,0,{0,b}.{0,x}
26356    do
26357      title={$frame,b}{`narg({$frame,x})?_'.':0`}{$frame,x}
26358      if $is_same_size
26359        w[$frame] -1,-1,1,0,$title
26360      else
26361        w[$frame] {$scale*[${fitscreen[]\ {$frame,[w,h]}}]},1,0,$title
26362      fi
26363      frame+=$direction
26364      if $2
26365        if $frame==-1 frame=0 direction=1
26366        elif $frame==$! frame={$!-1} direction=-1
26367        fi
26368      else frame%=$!
26369      fi
26370      wait $speed
26371
26372      # Increase window size.
26373      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} scale*=1.5 wait -1 fi
26374
26375      # Decrease window size.
26376      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} scale/=1.5 wait -1 fi
26377
26378      # Reset window size.
26379      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} scale=1 wait -1 fi
26380      if {*,o} speed={min(500,max(10,$speed-10*{*,o}))} wait -1 fi
26381      if {*,SPACE}
26382        if $pause>=0 direction=$pause pause=-1
26383        else pause=$direction direction=0 fi
26384        wait -1
26385      fi
26386
26387    while {*}" && "!{*,Q}" && "!{*,ESC} w 0
26388  else
26389    e[0--3] "Compute animated version of filter '$1', from parameters $2 to $3 with $4 frames."
26390    if !($5||narg("$6")) return fi
26391    ($2) ($3) y[-2,-1] x a[-2,-1] y r. 100%,$4,1,1,3 mv. 0 rprogress 0
26392    repeat $!-1,u
26393      e[] " > Animate image ["$>"]"
26394      repeat $4 +l[0,1]
26395        -$1. {0,@{$>*{0,w}}-{($>+1)*{0,w}-1}} rm[0]
26396        if narg("$6") o ${filename\ "$6",$u,$>} fi
26397        if !$5 rm fi
26398        rprogress {100*($>+1)/$4}
26399        e[] "\r > Animate image ["$u"] : Frame "{$>+1}"/$4    "
26400      endl done
26401    rm[1] done rm[0]
26402  fi
26403
26404#@cli apply_camera : _"command",_camera_index>=0,_skip_frames>=0,_output_filename
26405#@cli : Apply specified command on live camera stream, and display it on display window [0].
26406#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
26407#@cli : Default values: 'command=""', 'camera_index=0' (default camera), 'skip_frames=0' and 'output_filename=""'.
26408apply_camera : check_opencv $0 skip "${1=},${4=}" check "${2=0}>=0 && ${3=0}>=0"
26409  e[^-1] "Apply command '$1' on camera stream ""#$2, with $3 frames skip and output filename '$4'."
26410  is_ext "$4",avi is_outavi=${}
26411  is_ext "$4",mp4 is_outmp4=${}
26412  l[] i=0 do
26413    camera $2,1,$3 $1 w. -1,-1,"[G'MIC] Camera ""#$2 ("{w}x{h}")"
26414    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
26415    if {*,S} o. gmic_camera.png fi
26416    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
26417    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
26418    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
26419    rm.
26420  while {*}" && "!{*,ESC}" && "!{*,Q} camera $2,0 endl
26421
26422#@cli apply_files : "filename_pattern",_"command",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\
26423# _output_filename
26424#@cli : Apply a G'MIC command on specified input image files, in a streamed way.
26425#@cli : If a display window is opened, rendered frames are displayed in it during processing.
26426#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image file
26427#@cli : extension (saved as a sequence of images).
26428#@cli : Default values: 'command=(undefined)', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \
26429# and 'output_filename=(undefined)'.
26430apply_files : check "isint(${3=0}) && $3>=0 && isint(${4=-1}) && ($4>=0 || $4==-1) && ${5=1}>=1" skip "${2=},${6=}"
26431  e[^-1] "Apply command '$2' on input image files '$1', with first frame $3, last frame $4, frame step $5 and
26432          output filename '$6'.\n"
26433  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _apply_stream[] "${_file{$frame+1}}","$2",${3-5},"$6"
26434
26435#@cli apply_video : video_filename,_"command",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\
26436# _output_filename
26437#@cli : Apply a G'MIC command on all frames of the specified input video file, in a streamed way.
26438#@cli : If a display window is opened, rendered frames are displayed in it during processing.
26439#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
26440#@cli : file extension (saved as a sequence of images).
26441#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
26442#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'.
26443apply_video : check_opencv $0 check "isint(${3=0}) && $3>=0 && isint(${4=-1}) && ($4>=0 || $4==-1) && ${5=1}>=1"
26444              skip "${2=},${6=}"
26445  e[^-1] "Apply command '$2' on input video file '$1', with first frame $3, last frame $4, frame step $5 and
26446          output filename '$6'.\n"
26447  _N= v + _apply_stream[] "\"$1\",$frame","$2",${3-5},"$6"
26448
26449_apply_stream : skip "${2=},${6=}"
26450  is_ext "$6",avi is_outavi=${}
26451  is_ext "$6",mp4 is_outmp4=${}
26452  frame=$3 i=0 go_on=1
26453  do
26454    l[] $1 onfail go_on=0 endl
26455    if $go_on
26456      e[] "\r  > Frame ""#"$frame$_N"        "
26457      frame+=$5
26458      l $2 onfail error[0--5] "Command 'apply_stream': Specified command errored: "${} endl
26459      if !$! continue fi
26460      if narg("$6")
26461        if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$6",25,mp4v,1 else o. ${filename\ "$6",$i} i+=1 fi
26462      fi
26463      if {*}
26464        title="[G'MIC] Frame ""#"$frame
26465        if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,0,$title
26466        else w. -1,-1,0,$title
26467        fi
26468        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
26469        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
26470        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
26471      fi
26472      rm.
26473    fi
26474  while $go_on" && "($4==-1" || "$frame<=$4)
26475  if $is_outavi||$is_outmp4 o[] "$6",25,mp4v,0 fi
26476
26477#@cli average_files : "filename_pattern",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename
26478#@cli : Average specified input image files, in a streamed way.
26479#@cli : If a display window is opened, rendered frames are displayed in it during processing.
26480#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
26481#@cli : file extension (saved as a sequence of images).
26482#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'.
26483average_files : check "isint(${2=0}) && $2>=0 && isint(${3=-1}) && ($3>=0 || $3==-1) && ${4=1}>=1" skip "${5=}"
26484  e[^-1] "Average input image files '$1', with first frame $2, last frame $3, frame step $4 and
26485          output filename '$5'.\n"
26486  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _average_stream[] "${_file{$frame+1}}",${2-4},"$5"
26487
26488#@cli average_video : video_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename
26489#@cli : Average frames of specified input video file, in a streamed way.
26490#@cli : If a display window is opened, rendered frames are displayed in it during processing.
26491#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
26492#@cli : file extension (saved as a sequence of images).
26493#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
26494#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'.
26495average_video : check_opencv $0 check "isint(${2=0}) && $2>=0 && isint(${3=-1}) && ($3>=0 || $3==-1) && ${4=1}>=1"
26496                skip "${5=}"
26497  e[^-1] "Average frames of input video file '$1', with first frame $2, last frame $3, frame step $4 and
26498          output filename '$5'.\n"
26499  _N= v + _average_stream[] "\"$1\",$frame",${2-4},"$5"
26500
26501_average_stream : skip "${5=}"
26502  is_ext "$5",avi is_outavi=${}
26503  is_ext "$5",mp4 is_outmp4=${}
26504  frame=$2 i=0 go_on=1 N=0
26505  imM=inf,-inf
26506  do
26507    l[] $1 onfail go_on=0 endl
26508    if $go_on
26509      e[] "\r  > Frame ""#"$frame$_N"        "
26510      imM={v=[$imM];[min(im,v[0]),max(iM,v[1])]}
26511      N+=1
26512      if $!>1 r ${-max_whds} + fi
26513      if narg("$5")
26514        +/. $N c. $imM
26515        if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$5",25,mp4v,1 else o. ${filename\ "$5",$i} i+=1 fi
26516        rm.
26517      fi
26518      if {*}
26519        title="[G'MIC] Frame ""#"$frame
26520        +n 0,255
26521        if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,0,$title
26522        else w. -1,-1,0,$title
26523        fi
26524        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
26525        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
26526        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
26527        rm.
26528      fi
26529      frame+=$4
26530    fi
26531  while $go_on" && "($3==-1" || "$frame<=$3)
26532  / $N c $imM
26533  if $is_outavi||$is_outmp4 o[] "$5",25,mp4v,0 fi
26534
26535#@cli fade_files : "filename_pattern",_nb_inner_frames>0,_first_frame>=0,_last_frame={ >=0 | -1=last },\
26536# _frame_step>=1,_output_filename
26537#@cli : Generate a temporal fading from specified input image files, in a streamed way.
26538#@cli : If a display window is opened, rendered frames are displayed in it during processing.
26539#@cli : The output filename may have extension 'avi' or 'mp4' (saved as a video), or any other usual image
26540#@cli : file extension (saved as a sequence of images).
26541#@cli : Default values: 'nb_inner_frames=10', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \
26542# and 'output_filename=(undefined)'.
26543fade_files : check "isint(${2=10}) && $2>0 && isint(${3=0}) && $3>=0 &&
26544                    isint(${4=-1}) && ($4>=0 || $4==-1) && ${5=1}>=1" skip "${6=}"
26545  e[^-1] "Fade input image files '$1', with $2 inner frames, first frame $3, last frame $4, frame step $5 and
26546          output filename '$6'.\n"
26547  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _fade_stream[] "${_file{$frame+1}}",${2-5},"$6"
26548
26549#@cli fade_video : video_filename,_nb_inner_frames>0,_first_frame>=0,_last_frame={ >=0 | -1=last },\
26550# _frame_step>=1,_output_filename
26551#@cli : Create a temporal fading sequence from specified input video file, in a streamed way.
26552#@cli : If a display window is opened, rendered frames are displayed in it during processing.
26553#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
26554#@cli : Default values: 'nb_inner_frames=10', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \
26555# and 'output_filename=(undefined)'.
26556fade_video : check_opencv $0 check "isint(${2=10}) && $2>0 && isint(${3=0}) && $3>=0 &&
26557             isint(${4=-1}) && ($4>=0 || $4==-1) && ${5=1}>=1" skip "${6=}"
26558  e[^-1] "Fade frames of input video file '$1', with $2 inner frames, first frame $3, last frame $4, frame step $5 and
26559          output filename '$6'.\n"
26560  _N= v + _fade_stream[] "\"$1\",$frame",${2-5},"$6"
26561
26562_fade_stream : skip "${6=}"
26563  is_ext "$6",avi is_outavi=${}
26564  is_ext "$6",mp4 is_outmp4=${}
26565  frame=$3 i=0 go_on=1
26566
26567  l $1 onfail go_on=0 endl # Load first image.
26568  if !$go_on return fi
26569  w={w} h={h} s={s}
26570  if {*} w. ${fitscreen\ $w,$h},0,"[G'MIC]" fi
26571  pframe=$frame frame+=$5
26572  do
26573    l[] $1 onfail go_on=0 endl
26574    if !$go_on break fi
26575    to_colormode. $s r. $w,$h
26576    repeat $2+2 if $<
26577      title="[G'MIC] Frame ""#"$pframe" -> ""#"$frame$_N" ("{1+$>}/$2")"
26578      e[] "\r  - "$title
26579      +j[0] [1],0,0,0,0,{$>/($2+1)}
26580      if narg("$6")
26581        if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$6",25,mp4v,1 else filename "$6",$i i+=1 o. ${} fi
26582      fi
26583      if {*}
26584        w. -1,-1,0,$title
26585        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
26586        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
26587        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
26588      fi
26589      rm.
26590    fi done
26591    rm[0] pframe=$frame frame+=$5
26592  while $go_on" && "($4==-1" || "$frame<=$4)
26593
26594  # Output last frame.
26595  if narg("$6")
26596    if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o[] "$6",25,mp4v,0 else filename "$6",$i o. ${} fi
26597  fi
26598  rm
26599
26600#@cli files2video : "filename_pattern",_output_filename,_fps>0,_codec
26601#@cli : Convert several files into a single video file.
26602#@cli : Default values: 'output_filename=output.mp4', 'fps=25' and 'codec=mp4v'.
26603files2video : check "isint(${3=25}) && $3>0" skip "${2=output.mp4}",${4=mp4v}
26604  files=${"files \"$1\""} arg2var _file,$files nb_files=${}
26605  ('$files') if w>128 z. 0,127 s_files={t}... else s_files=$files fi rm.
26606  e[^-1] "Convert image files '"$s_files"' into frames of output video '$2', with $3 fps and $4 codec.\n"
26607  repeat $nb_files l[]
26608    file=${_file{$>+1}}
26609    _file=${basename\ $file}
26610    e[] "\r - Image "{1+$>}/$nb_files" ["$_file"] -> [$2]                    "
26611    i $file o "$2",$3,$4,1
26612    rm
26613  onfail e[] "\n - Error occurred on input file '"$file"'.\n"
26614  endl done
26615  o $"$2",0,0,0
26616
26617#@cli median_files : "filename_pattern",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\
26618# _frame_rows[%]>=1,_is_fast_approximation={ 0 | 1 }
26619#@cli : Compute the median frame of specified input image files, in a streamed way.
26620#@cli : If a display window is opened, rendered frame is displayed in it during processing.
26621#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1', 'frame_rows=20%' \
26622# and 'is_fast_approximation=0'.
26623median_files : check "isint(${2=0}) && $2>=0 && isint(${3=-1}) && ($3>=0 || $3==-1) &&
26624                     ${4=1}>=1 && ${5=20%}>0 && isnum(${6=0})"
26625  s0="fast" s1="precise"
26626  e[^-1] "Compute median of input image files '$1', with first frame $2, last frame $3, frame step $4,
26627              frame rows $5, using "${s{!$6}}" algorithm."
26628  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${}
26629  l[]
26630    ${_file{$frame+1}} nm. res f. 0
26631    v + _median_stream "${_file{$frame+1}}",${2-6} v -
26632  endl
26633
26634#@cli median_video : video_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\
26635# _frame_rows[%]>=1,_is_fast_approximation={ 0 | 1 }
26636#@cli : Compute the median of all frames of an input video file, in a streamed way.
26637#@cli : If a display window is opened, rendered frame is displayed in it during processing.
26638#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
26639#@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1', 'frame_rows=100%' \
26640# and 'is_fast_approximation=1'.
26641median_video : check_opencv $0 check "isint(${2=0}) && $2>=0 && isint(${3=-1}) && ($3>=0 || $3==-1) &&
26642               ${4=1}>=1 && ${5=100%}>0 && isnum(${6=1})"
26643  s0="fast" s1="precise"
26644  e[^-1] "Compute median frame of input video file '$1', with first frame $2, last frame $3, frame step $4,
26645              frame rows $5, using "${s{!$6}}" algorithm."
26646  _N=
26647  l[]
26648    "$1",0 nm. res f. 0
26649    v + _median_stream "\"$1\",$frame",${2-6} v -
26650  endl
26651
26652_median_stream :
26653
26654  # Retrieve min/max values of all frames when fast method is used.
26655  if $6
26656    e[] "- Retrieve min/max values of all frames.\n"
26657    frame=$2 go_on=1
26658    imM=inf,-inf
26659    do
26660      l[] $1 onfail go_on=0 endl
26661      if $go_on
26662        e[] "\r    > Frame ""#"$frame$_N"        "
26663        imM={v=[$imM];[min(im,v[0]),max(iM,v[1])]}
26664        if {*}
26665          title="[G'MIC] Frame ""#"$frame
26666          if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,1,$title
26667          else w. -1,-1,1,$title
26668          fi
26669        fi
26670        rm.
26671        frame+=$4
26672      fi
26673    while $go_on" && "($3==-1" || "$frame<=$3)
26674    _N=/{$frame-$4}
26675    fact={v=[$imM];dv=v[1]-v[0];dv<=0?0:255/dv}
26676  fi
26677
26678  # Start median computation.
26679  h1={h-1} drows={round(${is_percent\ $5}?$5*h:$5)}
26680  nb_iter={round(h/$drows,1,1)}
26681  repeat $nb_iter
26682    row0={$drows*$>} row1={0,min(h,$row0+$drows-1)}
26683    e[] "- Iteration \#"{$>+1}/$nb_iter": Load rows "$row0-$row1/$h1".\n"
26684    frame=$2 go_on=1
26685
26686    if $6
26687      # Fast method : compute median frame using streamed histogram computation.
26688      N=0
26689      {w},$drows,256,{s} nm. hist
26690      do
26691        l[] $1 nm. img onfail go_on=0 endl
26692        if $img
26693          e[] "\r    > Frame ""#"$frame$_N"        "
26694          if {*}" && "!$>
26695            title="[G'MIC] Frame ""#"$frame
26696            if !narg($wh) wh=${fitscreen[]\ {img,[w,h]}} w[img] $wh,1,$title
26697            else w[img] -1,-1,1,$title
26698            fi
26699          fi
26700          rows[img] $row0,$row1 f[img] ":++i(#-2,x,y,round(i*"$fact"),c)"
26701          rm[img]
26702          frame+=$4 N+=1
26703        fi
26704      while $go_on" && "($3==-1" || "$frame<=$3)
26705      cumulate[hist] z
26706      N2={int($N/2)}
26707      [hist],[hist],1,[hist]
26708      if $N%2 # Odd number of frames.
26709        f. ":go_on = 1; for (z = 0, i(#"$hist",x,y,z,c)<"$N2" && z<256, ++z); z"
26710      else # Even number of frames.
26711        f. ":begin(N2p = "$N2"; N2n = N2p + 1);
26712             go_on = 1;
26713             for (zp = 0, i(#"$hist",x,y,zp,c)<N2p && zp<256, ++zp);
26714             for (zn = zp, i(#"$hist",x,y,zn,c)<N2n && zn<256, ++zn);
26715             0.5*(zn + zp);"
26716      fi
26717      rm..
26718      /. $fact c. $imM
26719
26720    else
26721      # Slow method : compute median frame using temporal quicksort.
26722      l[]
26723        do
26724          l[] $1 nm. img onfail go_on=0 endl
26725          if $go_on
26726            e[] "\r    > Frame ""#"$frame$_N"        "
26727            if {*}" && "!$>
26728              title="[G'MIC] Frame ""#"$frame
26729              if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,1,$title
26730              else w. -1,-1,1,$title
26731              fi
26732            fi
26733            rows. $row0,$row1
26734            frame+=$4
26735          fi
26736        while $go_on" && "($3==-1" || "$frame<=$3)
26737        e[] "\r    > Compute median blending of "$!" frames."
26738        __median_stream
26739      endl
26740    fi
26741
26742    _N=/{$frame-$4}
26743    j[res] .,0,$row0
26744    if {*} w[res] -1,-1,1,"[G'MIC] Iteration ""#"$> fi
26745    rm.
26746
26747  done
26748  e[] "- Done!"
26749
26750# Median blending optimized to deal with a lot of input frames.
26751__median_stream :
26752  if $!<2 return
26753  elif $!==2 + / 2
26754  else
26755    f. ":
26756      stack = vector"{0,2*$!}"();
26757      stacksize = 0;
26758      push(elt0,elt1) = (stack[stacksize++] = elt0; stack[stacksize++] = elt1);
26759      pop() = (_s1 = stack[--stacksize]; _s0 = stack[--stacksize]; [_s0,_s1]);
26760      push(0,"$!" - 1);
26761      while (stacksize>0,
26762        range = pop();
26763        lo = range[0];
26764        hi = range[1];
26765        pivot = i(#int((lo + hi)/2));
26766        while (lo<=hi,
26767           while (i(#lo)<pivot, ++lo);
26768           while (pivot<i(#hi), --hi);
26769           if (lo<=hi, _tmp = i(#lo); i(#lo++) = i(#hi); i(#hi--) = _tmp);
26770        );
26771        if (range[0]<hi,push(range[0],hi));
26772        if (lo<range[1],push(lo,range[1]));
26773      )"
26774    if $!%2 k[{int($!/2)}]
26775    else k[{int($!/2-1)},{$!/2}] + / 2
26776    fi
26777  fi
26778
26779#@cli morph : nb_inner_frames>=1,_smoothness>=0,_precision>=0
26780#@cli : Create morphing sequence between selected images.
26781#@cli : Default values: 'smoothness=0.1' and 'precision=4'.
26782#@cli : $ image.jpg +rotate 20,1,1,50%,50% morph 9
26783morph : check "$1>=0.5 && ${2=0.1}>=0 && ${3=4}>=0"
26784  nbf={round($1)}
26785  e[^-1] "Create morphing sequence between image$?, with "$nbf" inner frames, smoothness $2 and precision $3.\n"
26786  nchan=${-max_s} if $nchan<=4 to_colormode $nchan else channels 0,{$nchan-1} fi
26787  ri[^0] [0],3
26788  repeat $!-1 nm={$>,n} l[$<,{$<+1}]
26789    e[] "\r > Morph image "$>" to image "{$>+1}".    "
26790    +equalize[0,1] n[-2,-1] 0,255
26791    +displacement[3] [2],$2,$3 +displacement[2] [3],$2,$3 rm[-4,-3]
26792    repeat $nbf+2 if $>&&$<
26793      t={$>/($nbf+1)} omt={1-$t}
26794      +*[2] $t +warp[0] .,1,1,1 rm.. *. $omt
26795      +*[3] {1-$t} +warp[1] .,1,1,1 rm.. *. $t
26796      +[-2,-1]
26797    fi done
26798    rm[2,3] mv[2--1] 1 nm $nm
26799  endl done
26800
26801#@cli morph_files : "filename_pattern",_nb_inner_frames>0,_smoothness>=0,_precision>=0,\
26802# _first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename
26803#@cli : Generate a temporal morphing from specified input image files, in a streamed way.
26804#@cli : If a display window is opened, rendered frames are displayed in it during processing.
26805#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
26806#@cli : file extension (saved as a sequence of images).
26807#@cli : Default values: 'nb_inner_frames=10', 'smoothness=0.1', 'precision=4', 'first_frame=0', 'last_frame=-1', \
26808# 'frame_step=1' and 'output_filename=(undefined)'.
26809morph_files : check "isint(${2=10}) && $2>0 && ${3=0.1}>=0 && ${4=4}>=0 &&
26810                     isint(${5=0}) && $5>=0 && isint(${6=-1}) && ($6>=0 || $6==-1) && ${7=1}>=1" skip "${8=}"
26811  e[^-1] "Morph input image files '$1', with $2 inner frames, smoothness $3, precision $4, first frame $5,
26812          last frame $6, frame step $7 and output filename '$8'.\n"
26813  files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} _morph_stream[] "${_file{$frame+1}}",${2-7},"$8"
26814
26815#@cli morph_rbf : nb_inner_frames>=1,xs0[%],ys0[%],xt0[%],yt0[%],...,xsN[%],ysN[%],xtN[%],ytN[%]
26816#@cli : Create morphing sequence between selected images, using RBF-based interpolation.
26817#@cli : Each argument (xsk,ysk)-(xtk,ytk) corresponds to the coordinates of a keypoint
26818#@cli : respectively on the source and target images. The set of all keypoints define the overall image deformation.
26819morph_rbf : check "$1>=0.5"
26820  nbf={round($1)}
26821  e[^-1] "Create morphing sequence between image$? using RBF interpolation, "\
26822    "with "$nbf" inner frames and keypoints ($*)."
26823  $=arg N={($#-1)/4}
26824  if int($N)!=$N error[0--2] "Command 'morph_rbf': Wrong number of arguments ($#)." fi
26825  ri[^0] [0],3
26826  repeat $!-1 nm={$>,n} l[$<,{$<+1}]
26827    e[] "\r > Morph image "$>" to image "{$>+1}".    "
26828
26829    # Retrieve absolute keypoints coordinates.
26830    4,$N
26831    repeat wh a=${arg{2+$>}} isp=${"is_percent "$a} eval i[$>]=$isp?($>%2?w#0:h#0)*$a:$a done
26832    permute. yzcx
26833
26834    # Generate forward and backward warping fields.
26835    +f. "[i0,i1,i2-i0,i3-i1]"
26836    f.. "[i2,i3,i0-i2,i1-i3]"
26837    rbf[-2,-1] {0,[w,h]}
26838
26839    # Compute morphing sequence.
26840    repeat $nbf+2 if $>&&$<
26841      [0],[0],1,[0],"
26842        const interpolation = 1;
26843        const boundary = 3;
26844        const t = "$>"/("$nbf"+1);
26845        const omt = 1 - t;
26846        begin(print([t,omt]));
26847        ub = i(#2,x,y,0,0);
26848        vb = i(#2,x,y,0,1);
26849        uf = i(#3,x,y,0,0);
26850        vf = i(#3,x,y,0,1);
26851        omt*I(#0,x - t*uf,y - t*vf) + t*I(#1,x - omt*ub,y - omt*vb)"
26852    fi done
26853    rm[2,3] mv[2--1] 1 nm $nm
26854  endl done
26855
26856#@cli morph_video : video_filename,_nb_inner_frames>0,_smoothness>=0,_precision>=0,\
26857# _first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename
26858#@cli : Generate a temporal morphing from specified input video file, in a streamed way.
26859#@cli : If a display window is opened, rendered frames are displayed in it during processing.
26860#@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image
26861#@cli : file extension (saved as a sequence of images).
26862#@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default).
26863#@cli : Default values: 'nb_inner_frames=10', 'smoothness=0.1', 'precision=4', 'first_frame=0', 'last_frame=-1', \
26864# 'frame_step=1' and 'output_filename=(undefined)'.
26865morph_video : check_opencv $0 check "isint(${2=10}) && $2>0 && ${3=0.1}>=0 && ${4=4}>=0 &&
26866              isint(${5=0}) && $5>=0 && isint(${6=-1}) && ($6>=0 || $6==-1) && ${7=1}>=1" skip "${8=}"
26867  e[^-1] "Morph frames of input video file '$1', with $2 fading frames, smoothness $3, precision $4, first frame $5,
26868          last frame $6, frame step $7 and output filename '$8'.\n"
26869  _N= v + _morph_stream[] "\"$1\",$frame",${2-7},"$8"
26870
26871_morph_stream : skip "${8=}"
26872  is_ext "$8",avi is_outavi=${}
26873  is_ext "$8",mp4 is_outmp4=${}
26874  frame=$5 i=0 go_on=1
26875
26876  l $1 onfail go_on=0 endl # Load first image.
26877  if !$go_on return fi
26878  w={w} h={h} s={s}
26879  if {*} w. ${fitscreen\ $w,$h},0,"[G'MIC]" fi
26880  pframe=$frame frame+=$7
26881  do
26882    l[] $1 onfail go_on=0 endl
26883    if !$go_on break fi
26884    to_colormode. $s r. $w,$h
26885    cutvals={[min(im#0,im#1),max(iM#0,iM#1)]}
26886    e[] "\r  - Frame ""#"$pframe" -> ""#"$frame"            "
26887    +equalize[0,1] n[-2,-1] 0,255
26888    +displacement[3] [2],$3,$4 +displacement[2] [3],$3,$4 rm[-4,-3]
26889    repeat $2+2 if $<
26890      title="Frame ""#"$pframe" -> ""#"$frame" ("$>/$2")        "
26891      e[] "\r  - "$title
26892      t={$>/($2+1)} omt={1-$t}
26893      +*[2] $t +warp[0] .,1,1,1 rm.. *. $omt
26894      +*[3] {1-$t} +warp[1] .,1,1,1 rm.. *. $t
26895      +[-2,-1] c. $cutvals
26896      if narg("$8")
26897        if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$8",25,mp4v,1 else filename "$8",$i i+=1 o. ${} fi
26898      fi
26899      if {*}
26900        w. -1,-1,0,$title
26901        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size.
26902        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size.
26903        if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi             # Reset window size.
26904      fi
26905      rm.
26906    fi done
26907    rm[0,-2,-1] pframe=$frame frame+=$7
26908  while $go_on" && "($6==-1" || "$frame<=$6)
26909
26910  # Output last frame.
26911  if narg("$8")
26912    if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o[] "$8",25,mp4v,0 else filename "$8",$i o. ${} fi
26913  fi
26914  rm
26915
26916#@cli register_nonrigid : [destination],_smoothness>=0,_precision>0,_nb_scale>=0
26917#@cli : Register selected source images with specified destination image, using non-rigid warp.
26918#@cli : Default values: 'smoothness=0.2', 'precision=6' and 'nb_scale=0(auto)'.
26919#@cli : $ image.jpg +rotate 20,1,1,50%,50% +register_nonrigid[0] [1]
26920register_nonrigid : check ${is_image_arg\ $1}" && ${2=0.2}>=0 && ${3=5}>0 && ${4=0}>=0"
26921  e[^-1] "Register source image$? with destination image $1, using non-rigid warp with smoothness $2,
26922          precision $3 and $4 scale(s)."
26923  pass$1 0 equalize. n. 0,255
26924  repeat $!-1
26925    +equalize[$>] n. 0,255 +displacement.. .,$2,$3,$4 rm..
26926    warp[$>] .,1,1,1 rm.
26927  done rm.
26928
26929#@cli register_rigid : [destination],_smoothness>=0,\
26930# _boundary_conditions={ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }
26931#@cli : Register selected source images with specified destination image, using rigid warp (shift).
26932#@cli : Default values: 'smoothness=0.1%' and 'boundary_conditions=0'.
26933#@cli : $ image.jpg +shift 30,20 +register_rigid[0] [1]
26934register_rigid : check ${is_image_arg\ $1}" && ${2=0.1%}>=0 && isint(${3=0}) && $3>=0 && $3<=3"
26935  e[^-1] "Register source image$? with destination image $1, using rigid warp with smoothness $2."
26936  m "_register_rigid : b $2 equalize 256 n 0,1"
26937  pass$1 0 W,H,D,S={[w,h,d,s]}
26938  f={max(w,h)/1024}
26939  if $f<=1 # Small image: one-step registration
26940    _register_rigid.
26941    repeat $!-1
26942      if {$>,w!=$W||h!=$H||d!=$D}
26943        error[0--4] "Images have incompatible sizes ("{$>,[w,h,d,s]}") and ("{[$W,$H,$D,$S]}")."
26944      fi
26945      +_register_rigid[$>] phase_correlation. .. shift[$>] {^},0,$3 rm.
26946    done
26947    rm.
26948  else # Large image: two-steps registration
26949    +r. {[min(w,1024),min(h,1024),min(d,1024)]},100%,0,0,0.5,0.5
26950    rr2d.. 1024,1024,0,2
26951    _register_rigid[-2,-1]
26952    repeat $!-2
26953      if {$>,w!=$W||h!=$H||d!=$D}
26954        error[0--4] "Images have incompatible sizes ("{$>,[w,h,d,s]}") and ("{[$W,$H,$D,$S]}")."
26955      fi
26956
26957      # Low scale.
26958      +rr2d[$>] 1024,1024,0,2 _register_rigid.
26959      phase_correlation. ...
26960      s={$f*crop()} rm.
26961
26962      # High scale.
26963      +shift[$>] $s,0,$3
26964      r. {[min(w,1024),min(h,1024),min(d,1024)]},100%,0,0,0.5,0.5 _register_rigid.
26965      phase_correlation. ..
26966      s={[$s]+crop()} rm.
26967
26968      shift[$>] $s,0,$3
26969    done
26970    rm[-2,-1]
26971  fi
26972  um _register_rigid
26973
26974#@cli transition : [transition_shape],nb_added_frames>=0,100>=shading>=0,_single_frame_only={ -1=disabled | >=0 }
26975#@cli : Generate a transition sequence between selected images.
26976#@cli : Default values: 'shading=0' and 'single_frame_only=-1'.
26977#@cli : $ image.jpg +mirror c 100%,100% plasma[-1] 1,1,6 transition[0,1] [2],5
26978transition : check ${is_image_arg\ $1}" && $2>=0 && ${3=0}>=0 && $3<=100" skip ${4=-1}
26979  frame={round($4)} s0=" and shading $3" s1=", shading $3 and single-frame-only "$frame
26980  e[^-1] "Create transition sequence between image$? with $2 added frames, transition shape $1"${s{$4>0}}"."
26981  if $!<2" || "!$2 return fi
26982  to_colormode 0 r ${-max_whd},100%,0,0,0.5,0.5
26983  pass$1 0 norm. r. [0],[0],[0],1,3 n. 0,1 mv. 0
26984  repeat $!-2 l[0,{$<+1},{$<+2}]
26985    nm0={1,n}
26986    if $3 repeat $2 if $4<0" || "$>==$frame
26987      val0={($>+0.5)/$2-$3%}
26988      val1={($>+0.5)/$2+$3%}
26989      +f[0] '(i-$val0)/($val1-$val0)' c. 0,1
26990      +j[2] [1],0,0,0,0,1,. rm..
26991      nm. $nm0\ ""#{1+$>}
26992    fi done else repeat $2 if $4<0" || "$>==$frame
26993      +>=[0] {($>+0.5)/$2}
26994      +j[2] [1],0,0,0,0,1,. rm..
26995      nm. $nm0\ ""#{1+$>}
26996    fi done fi
26997    mv[2] $!
26998  endl done rm[0]
26999
27000#@cli transition3d : _nb_frames>=2,_nb_xtiles>0,_nb_ytiles>0,_axis_x,_axis_y,_axis_z,_is_antialias={ 0 | 1 }
27001#@cli : Create 3D transition sequence between selected consecutive images.
27002#@cli : 'axis_x', 'axis_y' and 'axis_z' can be set as mathematical expressions, depending on 'x' and 'y'.
27003#@cli : Default values: 'nb_frames=10', 'nb_xtiles=nb_ytiles=3', 'axis_x=1', 'axis_y=1', 'axis_z=0' \
27004# and 'is_antialias=1'.
27005#@cli : $ image.jpg +blur 5 transition3d 9 display_rgba
27006transition3d : check "isint(${1=10}) && $1>=2 && isint(${2=3}) && $2>0 && isint(${3=$2}) && $3>0"
27007               skip ${4=1},${5=1},${6=0},${7=1}
27008  e[^-1] "Create 3D transition sequence between image$?, with $1 frames, $2x$3 tiles and rotation axis ($4,$5,$6).\n"
27009  if $!<2 return fi
27010  slices 0 to_rgb r ${-max_whds},3
27011  off=0 repeat $!-1 l[{$>+$off},{$>+$off+1}]
27012    e[] "\r > Generate transition from image "$>" to image "{$>+1}".    "
27013
27014    # Create 3D rotation vectors.
27015    $2,$3,1,1,'$4'
27016    $2,$3,1,1,'$5'
27017    $2,$3,1,1,'$6'
27018    a[-3--1] z
27019    permute. zxyc r. 3,{$2*$3},1,1,-1
27020    repeat h rot$>={@0-2} shift. 0,-1,0,0 done
27021    rm.
27022
27023    # Create 3D tiles.
27024    +split_tiles[-2,-1] $2,$3 mv[0,1] $!
27025    N={$2*$3} i=0 y=0
27026    repeat $3
27027      x=0
27028      repeat $2
27029        lw={$i,w} lh={$i,h}
27030        imageplane3d[$i] imageplane3d[$N] r3d[$N] ${rot$i},180 c3d[$i,$N] +3d[$i,$N]
27031        x$i=$x y$i=$y x+=$lw i+=1
27032      done
27033      y+=$lh
27034    done
27035
27036    # Generate intermediate animation frames.
27037    repeat $1-2
27038      repeat $N r3d[$>] ${rot$>},{180/(1-$1)} ++3d[$>] ${x$>},${y$>},0 done
27039      +3d[-$N--1] c3d.
27040
27041      if $7 # Antialiased rendering.
27042        i... {-2,2*[w,h]},1,3,-1
27043        *3d. 2 j3d... .,50%,50%,0,1,2,0,0 rm.
27044        to_rgba.. replace_color.. 0,0,-1,-1,-1,255,0,0,0,0
27045        downsize_aliased.. 50
27046      else # Standard rendering.
27047        i... {-2,w},{-2,h},1,3,-1 j3d... .,50%,50%,0,1,2,0,0 rm.
27048        to_rgba.. replace_color.. 0,0,-1,-1,-1,255,0,0,0,0
27049      fi
27050
27051    done
27052    rm[0-{$N-1}]
27053    nm[1--2] {0,n}
27054    off+={$1-2}
27055  endl done
27056
27057#@cli video2files : input_filename,_output_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1
27058#@cli : Split specified input video file into image files, one for each frame.
27059#@cli : First and last frames as well as step between frames can be specified.
27060#@cli : Default values: 'output_filename=frame.png', 'first_frame=0', 'last_frame=-1' and 'frame_step=1'.
27061video2files : check "isint(${3=0}) && $3>=0 && isint(${4=-1}) && ($4>=0 || $4==-1) && isint(${5=1}) && $5>=1"
27062              skip ${2="frame.png"}
27063  e[^-1] "Split input video file '$1' into image frames '$2', with first frame $3, last frame $4, and frame step $5.\n"
27064  frame=$3 stopflag=0
27065  do l[]
27066    i "$1",$frame
27067    if $!
27068      o ${"filename \"$2\","$frame} rm
27069      e[] "\r  > Frame ""#"$frame
27070      frame+=$5
27071    else stopflag=1
27072    fi
27073  onfail stopflag=1
27074  endl while !$stopflag" && "($frame<=$4" || "$4==-1)
27075
27076#------------------------------
27077#
27078#@cli :: Convenience Functions
27079#
27080#------------------------------
27081
27082#@cli alert : _title,_message,_label_button1,_label_button2,...
27083#@cli : Display an alert box and wait for user's choice.
27084#@cli : If a single image is in the selection, it is used as an icon for the alert box.
27085#@cli : Default values: 'title=[G'MIC Alert]' and 'message=This is an alert box.'.
27086alert : skip "${1=[G"{`39`}"MIC Alert]},${2=This is an alert box.},${3=OK}"
27087  if $!==1
27088    e[0--3] "Display alert box, with image$?, title '$1', message '$2' and buttons '${3--1}'."
27089  else
27090    e[0--3] "Display alert box, with title '$1', message '$2' and buttons '${3--1}'."
27091  fi
27092
27093  if $!==1 logo= else logo=[] fi
27094  +l$logo
27095
27096    # Manage alert icon.
27097    if $!==1 to_rgb
27098    else # No logo provided, generate default logo (alert).
27099      64,64 polygon 3,50%,10%,10%,90%,90%,90%,1,1 b 3 >= 50%
27100      +erode. 5 -. .. ==. 0
27101      polygon. 4,47%,43%,53%,43%,53%,66%,47%,66%,1,0 circle. 50%,76%,2,1,0
27102      +*[0] 255 . 100%,100% a[-3--1] c -. '3*(y-h/2)'
27103      *. .. rm.. *[0] 255 rv a c
27104      drop_shadow 3,3,1 i[0] 100%,100%,1,3,200 blend alpha
27105    fi
27106    channels -1,2
27107
27108    # Create buttons graphics.
27109    $=arg
27110    repeat $#-2 label=${arg{$>+3}} 0 t. $label,0,0,16,1,-200 done
27111    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
27112    +[^0] 200 to_rgb[^0]
27113    mv[0] $!
27114    [0],[0],1,1,'(y-h/2)' *. -2 c. -30,30 +[0--3] . rm. c[^-1] 0,255  # Add shading to buttons.
27115
27116    repeat $!-1 l[$<]
27117
27118      # Create selected buttons.
27119      +rectangle 0,0,100%,100%,1,0xFFFFFFFF,0
27120      rectangle. 1,1,{w-2},{h-2},1,0xFFFFFFFF,0
27121      line. 2,{h-3},{w-3},{h-3},1,150 line. {w-3},{h-3},{w-3},2,1,150
27122      line. 1,1,{w-3},1,1,255 line. 1,1,1,{h-3},1,255
27123      rectangle. 4,4,{w-5},{h-5},1,0xAAAAAAAA,0
27124
27125      # Create clicked buttons.
27126      +shift.. 1,1,0,0,2
27127      rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0
27128      rectangle. 1,1,{w-2},{h-2},1,0xFFFFFFFF,150
27129      rectangle. 4,4,{w-5},{h-5},1,0xAAAAAAAA,0
27130
27131      # Create default aspect.
27132      rectangle... 0,0,100%,100%,1,0xFFFFFFFF,0
27133      line... 1,{h-2},{w-2},{h-2},1,150 line... {w-2},{h-2},{w-2},1,1,150
27134      line... 0,0,{w-2},0,1,255 line... 0,0,0,{h-2},1,255
27135
27136      # Create coordinates image.
27137      i[0] 100%,100% =[0] 1,0,0
27138
27139    a c endl done
27140
27141
27142    # Render alert box graphics.
27143    +l
27144      channels 0,3 sh 1,100% -[50%--1] 200 rm[50%--1] frame 8,8,0
27145      if $!<6 a[^-1] x else append_tiles[^-1] , fi
27146      0 t. "$2",0,0,16,1,0,-200,-200,-200 r. {w+16},{h+8},1,4,0
27147      a[-2,-1] x,0.5 rv a y,0.5
27148      sh 1,100% +. 200 rm.
27149      rectangle 0,0,100%,100%,1,0xFFFFFFFF,0
27150      line 0,0,{w-2},0,1,0,255,255,255 line 0,0,0,{h-2},1,0,255,255,255
27151    endl
27152    rm..
27153    +channels. 0
27154
27155    # Retrieve (x,y) coordinates of the buttons and fill active area.
27156    (0,{w-1}) (0;{-2,h-1}) ri[-2,-1] ...,3 a[-2,-1] c round. rv[-2,-1] *[-2,-1]
27157    discard. 0 y. r. {h/2},2,1,1,-1
27158    channels.. 1,3 rv[-2,-1]
27159    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
27160    a[-2,-1] c
27161
27162    # Enter event loop.
27163    repeat 9 if !{*$>} disp=$> break fi done   # Find available display window.
27164    if !narg($disp) error[0--4] "Command '$0': Cannot open display window for alert box." fi
27165
27166    selected={if($#==3,0,-1)} clicked=-1
27167    do
27168
27169      # Render current view.
27170      +channels. 0,2
27171      if $clicked>=0
27172        x0={-3,i($clicked,0)} y0={-3,i($clicked,1)}
27173        sh[$clicked] 7,9
27174        j.. .,$x0,$y0 rm.
27175      elif $selected>=0
27176        x0={-3,i($selected,0)} y0={-3,i($selected,1)}
27177        sh[$selected] 4,6
27178        j.. .,$x0,$y0 rm.
27179      fi
27180      w$disp. 100%,100%,0,"$1" rm. wait
27181
27182      # Handle user interactions.
27183      xm={*$disp,x} ym={*$disp,y} bm={{*$disp,b}&1} val={i($xm,$ym,0,3)}
27184      if $bm" && "$val clicked={$val-1}
27185      elif $bm" && "!$val" && "$clicked>=0 selected=$clicked clicked=-1
27186      elif !$bm" && "$clicked>=0" && "$clicked==$val-1 break
27187      fi
27188      if {*$disp,ARROWRIGHT} selected={($selected+1)%{-2,w}} wait -1
27189      elif {*$disp,ARROWLEFT} selected={($selected-1)%{-2,w}+($selected==-1)} wait -1
27190      elif $selected>=0" && "{*$disp,ENTER} clicked=$selected break
27191      fi
27192
27193    while {*$disp}" && "!{*$disp,ESC}
27194
27195    # Return result (index of clicked button or '-1').
27196    w$disp 0 rm u $clicked
27197  endl
27198
27199#@cli arg : n>=1,_arg1,...,_argN
27200#@cli : Return the n-th argument of the specified argument list.
27201arg : check "isint($1) && ($1)>0"
27202  $=arg u ${arg{1+($1)}}
27203
27204#@cli arg0 : n>=0,_arg0,...,_argN
27205#@cli : Return the n-th argument of the specified argument list (where 'n' starts from '0').
27206arg0 : check "isint($1) && ($1)>=0"
27207  $=arg u ${arg{2+($1)}}
27208
27209#@cli arg2img : argument_1,...,argument_N
27210#@cli : Split specified list of arguments and return each as a new image (as a null-terminated string).
27211arg2img :
27212  $=arg repeat $# ({'${arg{1+$>}}'},0) done
27213
27214#@cli arg2var : variable_name,argument_1,...,argument_N
27215#@cli : For each i in [1...N], set 'variable_name$i=argument_i'.
27216#@cli : The variable name should be global to make this command useful (i.e. starts by an underscore).
27217arg2var :
27218  $=arg u {$#-1} repeat ${} $1{1+$>}=${arg{2+$>}} done
27219
27220#@cli autocrop_coords : value1,value2,... | auto
27221#@cli : Return coordinates (x0,y0,z0,x1,y1,z1) of the autocrop that could be performed on the latest
27222#@cli : of the selected images.
27223#@cli : Default value: 'auto'
27224autocrop_coords : skip ${1=auto}
27225  is_auto={['"$1"']=='auto'}
27226  w={w} h={h} d={d}
27227  value={i(w-1,h-1,d-1)} +=. {1+$value},100%,100%,100% _autocrop$is_auto. ${1--1} =. $value,100%,100%,100%
27228  x0={$w-w} y0={$h-h} z0={$d-d} rm.
27229  +_autocrop$is_auto. ${1--1}
27230  x1={$x0+w-1} y1={$y0+h-1} z1={$z0+d-1} rm.
27231  u $x0,$y0,$z0,$x1,$y1,$z1
27232
27233_autocrop0 : autocrop $*
27234_autocrop1 : skip $* autocrop
27235
27236#@cli average_colors
27237#@cli : Return the average vector-value of the latest of the selected images.
27238average_colors :
27239  res=""
27240  repeat s-1 sh. {1+$>} res=$res,{ia} rm. done
27241  sh. 0 u {ia}$res rm.
27242
27243#@cli base642img : "base64_string"
27244#@cli : Decode given base64-encoded string as a newly inserted image at the end of the list.
27245#@cli : The argument string must have been generated using command 'img2base64'.
27246base642img :
27247  base642uchar "$1" unserialize.
27248
27249#@cli base642uchar : "base64_string"
27250#@cli : Decode given base64-encoded string as a newly inserted 1-column image at the end of the list.
27251#@cli : The argument string must have been generated using command 'uchar2base64'.
27252base642uchar :
27253  0
27254  eval "
27255    ref(vector256(),hash);
27256    for (k = _'A', k<=_'Z', ++k, hash[k] = k - _'A');
27257    for (k = _'a', k<=_'z', ++k, hash[k] = k - _'a' + 26);
27258    for (k = _'0', k<=_'9', ++k, hash[k] = k - _'0' + 52);
27259    hash[_'+'] = hash[_'-'] = 62;
27260    hash[_'/'] = hash[_'_'] = 63;
27261    s = ['$1'];
27262    const ss = size(s);
27263    ss>=2?(
27264      resize(#-1,1,ss*3/4 - (s[ss-1]==_'=') - (s[ss-2]==_'='),1,1);
27265      od = 0;
27266      for (os = 0, os<size(s),
27267        c1 = hash[s[os++]];
27268        c2 = hash[s[os++]];
27269        c3 = hash[s[os++]];
27270        c4 = hash[s[os++]];
27271        i[#-1,od++] = (c1<<2) | (c2>>4);
27272        i[#-1,od++] = ((c2&15)<<4) | (c3>>2);
27273        i[#-1,od++] = ((c3&3)<<6) | c4;
27274      )
27275    )"
27276  nm. "[unnamed]"
27277
27278#@cli basename : file_path,_variable_name_for_folder
27279#@cli : Return the basename of a file path, and opt. its folder location.
27280#@cli : When specified 'variable_name_for_folder' must starts by an underscore
27281#@cli : (global variable accessible from calling function).
27282basename : skip ${2=unused}
27283  l[] ({"'$1'"}) replace 92,47 s +,47
27284  if i==47 a y $2={t} u ""
27285  elif $!==1 u {t} $2=""
27286  else a[^-1] y u {t} $2={-2,t}
27287  fi
27288  rm endl
27289
27290#@cli bin : binary_int1,...
27291#@cli : Print specified binary integers into their octal, decimal, hexadecimal and string representations.
27292bin :
27293  dec=${bin2dec\ ${^0}}
27294  e[^-1] "Convert binary integer"${arg\ 1+($#>1),"",s}" '${^0}' to octal '"${dec2oct\ $dec}"',
27295          decimal '"$dec"', hexadecimal '"${dec2hex\ $dec}"' and string '"${dec2str\ $dec}"'."
27296
27297#@cli bin2dec : binary_int1,...
27298#@cli : Convert specified binary integers into their decimal representations.
27299bin2dec :
27300  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
27301
27302_bin2dec :
27303  u {"str = vtos(abs($1));
27304      for (k = val = 0, str[k], ++k,
27305        c = str[k];
27306        (val<<=1)+=(c==_'0'?0:c==_'1'?1:nan);
27307        isnan(val)?break()
27308      ); sign($1)*val"}
27309
27310# Command to check what lines of G'MIC sources are larger than 120 columns.
27311_check120 :
27312  use_vt100
27313  it[] "$1" s +,10
27314  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]"
27315  lines={{@-1}-1} f. "I = I; if(I[0]<0,I[1]=-1); I"
27316  discard. -1
27317  if w
27318    r. 1,{h/2},1,2,-1
27319    repeat h
27320      l,L={I[$>]}
27321      e[] $_vt100_c"  - [Line "$_vt100_b$L$_vt100_n$_vt100_c", "\
27322              $_vt100_b{$l,h}$_vt100_n$_vt100_c" chars]: "$_vt100_n{$l,t}
27323    done
27324  fi
27325  rm
27326  _total_lines+=$lines
27327  e[] "  - Scanned : "${lines}" lines"
27328
27329check120 :
27330  _total_lines=0
27331  files 0,*.h c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi
27332  files 0,*.cpp c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi
27333  files 0,*.c c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi
27334  files 0,*.gmic c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi
27335  repeat narg($files)
27336    arg 1+$>,$files file=${}
27337    e[] " * File '"$file"'."
27338    v + _check120 $file v -
27339  done
27340  e[] " - Total scanned : "${_total_lines}" lines"
27341
27342#@cli covariance_colors : _avg_outvarname
27343#@cli : Return the covariance matrix of the vector-valued colors in the latest of the selected images
27344#@cli : (for arbitrary number of channels).
27345#@cli : Parameter 'avg_outvarname' is used as a variable name that takes the value of the average vector-value.
27346covariance_colors : skip "${1=avg}"
27347  $1=${-average_colors}
27348  f ">begin( avg = [ "$""$1" ]; const S2 = s^2; C = vectorS2(0); );
27349      mI = I - avg; C+=mul(mI,mI,s);
27350      end( C/=whd - 1; run('u ',vtos(C)) );
27351      I"
27352
27353#@cli dec : decimal_int1,...
27354#@cli : Print specified decimal integers into their binary, octal, hexadecimal and string representations.
27355dec :
27356  e[^-1] "Convert decimal integer"${arg\ 1+($#>1),"",s}" '${^0}' to binary '"${dec2bin\ ${^0}}"',"\
27357          " octal '"${dec2oct\ ${^0}}"', hexadecimal '"${dec2hex\ ${^0}}"' and string '"${dec2str\ ${^0}}"'."
27358
27359#@cli dec2str : decimal_int1,...
27360#@cli : Convert specifial decimal integers into its string representation.
27361dec2str :
27362  u {`[${^0}]`}
27363
27364#@cli dec2bin : decimal_int1,...
27365#@cli : Convert specified decimal integers into their binary representations.
27366dec2bin :
27367  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
27368
27369_dec2bin :
27370  u {`"const sgn = sign($1);
27371       const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1))))) + (sgn>=0?0:1);
27372       res = vectorN();
27373       sgn>=0?0:(res[0] = _'-');
27374       for (val = abs($1); k = size(res) - 1, k>=(sgn<0?1:0), --k, res[k] = _'0' + (val&1); val>>=1); res"`}
27375
27376#@cli dec2hex : decimal_int1,...
27377#@cli : Convert specified decimal integers into their hexadecimal representations.
27378dec2hex :
27379  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
27380
27381_dec2hex :
27382  u {`"begin(tab = [ _'0',_'1',_'2',_'3',_'4',_'5',_'6',_'7',_'8',_'9',_'a',_'b',_'c',_'d',_'e',_'f' ]);
27383       const sgn = sign($1);
27384       const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1)))/4)) + (sgn>=0?0:1);
27385       res = vectorN();
27386       sgn>=0?0:(res[0] = _'-');
27387       for (val = abs($1); k = size(res) - 1, k>=(sgn<0?1:0), --k, res[k] = tab[val&15]; val>>=4); res"`}
27388
27389#@cli dec2oct : decimal_int1,...
27390#@cli : Convert specified decimal integers into their octal representations.
27391dec2oct :
27392  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
27393
27394_dec2oct :
27395  u {`"const sgn = sign($1);
27396       const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1)))/3)) + (sgn>=0?0:1);
27397       res = vectorN();
27398       sgn>=0?0:(res[0] = _'-');
27399       for (val = abs($1); k = size(res) - 1, k>=(sgn<0?1:0), --k, res[k] = _'0' + (val&7); val>>=3); res"`}
27400
27401#@cli fact : value
27402#@cli : Return the factorial of the specified value.
27403fact : check isint($1)
27404  res=1 repeat $1 res*={($>+1)} done u $res
27405
27406#@cli fibonacci : N>=0
27407#@cli : Return the Nth number of the Fibonacci sequence.
27408#@cli : $ echo ${"fibonacci 10"}
27409#@cli : \n~~~\n[gmic]-0./ Start G'MIC interpreter.\n[gmic]-0./ 55\n[gmic]-0./ End G'MIC interpreter.\n~~~\n
27410fibonacci : check "$1>=0"
27411  u {N=$1;if(N<2,N,for(n=N;F0=0;F1=1,n=n-1,F2=F0+F1;F0=F1;F1=F2))}
27412
27413#@cli file_mv : filename_src,filename_dest
27414#@cli : Rename or move a file from a location $1 to another location $2.
27415file_mv :
27416  e[^-1] "Move file '$1' to location '$2'."
27417  if ${-is_windows} x "move "$1" "$2 else x "mv "$1" "$2 fi
27418
27419#@cli file_rand
27420#@cli : Return a random filename for storing temporary data.
27421file_rand :
27422  do filename=${-path_tmp}gmic$_pid{`round(u(vector6(_'0'),vector6(_'9')))`}
27423  while isfile(['{/$filename}'])
27424  u $filename
27425
27426#@cli filename : filename,_number1,_number2,...,_numberN
27427#@cli : Return a filename numbered with specified indices.
27428filename : skip "${1=default}"
27429  if $#==1 u "$1"
27430  else
27431  (${2--1}) nm. "$1" u {f}{b}
27432  repeat w
27433    u ${}_{int(i/100000)%10}{int(i/10000)%10}{int(i/1000)%10}{int(i/100)%10}{int(i/10)%10}{i%10}
27434    shift. -1
27435  done
27436  if narg({'{x}'}) u ${}.{x} fi
27437  rm.
27438  fi
27439
27440#@cli files : _mode,path : (+)
27441#@cli : Return the list of files and/or subfolders from specified path.
27442#@cli : 'path' can be eventually a matching pattern.
27443#@cli : 'mode' can be { 0=files only | 1=folders only | 2=files + folders }.
27444#@cli : Add '3' to 'mode' to return full paths instead of filenames only.
27445#@cli : Default value: 'mode=5'.
27446
27447#@cli files2img : _mode,path
27448#@cli : Insert a new image where each vector-valued pixel is a string encoding the filenames returned by \
27449# command ''files''.
27450#@cli : Useful to manage list of filenames containing characters that have a special meaning in the G'MIC language,\
27451# such as spaces or commas.
27452+files2img :
27453  if ['$$_$0_f2i']==0 m "_$0_f2i: $""=arg repeat $""# ({'${arg{1+$>}}'}:^) done a y" fi
27454  files $"*" l[] _$0_f2i[] ${} if !$! 0 fi endl
27455
27456#@cli fitratio_wh : min_width,min_height,ratio_wh
27457#@cli : Return a 2D size 'width,height' which is bigger than 'min_width,min_height' and has the specified w/h ratio.
27458fitratio_wh :
27459  if $3*$2>$1 u {int($3*$2)},$2 else u $1,{int($1/$3)} fi
27460
27461#@cli fitscreen : width,height,_depth,_minimal_size[%],_maximal_size[%] : [image],_minimal_size[%],_maximal_size[%]
27462#@cli : Return the 'ideal' size WxH for a window intended to display an image of specified size on screen.
27463#@cli : Default values: 'depth=1', 'minimal_size=128' and 'maximal_size=85%'.
27464fitscreen : skip "${2=},${3=},${4=},${5=}"
27465  if ${"is_image_arg $1"}
27466    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
27467  else
27468    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
27469  fi
27470  eval "
27471    is_percent(str) = (unref(_is_pct); _is_pct=['#str']; _is_pct[size(_is_pct) - 1]==_'%');
27472    const u = "{*,u}";
27473    const v = "{*,v}";
27474    ms = round(is_percent("$m")?[ u,v ]*"$m":[ "$m,$m" ]);
27475    Ms = round(is_percent("$M")?[ u,v ]*"$M":[ "$M,$M" ]);
27476    s = [ "$W,$H" ];
27477    "$D">1?(s+="$D");
27478    s[0]<ms[0]?(s = [ ms[0],s[1]*ms[0]/s[0] ]);
27479    s[1]<ms[1]?(s = [ s[0]*ms[1]/s[1],ms[1] ]);
27480    s[0]>Ms[0]?(s = [ Ms[0],s[1]*Ms[0]/s[0] ]);
27481    s[1]>Ms[1]?(s = [ s[0]*Ms[1]/s[1],Ms[1] ]);
27482    s[0] = max(1,s[0],ms[0]);
27483    s[1] = max(1,s[1],ms[1]);
27484    round(s)"
27485
27486#@cli fontchart
27487#@cli : Insert G'MIC font chart at the end of the image list.
27488#@cli : $ fontchart
27489+fontchart :
27490  e[^-1] "Generate G'MIC font chart."
27491  l[]
27492    repeat 256
27493      if $>==92 char=\\ else char={`max(1,(c=$>;c>=23&&c<=28?32:c))`} fi
27494      0 t. {``$char},0,0,50,1,255
27495    done
27496    a z,0.5
27497    s z
27498    repeat $!
27499      t[$>] $>,1,-1,13,1,200
27500      0 t. \\${dec2oct\ $>},1,-1,13,1,1
27501      100%,100%,1,1,200 j[$>] .,{$>,[w,h]-[w#-1,h#-1]},0,0,1,.. rm[-2,-1]
27502    done
27503    frame 1,1,128 append_tiles ,
27504  endl
27505
27506# font2cimgh
27507# Encode a font image as a C-style string for CImg.h.
27508font2cimgh :
27509  e[^-1] "Encode font image$? as a C-style string for CImg.h."
27510  repeat $! l[$>] bnm={0,b}
27511    e[] "  > Encode font '"$bnm"'."
27512    W,H={[w/256,h]}
27513    if !isint($W) error[0--4] "Font image '"$bnm"' has wrong dimensions ("{[w,h,d,s]}")." fi
27514    +f "i==im || i==iM" is_binary={im==1} rm.
27515
27516    # Find best parameters for RLE compression.
27517    Mm=0 MM=100
27518    do
27519      M={floor(($Mm+$MM)/2)}
27520      +compress_rle $is_binary,$M rows. 6,100% +. {32-im} iM={iM}
27521      if iM<126 Mm=$M rm. elif iM>126 MM=$M rm. fi
27522    while $iM!=126
27523    k. nb_chunks={1+int(h/65536)}
27524
27525    e[] "\r  > Encode font '"$bnm"' -> W = "$W", H = "$H", M = "$M", is_binary = "$is_binary",
27526             nb_chunks = "$nb_chunks"."
27527
27528    # Generate C-style string for storing font data.
27529    replace_str "\\","\\\\"
27530    replace_str "\"","\\\""
27531    s y,-111
27532    repeat $!
27533      if {$>,i[h-1]==_'\\'&&i[h-2]!=_'\\'} rows[$>] 0,{$>,h-2} rows[{$>+1}] -1,100% =[{$>+1}] {'\\'} fi
27534      l[$>]
27535        i[0] ('"      \""') ('\"')
27536        if !$< ('" };"') fi
27537        ('\n')
27538        y a y
27539      endl
27540    done
27541    repeat $nb_chunks-1 ind={int($!*($>+1)/$nb_chunks)} l[$ind] = {','},0,100% ('\n') y a y endl done
27542    i[0] ('"    static const char *const data_font"${W}x${H}"[] = {"\n')
27543    y a y ot $bnm.h
27544
27545  endl done
27546
27547#@cli fps
27548#@cli : Return the number of time this function is called per second, or -1 if this info is not yet available.
27549#@cli : Useful to display the framerate when displaying animations.
27550fps :
27551  if narg($_fps_fps)
27552    dt={$|-$_fps_time}
27553    if $dt>1 _fps_fps={round($_fps_nbframes/$dt)} _fps_time=$| _fps_nbframes=0 fi
27554    u $_fps_fps
27555    _fps_nbframes+=1
27556  else _fps_nbframes=0 _fps_time=$| _fps_fps=-1 u -1
27557  fi
27558
27559#@cli gcd : a,b
27560#@cli : Return the GCD (greatest common divisor) between a and b.
27561gcd : check "isint($1) && isint($2) && $1*$2!=0"
27562  _gcd {max(abs($1),abs($2))},{min(abs($1),abs($2))}
27563
27564_gcd :
27565  r={$1%$2} if $r u ${_gcd\ $2,$r} else u $2 fi
27566
27567#@cli hex : hexadecimal_int1,...
27568#@cli : Print specified hexadecimal integers into their binary, octal, decimal and string representations.
27569hex :
27570  dec=${hex2dec\ ${^0}}
27571  e[^-1] "Convert hexadecimal integer"${arg\ 1+($#>1),s,""}" '${^0}' to binary '"${dec2bin\ $dec}"',
27572           octal '"${dec2oct\ $dec}"', decimal '"$dec"' and string '"${dec2str\ $dec}"'."
27573
27574#@cli hex2dec : hexadecimal_int1,...
27575#@cli : Convert specified hexadecimal integers into their decimal representations.
27576hex2dec :
27577  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
27578
27579_hex2dec :
27580  u {"str = ['$1']; str[0]==_'-'?-stov([['0x'],str[1,max(1,size(str)-1)]]):stov([['0x'],str])"}
27581
27582#@cli hex2img : "hexadecimal_string"
27583#@cli : Insert new image 1xN at the end of the list with values specified by the given hexadecimal-encoded string.
27584+hex2img :
27585  ('"$1"') 1,{w/2}
27586  f. "*
27587    from_char(x) = x>=48 && x<=57?x - 48:x-87;
27588    off = 2*y;
27589    from_char(i[#-2,off])*16 + from_char(i[#-2,off + 1])"
27590  rm..
27591
27592#@cli hex2str : hexadecimal_string
27593#@cli : Convert specified hexadecimal string into a string.
27594#@cli : See also: ''str2hex''.
27595hex2str : skip ${1=""}
27596  if !narg("$1") u "" return fi
27597  ('$*') if w<2 rm. u "" return fi
27598  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
27599  u {t} rm.
27600
27601#@cli img2base64 : _encoding={ 0=base64 | 1=base64url },_store_names={ 0 | 1 }
27602#@cli : Encode selected images as a base64-encoded string.
27603#@cli : The images can be then decoded using command 'base642img'.
27604#@cli : Default values: 'encoding=0'.
27605img2base64 : check "isbool(${1=0}) && isbool(${2=1})"
27606  if isnum("$1") encoding=$1 else encoding=0 noarg fi
27607  +serialize auto,1,$2 u ${uchar2base64\ $encoding} rm.
27608
27609#@cli img2hex
27610#@cli : Return representation of last image as an hexadecimal-encoded string.
27611#@cli : Input image must have values that are integers in [0,255].
27612img2hex :
27613  whds={w},{h},{d},{s} y. 2,{h}
27614  f.. "*
27615    to_char(x) = x>=0 && x<=9?48 + x:87 + x;
27616    i(#-1,0,y) = to_char(int(i/16));
27617    i(#-1,1,y) = to_char(i%16);
27618    i"
27619  u {t} rm. r. $whds,-1
27620
27621#@cli img2str
27622#@cli : Return the content of the latest of the selected images as a special G'MIC input string.
27623img2str :
27624  i[-2] 256
27625  eval. ">begin(off = 0);
27626    sep = x==w - 1?(y==h - 1?(z==d - 1?(c==s - 1?0:_'^'):_'/'):_';'):_',';
27627    it = vtos(i);
27628    off + size(it) + 1>=w(#-2)?resize(#-2,round(1.5*w(#-2)),1,1,1,0);
27629    for (k = 0, k<size(it) && it[k], ++k, i[#-2,off++] = it[k]);
27630    i[#-2,off++] = sep"
27631  u {-2,t} rm..
27632
27633#@cli img2text : _line_separator
27634#@cli : Return text contained in a multi-line image.
27635#@cli : Default value: 'line_separator= '.
27636img2text : skip "${1= }"
27637  +l s y s -,0 y x if $!>1 i[1--2] ('"$1"') fi a x u {0,t} rm endl
27638
27639#@cli img82hex
27640#@cli : Convert selected 8bits-valued vectors into their hexadecimal representations (ascii-encoded).
27641img82hex :
27642  e[^-1] "Convert 8bits-valued vector$? into hexadecimal representations (ascii-encoded)."
27643  % 256 y
27644  repeat $!
27645    +f[$>] 'v=int(i)&15;v+if(v<10,48,87)' # Lower digit
27646    f[$>] 'v=int(i)>>4;v+if(v<10,48,87)'  # Higher digit
27647    a[$>,-1] x
27648  done
27649
27650#@cli hex2img8
27651#@cli : Convert selected hexadecimal representations (ascii-encoded) into 8bits-valued vectors.
27652hex2img8 :
27653  e[^-1] "Convert hexadecimal representation$? (ascii-encoded) into 8bits-valued vectors."
27654  repeat $!
27655    s. x,2 f[-2,-1] 'if(i>=97,i-87,i-48)' *.. 16 +[-2,-1]
27656  mv. 0 done
27657
27658#@cli is_3d
27659#@cli : Return 1 if all of the selected images are 3D objects, 0 otherwise.
27660is_3d :
27661  u 1 l check3d 1 onfail u 0 endl
27662
27663# Faster version.
27664_is_3d :
27665  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"}
27666
27667#@cli is_change : _value={ 0=false | 1=true }
27668#@cli : Set or unset the 'is_change' flag associated to the image list.
27669#@cli : This flag tells the interpreter whether or not the image list should be displayed when the pipeline ends.
27670#@cli : Default value: 'value=1'.
27671is_change :
27672  l[] check "isbool(${1=1})" arg=$1 onfail noarg arg=1 endl
27673  if $arg 0 rm. else w9[] 0 fi
27674
27675#@cli is_half
27676#@cli : Return 1 if the type of image pixels is limited to half-float.
27677is_half :
27678  (2049) u {i==2048} rm.
27679
27680#@cli is_ext : filename,_extension
27681#@cli : Return 1 if specified filename has a given extensioin.
27682is_ext : skip "${2=}"
27683  0 nm. "_$1" u {"lowercase(['"{x}"'])==lowercase(['$2'])"} rm.
27684
27685#@cli is_image_arg : string
27686#@cli : Return 1 if specified string looks like '[ind]'.
27687is_image_arg : skip "${1=;}"
27688  u {"str = ['$1'];
27689      s1 = size(str) - 1;
27690      (str[0]==_'[' && str[s1]==_']') || (str[0]=='.' && str[s1]=='.') && min(str)>=45 && max(str)<=122"}
27691
27692#@cli is_pattern : string
27693#@cli : Return 1 if specified string looks like a drawing pattern '0x......'.
27694is_pattern : skip "${1=;}"
27695  u {"str = ['$1']; size(str)>2 && same(str,'0x',2)"}
27696
27697#@cli is_percent : string
27698#@cli : Return 1 if specified string ends with a '%', 0 otherwise.
27699is_percent :
27700  u {"s=['$1'];s[size(s)-1]==_'%'"}
27701
27702#@cli is_videofilename
27703#@cli : Return 1 if extension of specified filename is typical from video files.
27704is_videofilename : skip "${1=}"
27705  0 nm. "_$1" u {"
27706    ext = lowercase(['"{x}"']);
27707    ext=='avi' || ext=='mov' || ext=='asf' || ext=='divx' || ext=='flv' || ext=='mpg' ||
27708    ext=='m1v' || ext=='m2v' || ext=='m4v' || ext=='mjp' || ext=='mp4' || ext=='mkv' ||
27709    ext=='mpe' || ext=='movie' || ext=='ogm' || ext=='ogg' || ext=='ogv' || ext=='qt' || ext=='rm' ||
27710    ext=='vob' || ext=='wmv' || ext=='xvid' || ext=='mpeg'"}
27711  rm.
27712
27713#@cli is_macos
27714#@cli : Return 1 if current computer OS is Darwin (MacOS), 0 otherwise.
27715is_macos :
27716  if !narg($_is_macos) l[]
27717    if ${-is_windows} _is_macos=0
27718    else
27719      file=${-path_tmp}gmic_uname
27720      x "uname >"{/$file}
27721      it[] $file
27722      _is_macos={same(crop(),'Darwin',6,0)}
27723      rm
27724    fi
27725  onfail _is_macos=0 rm
27726  endl fi
27727  u $_is_macos
27728
27729#@cli is_windows
27730#@cli : Return 1 if current computer OS is Windows, 0 otherwise.
27731is_windows :
27732  if !narg($_is_windows) _is_windows={['$OS']!=0" && "['$WINDIR']!=0} fi
27733  u $_is_windows
27734
27735#@cli math_lib
27736#@cli : Return string that defines a set of several useful macros for the embedded math evaluator.
27737math_lib :
27738  u "
27739
27740  # Return number of elements in min-heap #ind.
27741  heap_size(ind) = i[#ind,whd(#ind)-1];
27742
27743  # Insert new element 'elt' into min-heap #ind.
27744  heap_insert(ind,elt) = (
27745    _heap_siz = heap_size(#ind);
27746    _heap_siz>=h(#ind) - 1?resize(#ind,1,_heap_siz*2 + 2,1,s#ind,0);
27747    hind = h(#ind);
27748    unref(_heap_elt);
27749    _heap_elt = [ elt ];
27750    _heap_eltsiz = max(1,size(_heap_elt));
27751
27752    copy(i[#ind,_heap_siz],_heap_elt[0],_heap_eltsiz,hind,1);
27753    for (_heap_pos = _heap_siz,
27754         _heap_pos && (_heap_par = int((_heap_pos + 1)/2) - 1; _heap_elt[0]<i[#ind,_heap_par]),
27755         _heap_pos = _heap_par,
27756      copy(i[#ind,_heap_pos],i[#ind,_heap_par],_heap_eltsiz,hind,hind);
27757      copy(i[#ind,_heap_par],_heap_elt,_heap_eltsiz,hind,1);
27758    );
27759    (heap_size(#ind)) = ++_heap_siz;
27760  );
27761
27762  # Remove root element from min-heap #ind.
27763  heap_remove(ind) = (
27764    _heap_siz = heap_size(#ind) - 1;
27765    hind = h(#ind);
27766    _heap_eltsiz = max(1,s#ind);
27767
27768    copy(i[#ind,0],i[#ind,_heap_siz],_heap_eltsiz,hind,hind);
27769    _heap_pos = 0;
27770    do (
27771      _heap_left = 2*_heap_pos + 1;
27772      _heap_right = _heap_left + 1;
27773      value = i[#ind,_heap_pos];
27774      _heap_right<_heap_siz && value>i[#ind,_heap_right]?(
27775        _heap_swap = i[#ind,_heap_left]<i[#ind,_heap_right]?_heap_left:_heap_right;
27776      ):_heap_left<_heap_siz && value>i[#ind,_heap_left]?(
27777        _heap_swap = _heap_left;
27778      ):break();
27779      copy(i[#ind,_heap_siz],i[#ind,_heap_swap],_heap_eltsiz,hind,hind); # Use previous last elt as temporary for swap.
27780      copy(i[#ind,_heap_swap],i[#ind,_heap_pos],_heap_eltsiz,hind,hind);
27781      copy(i[#ind,_heap_pos],i[#ind,_heap_siz],_heap_eltsiz,hind,hind);
27782      _heap_pos = _heap_swap;
27783    );
27784    (heap_size(#ind)) = _heap_siz;
27785  );
27786
27787  # Return number of elements in dynamic array #ind.
27788  dar_size(ind) = i[#ind,h(#ind) - 1];
27789
27790  # Return last element of a dynamic array
27791  dar_Back(ind) = I[#ind,dar_size(#ind) - 1];
27792  dar_back(ind) = i[#ind,dar_size(#ind) - 1];
27793
27794  # Inserts new element 'elt' into dynamic array #ind, at index [pos] ('pos' must be in [0,dar_size(#ind)]).
27795  dar_insert(ind,elt,pos) = (
27796    ref(pos,_dar_pos);
27797    ref(elt,_dar_elt);
27798    _dar_siz = dar_size(#ind);
27799    ref(max(1,s(#ind),size(_dar_elt)),_dar_sizelt);
27800    if (_dar_pos<=_dar_siz,
27801      _dar_siz>=h(#ind) - 1?resize(#ind,1,_dar_siz*2 + 2,1,_dar_sizelt,0);
27802      for (_dar_c = 0, _dar_c<_dar_sizelt, ++_dar_c,
27803        copy(i(#ind,_dar_pos + 1,0,0,_dar_c),i(#ind,_dar_pos,0,0,_dar_c),_dar_siz - _dar_pos)
27804      );
27805      copy(i[#ind,_dar_pos],_dar_elt,_dar_sizelt,h(#ind),1);
27806      i[#ind,h(#ind) - 1] = ++_dar_siz;
27807    );
27808  );
27809
27810  # Inserts new element 'elt' at the end of dynamic array #ind.
27811  dar_insert(ind,elt) = (
27812    ref(elt,_dar_elt);
27813    _dar_siz = dar_size(#ind);
27814    ref(max(1,s(#ind),size(_dar_elt)),_dar_sizelt);
27815    _dar_siz>=h(#ind) - 1?resize(#ind,1,_dar_siz*2 + 2,1,_dar_sizelt,0);
27816    copy(i[#ind,_dar_siz],_dar_elt,_dar_sizelt,h(#ind),1);
27817    i[#ind,h(#ind) - 1] = ++_dar_siz;
27818  );
27819
27820  # Remove element from dynamic array #ind, at index [pos] ('pos' must be in [0,dar_size(#ind) - 1]).
27821  dar_remove(ind,pos) = (
27822    ref(pos,_dar_pos);
27823    _dar_siz = dar_size(#ind);
27824    if (_dar_pos<_dar_siz,
27825      _dar_siz = --dar_size(#ind);
27826      for (_dar_c = 0, _dar_c<s(#ind), ++_dar_c,
27827        copy(i(#ind,_dar_pos,0,0,_dar_c),i(#ind,_dar_pos + 1,0,0,_dar_c),_dar_siz - _dar_pos)
27828      );
27829    )
27830  );
27831
27832  # Remove last element from dynamic array #ind.
27833  dar_remove(ind) = (
27834    _dar_siz = dar_size(#ind);
27835    _dar_siz>0?--dar_size(#ind);
27836  );
27837
27838  # Distance from point A to point B
27839  dist(A,B) = (
27840    norm(B - A);
27841  );
27842
27843  # Distance from point X to segment [A,B]
27844  dist(X,A,B) = (
27845    AB = B - A;
27846    P = A + dot(X - A,B - A)/max(1e-8,dot(AB,AB))*AB;
27847    dot(P - A,P - B)<=0?norm(P - X):min(norm(A - X),norm(B - X));
27848  );
27849
27850  # Distance from segment [A,B] to segment [C,D]
27851  dist(A,B,C,D) = (
27852    min(dist(A,C,D),dist(B,C,D),dist(C,A,B),dist(D,A,B));
27853  );
27854
27855  # Return the 'm' largest eigenvalues of a (large) square matrix A,
27856  # using the Arnoldi iteration method (https://en.wikipedia.org/wiki/Arnoldi_iteration).
27857  # A larger 'm' goes with better numerical precision.
27858  meig(A,m,n) = (
27859    unref(meig_m,meig_n);
27860    const meig_n = n;
27861    const meig_m = min(m,meig_n);
27862
27863    # Initialize data.
27864    ref(vector(#meig_m*meig_n),meig_V);
27865    ref(vector(#meig_m^2),meig_H);
27866    ref(expr('u(-1,1)',1,meig_n),meig_vj); meig_vj/=norm(meig_vj);
27867
27868    # Iteration loop.
27869    repeat (meig_m,meig_j,
27870      copy(meig_V[meig_j*meig_n],meig_vj,meig_n);
27871      meig_vj = A*meig_vj;
27872      for (meig_i = 0, meig_i<=meig_j, ++meig_i,
27873        ref(meig_V[meig_i*meig_n,meig_n],meig_vi);
27874        meig_d = dot(meig_vj,meig_vi);
27875        meig_H[meig_j*meig_m + meig_i] = meig_d;
27876        meig_vj-=meig_d*meig_vi;
27877      );
27878      meig_nvj = norm(meig_vj);
27879      meig_H[meig_j*meig_m + meig_j + 1] = meig_nvj;
27880      meig_nvj<1e-12?break();
27881      meig_vj/=meig_nvj;
27882    );
27883    eig(meig_H)[0,m];
27884  );
27885
27886  # Dichotomic search : Find x in [xmin,xmax] s.a f(x) = target, with a epsilon-precision
27887  search_dichotomic(fn_x,target,epsilon,xmin,xmax) = (
27888    # fn_x must be a strictly monotonic function !
27889    # if 'xmin = xmax = nan', range of 'x' is auto-detected.
27890    _dicho_fn(x) = _dicho_sgn*(fn_x);
27891    _dicho_epsilon = epsilon;
27892    _dicho_m = xmin;
27893    _dicho_M = xmax;
27894    _dicho_sgn = 1;
27895    _dicho_autom = isnan(_dicho_m);
27896    _dicho_autoM = isnan(_dicho_M);
27897    if (_dicho_autom, _dicho_m = -1);
27898    if (_dicho_autoM, _dicho_M = 1);
27899    _dicho_sgn = _dicho_fn(_dicho_m)>_dicho_fn(_dicho_M)?-1:1;
27900    _dicho_res = nan;
27901    _dicho_target = _dicho_sgn*target;
27902    _dicho_nb_attempts = 30;
27903    _dicho_autom?do (
27904      _dicho_fm = _dicho_fn(_dicho_m);
27905      _dicho_fm<_dicho_target?break();
27906      _dicho_m*=2;
27907    _(while), --_dicho_nb_attempts);
27908    _dicho_nb_attempts?(
27909      _dicho_autoM?do (
27910        _dicho_fM = _dicho_fn(_dicho_M);
27911        _dicho_fM>_dicho_target?break();
27912        _dicho_M*=2;
27913      _(while), --_dicho_nb_attempts);
27914      _dicho_nb_attempts?(
27915        _dicho_nb_attempts = 100;
27916        do (
27917          _dicho_c = (_dicho_m + _dicho_M)/2;
27918          _dicho_fc = _dicho_fn(_dicho_c);
27919          abs(_dicho_fc - _dicho_target)<_dicho_epsilon?(_dicho_res = _dicho_c; break()):
27920          _dicho_fc<_dicho_target?(_dicho_m = _dicho_c):
27921          (_dicho_M = _dicho_c);
27922          _(while), --_dicho_nb_attempts
27923        );
27924      );
27925    );
27926    _dicho_res;
27927  );
27928  search_dichotomic(fn_x,target,epsilon) = search_dichotomic(fn_x,target,1e-3,nan,nan);
27929  search_dichotomic(fn_x,target) = search_dichotomic(fn_x,target,1e-3);
27930  search_dichotomic(fn_x) = search_dichotomic(fn_x,0);
27931
27932  arrow(ind,P0,P1,angle,length,opacity,color) = ( # Draw an arrow from P0 to P1 on image #ind
27933    unref(_da_color);
27934    _opacity = opacity;
27935    _da_color = color;
27936    _P0 = P0;
27937    _P1 = P1;
27938    _P0P1 = _P1;
27939    _P0P1-=_P0;
27940    if (length<0, _P0P1*=-length/100, _P0P1*=length/norm2(_P0P1));
27941    coords = [ _P0,_P1,_P1,_P1 - rot(angle°)*_P0P1,_P1,_P1 - rot(-angle°)*_P0P1 ];
27942    repeat (3,_k, polygon(#ind,2,coords[4*_k,2],coords[4*_k+2,2],_opacity,_da_color));
27943  );
27944
27945  spline(ind,P0,T0,P1,T1,opacity,color) = ( # Draw spline P0-P1 with tangents T0,T1 on image #ind
27946    unref(_ds_color);
27947    _P0 = P0;
27948    _P1 = P1;
27949    _opacity = opacity;
27950    _ds_color = resize(color,s#ind)*=abs(_opacity);
27951    _omopacity = 1 - max(_opacity,0);
27952    _C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ _P0,P1,T0,T1 ],2);
27953    _dt = _dtmin = 1/max(abs(_P1 - _P0));
27954    _P0 = inf;
27955    for (_t = 0, _t<=1, _t+=_dt,
27956      _P = round(mul([_t^3,_t^2,_t,1],_C,2));
27957      _dP = abs(mul([3*_t^2,2*_t,1,0],_C,2));
27958      _dt = min(_dtmin,0.75/max(_dP));
27959      if (_P0!=_P, I(#ind,_P) = _ds_color + _omopacity*I(#ind,_P));
27960      _P0 = _P;
27961    );
27962    nan;
27963  );
27964
27965  triangle(ind,P0,P1,P2,opacity,color0,color1,color2) = ( # Draw a shaded triangle P0-P1-P2 on image #ind
27966    unref(_dt_color);
27967    unref(_dt_color0);
27968    unref(_dt_color1);
27969    unref(_dt_color2);
27970    _opacity = opacity;
27971    _dt_color0 = resize(color0,s#ind);
27972    _dt_color1 = resize(color1,s#ind);
27973    _dt_color2 = resize(color2,s#ind);
27974    _A = round(P0);
27975    _B = round(P1);
27976    _C = round(P2);
27977    _xmin = max(0,min(_A[0],_B[0],_C[0]));
27978    _xmax = min(w#ind-1,max(_A[0],_B[0],_C[0]));
27979    _ymin = max(0,min(_A[1],_B[1],_C[1]));
27980    _ymax = min(h#ind-1,max(_A[1],_B[1],_C[1]));
27981    _M = transpose([_A,1,_B,1,_C,1],3);
27982    for (_y = _ymin, _y<_ymax, ++_y,
27983      for (_x = _xmin, _x<_xmax, ++_x,
27984        _L = round(solve(_M,[_x,_y,1]),1e-5);
27985        if (min(_L)>=0,
27986          _dt_color = _L[0]*_dt_color0 + _L[1]*_dt_color1 + _L[2]*_dt_color2;
27987          copy(i(#ind,_x,_y,0,0),_dt_color,size(_dt_color),whd#ind,1,_opacity);
27988        );
27989      );
27990    );
27991    nan;
27992  );
27993
27994  hsv2rgb(I) = ( # Convert HSV vector to RGB
27995    ref(I,_I);
27996    _I[0]%=360;
27997    _I[1] = cut(_I[1],0,1);
27998    _I[2] = cut(_I[2],0,1);
27999    _c = _I[2]*_I[1];
28000    _x = _c*(1-abs((_I[0]/60)%2-1));
28001    (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;
28002  );
28003
28004  rgb2hsv(I) = ( # Convert RGB vector to HSV
28005    ref(I,_I);
28006    _M = max(_I);
28007    _C = _M - min(_I);
28008    [ 60*(_C==0?0:_M==_I[0]?((_I[1] - _I[2])/_C)%6:_M==_I[1]?(_I[2] - _I[0])/_C + 2:(_I[0] - _I[1])/_C + 4),
28009      _M<=0?0:_C/_M, _M/255 ];
28010  );
28011
28012  is_intriangle(P,A,B,C) = ( # Test if a point is inside a triangle
28013    _v0 = C - A;
28014    _v1 = B - A;
28015    _v2 = P - A;
28016    _dot00 = dot(_v0,_v0);
28017    _dot01 = dot(_v0,_v1);
28018    _dot02 = dot(_v0,_v2);
28019    _dot11 = dot(_v1,_v1);
28020    _dot12 = dot(_v1,_v2);
28021    _invDenom = 1/(_dot00*_dot11 - _dot01*_dot01);
28022    _u = (_dot11*_dot02 - _dot01*_dot12)*_invDenom;
28023    _v = (_dot00*_dot12 - _dot01*_dot02)*_invDenom;
28024    _u>=0 && _v>=0 && _u + _v<1);
28025
28026  is_inquadrilateral(P,A,B,C,D) = ( # Test if a point is inside a quadrilateral
28027    is_intriangle(P,A,B,D) || is_intriangle(P,B,C,D);
28028  );
28029
28030  is_percent(str) = (unref(_is_pct); _is_pct=['#str']; _is_pct[size(_is_pct) - 1]==_'%');
28031
28032  length_spline(P0,T0,P1,T1) = ( # Compute the length of a spline P0-P1 with tangents T0,T1
28033    _P0 = P0;
28034    _P1 = P1;
28035    _C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ _P0,_P1,T0,T1 ],2);
28036    _l = norm(_P1 - _P0);
28037    if (_l,
28038      _nl = _l + 1;
28039      _dt = 1/_l;
28040      while (_nl - _l>=0.01,
28041        _l = _nl;
28042        _nl = 0;
28043        __P0 = _P0;
28044        for (_t = 0, _t<=1, _t+=_dt,
28045          __P = mul([_t^3,_t^2,_t,1],_C,2);
28046          _nl+=norm(__P - __P0);
28047          __P0 = __P;
28048        );
28049        _dt = 1/max(1,_nl);
28050      );
28051    );
28052    _l
28053  );
28054
28055  pexp(x) = ( # Good polynomial approximation of 'exp(-x^2)' for |x|<2
28056    _pexp_x = abs(x);
28057    _pexp_x<2?-0.110353*_pexp_x^4 + 0.683221*_pexp_x^3 -1.17282*_pexp_x^2 + 1:0
28058  );
28059
28060  proj(X,A,B) = ( # Projection of point X onto line (A,B)
28061    _AB = B - A;
28062    P = A + dot(X - A,_AB)/max(1e-8,dot(_AB,_AB))*_AB;
28063  );
28064
28065  # CIE DeltaE_2000 color difference function.
28066  # (code adapted from https://github.com/gfiumara/CIEDE2000).
28067  deltaE00(lab1,lab2) = (
28068    _deltaE00_deg2rad(deg) = (deg*pi/180);
28069    unref(_deltaE00_lab1); _deltaE00_lab1 = lab1;
28070    unref(_deltaE00_lab2); _deltaE00_lab2 = lab2;
28071    unref(_deltaE00_k_L); const _deltaE00_k_L = 1;
28072    unref(_deltaE00_k_C); const _deltaE00_k_C = 1;
28073    unref(_deltaE00_k_H); const _deltaE00_k_H = 1;
28074    unref(_deltaE00_deg360inrad); const _deltaE00_deg360inrad = _deltaE00_deg2rad(360);
28075    unref(_deltaE00_deg180inrad); const _deltaE00_deg180inrad = _deltaE00_deg2rad(180);
28076    unref(_deltaE00_pow25to7); const _deltaE00_pow25to7 = 25^7;
28077
28078    # Step 1
28079    _deltaE00_C1 = norm(_deltaE00_lab1[1,2]);
28080    _deltaE00_C2 = norm(_deltaE00_lab2[1,2]);
28081    _deltaE00_barC = (_deltaE00_C1 + _deltaE00_C2)/2;
28082    _deltaE00_G = 0.5*(1 - sqrt(_deltaE00_barC^7/(_deltaE00_barC^7 + _deltaE00_pow25to7)));
28083    _deltaE00_a1prime = (1 + _deltaE00_G)*_deltaE00_lab1[1];
28084    _deltaE00_a2prime = (1 + _deltaE00_G)*_deltaE00_lab2[1];
28085    _deltaE00_Cprime1 = norm(_deltaE00_a1prime,_deltaE00_lab1[2]);
28086    _deltaE00_Cprime2 = norm(_deltaE00_a2prime,_deltaE00_lab2[2]);
28087
28088    _deltaE00_lab1[2]==0 && _deltaE00_a1prime==0?(
28089      _deltaE00_hprime1 = 0;
28090    ):(
28091      _deltaE00_hprime1 = atan2(_deltaE00_lab1[2],_deltaE00_a1prime);
28092      _deltaE00_hprime1<0?(_deltaE00_hprime1+=_deltaE00_deg360inrad);
28093      _deltaE00_lab2[2]==0 && _deltaE00_a2prime==0?(
28094        _deltaE00_hprime2 = 0;
28095      ):(
28096        _deltaE00_hprime2 = atan2(_deltaE00_lab2[2],_deltaE00_a2prime);
28097        _deltaE00_hprime2<0?(_deltaE00_hprime2+=_deltaE00_deg360inrad);
28098      );
28099    );
28100
28101    # Step 2
28102    _deltaE00_deltaLprime = _deltaE00_lab2[0] - _deltaE00_lab1[0];
28103    _deltaE00_deltaCprime = _deltaE00_Cprime2 - _deltaE00_Cprime1;
28104    _deltaE00_CprimeProduct = _deltaE00_Cprime1*_deltaE00_Cprime2;
28105    _deltaE00_CprimeProduct==0?(
28106      _deltaE00_deltahprime = 0;
28107    ):(
28108      _deltaE00_deltahprime = _deltaE00_hprime2 - _deltaE00_hprime1;
28109      _deltaE00_deltahprime<-_deltaE00_deg180inrad?(
28110        _deltaE00_deltahprime += _deltaE00_deg360inrad;
28111      ):_deltaE00_deltahprime>_deltaE00_deg180inrad?(
28112        _deltaE00_deltahprime -= _deltaE00_deg360inrad;
28113      );
28114    );
28115    _deltaE00_deltaHprime = 2*sqrt(_deltaE00_CprimeProduct)*sin(_deltaE00_deltahprime/2);
28116
28117    # Step 3
28118    _deltaE00_barLprime = (_deltaE00_lab1[0] + _deltaE00_lab2[0])/2;
28119    _deltaE00_barCprime = (_deltaE00_Cprime1 + _deltaE00_Cprime2)/2;
28120    _deltaE00_hprimeSum = _deltaE00_hprime1 + _deltaE00_hprime2;
28121    _deltaE00_Cprime1*_deltaE00_Cprime2==0?(
28122      _deltaE00_barhprime = _deltaE00_hprimeSum;
28123    ):(
28124      abs(_deltaE00_hprime1 - _deltaE00_hprime2)<=_deltaE00_deg180inrad?(
28125        _deltaE00_barhprime = _deltaE00_hprimeSum/2;
28126      ):(
28127        _deltaE00_hprimeSum<_deltaE00_deg360inrad?(
28128          _deltaE00_barhprime = (_deltaE00_hprimeSum + _deltaE00_deg360inrad)/2;
28129        ):(
28130          _deltaE00_barhprime = (_deltaE00_hprimeSum - _deltaE00_deg360inrad)/2;
28131        );
28132      );
28133    );
28134
28135    _deltaE00_T = 1.0 - (0.17*cos(_deltaE00_barhprime - _deltaE00_deg2rad(30))) +
28136                         (0.24*cos(2*_deltaE00_barhprime)) +
28137                         (0.32*cos(3*_deltaE00_barhprime + _deltaE00_deg2rad(6))) -
28138                         (0.2*cos(4*_deltaE00_barhprime - _deltaE00_deg2rad(63)));
28139    _deltaE00_deltaTheta = _deltaE00_deg2rad(30)*
28140      exp(-((_deltaE00_barhprime - _deltaE00_deg2rad(275)) / _deltaE00_deg2rad(25))^2);
28141
28142    _deltaE00_R_C = 2*sqrt(_deltaE00_barCprime^7/(_deltaE00_barCprime^7 + _deltaE00_pow25to7));
28143    _deltaE00_S_L = 1 + 0.015*(_deltaE00_barLprime - 50)^2/sqrt(20 + (_deltaE00_barLprime - 50)^2);
28144    _deltaE00_S_C = 1 + 0.045*_deltaE00_barCprime;
28145    _deltaE00_S_H = 1 + 0.015*_deltaE00_barCprime*_deltaE00_T;
28146    _deltaE00_R_T = -sin(2*_deltaE00_deltaTheta)* _deltaE00_R_C;
28147
28148    sqrt((_deltaE00_deltaLprime/(_deltaE00_k_L*_deltaE00_S_L))^2 +
28149         (_deltaE00_deltaCprime/(_deltaE00_k_C*_deltaE00_S_C))^2 +
28150         (_deltaE00_deltaHprime/(_deltaE00_k_H*_deltaE00_S_H))^2 +
28151         _deltaE00_R_T*_deltaE00_deltaCprime/(_deltaE00_k_C*_deltaE00_S_C)*
28152         _deltaE00_deltaHprime/(_deltaE00_k_H*_deltaE00_S_H));
28153  );"
28154
28155#@cli mad
28156#@cli : Return the MAD (Maximum Absolute Deviation) of the last selected image.
28157#@cli : The MAD is defined as MAD = med_i|x_i-med_j(x_j)|
28158mad :
28159  if $! +-. {ic} abs. u {1.4826*ic} rm. else u 0 fi
28160
28161#@cli max_w
28162#@cli : Return the maximal width between selected images.
28163max_w :
28164  _minmax_whds max,0,1
28165
28166#@cli max_h
28167#@cli : Return the maximal height between selected images.
28168max_h :
28169  _minmax_whds max,1,1
28170
28171#@cli max_d
28172#@cli : Return the maximal depth between selected images.
28173max_d :
28174  _minmax_whds max,2,1
28175
28176#@cli max_s
28177#@cli : Return the maximal spectrum between selected images.
28178max_s :
28179  _minmax_whds max,3,1
28180
28181#@cli max_wh
28182#@cli : Return the maximal wxh size of selected images.
28183max_wh :
28184  _minmax_whds max,0,2
28185
28186#@cli max_whd
28187#@cli : Return the maximal wxhxd size of selected images.
28188max_whd :
28189  _minmax_whds max,0,3
28190
28191#@cli max_whds
28192#@cli : Return the maximal wxhxdxs size of selected images.
28193max_whds :
28194  _minmax_whds max,0,4
28195
28196#@cli median_color
28197#@cli : Return the median color value of the last selected image.
28198median_color :
28199  u {"expr('med(crop(#-1,0,0,0,y,w#-1,h#-1,d#-1,1))',1,3)"}
28200
28201#@cli min_w
28202#@cli : Return the minimal width between selected images.
28203min_w :
28204  _minmax_whds min,0,1
28205
28206#@cli min_h
28207#@cli : Return the minimal height between selected images.
28208min_h :
28209  _minmax_whds min,1,1
28210
28211#@cli min_d
28212#@cli : Return the minimal depth between selected images.
28213min_d :
28214  _minmax_whds min,2,1
28215
28216#@cli min_s
28217#@cli : Return the minimal s size of selected images.
28218min_s :
28219  _minmax_whds min,3,1
28220
28221#@cli min_wh
28222#@cli : Return the minimal wxh size of selected images.
28223min_wh :
28224  _minmax_whds min,0,2
28225
28226#@cli min_whd
28227#@cli : Return the minimal wxhxd size of selected images.
28228min_whd :
28229  _minmax_whds min,0,3
28230
28231#@cli min_whds
28232#@cli : Return the minimal wxhxdxs size of selected images.
28233min_whds :
28234  _minmax_whds min,0,4
28235
28236_minmax_whds :
28237  u {"
28238    mw = w; mh = h; md = d; ms = s;
28239    repeat (l,k,
28240      mw = $1(mw,w#k);
28241      mh = $1(mh,h#k);
28242      md = $1(md,d#k);
28243      ms = $1(ms,s#k);
28244    );
28245    ([mw,mh,md,ms])[$2,$3]"}
28246
28247
28248#@cli nmd : eq. to 'named' : (+)
28249
28250#@cli named : _mode,"name1","name2",... : (+)
28251#@cli : Return the set of indices corresponding to images of the selection with specified names.
28252#@cli : After this command returns, the status contains a list of indices (unsigned integers),
28253#@cli : separated by commas (or an empty string if no images with those names have been found).
28254#@cli : (eq. to 'nmd').
28255#@cli : 'mode' can be { 0=all indices (default) | 1=lowest index | 2=highest index | \
28256# 3 = all indices (case insensitive) | 4 = lowest index (case insensitive) | 5 = highest index (case insensitive)}
28257
28258#@cli normalize_filename : filename
28259#@cli : Return a "normalized" version of the specified filename, without spaces and capital letters.
28260normalize_filename :
28261  ('"$1"') f. 'if(i>=65&&i<=90,i+32,if(i==32,95,i))' u {t} rm.
28262
28263#@cli oct : octal_int1,...
28264#@cli : Print specified octal integers into their binary, decimal, hexadecimal and string representations.
28265oct :
28266  dec=${oct2dec\ ${^0}}
28267  e[^-1] "Convert octal integer"${arg\ 1+($#>1),"",s}" '${^0}' to binary '"${dec2bin\ $dec}"', decimal '"$dec"',
28268           hexadecimal '"${dec2hex\ $dec}"' and string '"${dec2str\ $dec}"'."
28269
28270#@cli oct2dec : octal_int1,...
28271#@cli : Convert specified octal integers into their decimal representations.
28272oct2dec :
28273  $=arg res,sep= repeat $# res=$res$sep${_$0\ ${arg{$>+1}}} sep=, done u $res
28274
28275_oct2dec :
28276  u {"str = vtos(abs($1));
28277      for (k = val = 0, str[k], ++k,
28278        c = str[k];
28279        (val<<=3)+=(c>=_'0' && c<=_'7'?c - _'0':nan);
28280        isnan(val)?break()
28281      ); sign($1)*val"}
28282
28283#gmic ovh2stats : logfile_globname
28284#gmic : Generate statistics on the usage of G'MIC-Qt from OVH server logfiles.
28285#gmic : logfiles must be located in current directory.
28286#gmic : Default value: 'logfile_globname=gmic.eu-*-*-*.log.gz"
28287ovh2stats : skip "${1=gmic.eu-*-*-*.log.gz}"
28288  e[^-1] "Generate statistics on the usage of G'MIC-Qt from OVH server logfiles '$1'."
28289  use_vt100
28290
28291  # Load log files.
28292  files "$1" files=${}
28293  e[] "* Load log files."
28294  if !narg(${}) error[0--2] "No OVH logfiles matching name '$1' have been found." fi
28295
28296  repeat narg(${})
28297    arg0 $>,$files file=${}
28298    day,month,year={"str = ['"$file"'];
28299      t1 = find(str,_'-') + 1;
28300      t2 = find(str,_'-',t1) + 1;
28301      t3 = find(str,_'-',t2) + 1;
28302      t1>0 && t2>t1 && t3>t2?(
28303        day = stov(str,t1);
28304        month = stov(str,t2);
28305        year = stov(str,t3);
28306        [ day,month,year ];
28307      ):[0,0,0]"}
28308    if !$day&&!$month&&!$year day,month,year=n.a fi
28309    e[] "  > "$year/$month/$day:" "$_vt100_g$file$_vt100_n
28310    it $file
28311    nm. ${year}_${month}_${day}
28312  done
28313
28314  # Keep only lines containing 'G'MIC-Qt'.
28315  repeat $! nm={$>,n} l[$>] s -,{'\n'}
28316    eval "
28317      str = ['G'MIC-Qt'];
28318      to_keep = vectorl();
28319      p = 0;
28320      repeat (l,k, find(#k,str)>=0?(to_keep[p++] = k));
28321      store(to_keep,'to_keep',p);
28322      run('$to_keep k[{^}]')"
28323    if $!>1 i[1--2] ('\n') fi
28324  a y nm $nm endl done
28325
28326  # Analyze log files.
28327  e[] "\n* Analyze log files (day by day)."
28328  +_ovh2stats    # Display stats, day by day.
28329
28330  e[] "\n* Analyze log files (all days)."
28331  a y _ovh2stats # Display stats, all days.
28332
28333
28334_ovh2stats :
28335  v + N=$! repeat $! l[$>] ('{n}') replace. {'_'},{'^'} ({t})
28336    date={`[vtos(i0,0,4),_'/',i1<10?[_'0',i1+_'0']:vtos(i1,0,2),_'/',i2<10?[_'0',i2+_'0']:vtos(i2,0,2)]`}
28337    s -,{'\n'}
28338
28339    # Name each image by the IP address.
28340    eval "
28341      name = vector256();
28342      repeat (l,k,
28343        p = find(#k,_' ');
28344        copy(name,i[#k,0],p);
28345        name[p] = 0;
28346        run('name[',k,'] ',name);
28347      )"
28348
28349    # Discard IP duplicates.
28350    eval "
28351      const N = "$!";
28352      ref(vector(#2*N),tab);
28353      repeat (N,k,
28354        ref(name(#k,256),nam);
28355        for (i = j = 0, i<size(nam) && nam[i], ++i, nam[i]!=_'.'?(nam[j++] = nam[i])); nam[j] = 0;
28356        tab[2*k] = stov(nam);
28357        tab[2*k + 1] = k;
28358      );
28359      tab = sort(tab,1,N,2);
28360      for (i = 0, i<size(tab) - 1, ++i, tab[2*i]==tab[2*i + 2]?tab[2*i] = tab[2*i + 1] = -1);
28361      store(tab,'tab')"
28362    $tab discard. -1 r. 2,{h/2},1,1,-1 z. 1,1 k[{^}]
28363
28364    # Analyze G'MIC version, host software and OS.
28365    eval "
28366      gimp = krita = _8bf = paintdotnet = digikam = standalone = 0;
28367      linux = windows = macos = bsd = unknown = 0;
28368      versions = vector400();
28369      prereleases = 0;
28370      ips = 0;
28371      repeat (l,k,
28372        ref(lowercase(crop(#k,0,0,0,0,1,256,1,1)),str);
28373        find(str,'gimp')>=0?++gimp:
28374        find(str,'krita')>=0?++krita:
28375        find(str,'8bf')>=0?++_8bf:
28376        find(str,'paint.net')>=0?++paintdotnet:
28377        find(str,'digikam')>=0?++digikam:
28378        ++standalone;
28379
28380        find(str,'windows')>=0?++windows:
28381        find(str,'linux')>=0?++linux:
28382        find(str,'mac os')>=0?++macos:
28383        find(str,'bsd')>=0?++bsd:
28384        ++unknown;
28385
28386        (eos = find(str,0))<0?(eos = size(str));
28387        space = find(str,_' ',eos,-1);
28388        ver = sum(([ str[space + 1],str[space + 3],str[space + 5] ] - _'0')*[ 100,10,1 ]);
28389        ++versions[ver];
28390        find(str,'_pre#')>=0?++prereleases;
28391
28392        ++ips;
28393      );
28394      store([ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown,prereleases],
28395            'features');
28396      store(versions,'versions')"
28397    rm $features
28398    ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown,prereleases={^}
28399    $versions
28400    a y
28401
28402    if $N>1
28403      e[] "  > "$date": "\
28404          ${_vt100_b}"Unique IPs"${_vt100_n}" = "$ips" (100%),"
28405      e[] "                "\
28406          ${_vt100_m}${_vt100_b}"GIMP"${_vt100_n}" = "$gimp" ("{_round($gimp/$ips*100,0.1)}"%), "\
28407          ${_vt100_c}${_vt100_b}"Krita"${_vt100_n}" = "$krita" ("{_round($krita/$ips*100,0.1)}"%), "\
28408          ${_vt100_g}${_vt100_b}"8bf"${_vt100_n}" = "$_8bf" ("{_round($_8bf/$ips*100,0.1)}"%), "\
28409          ${_vt100_r}${_vt100_b}"Paint.NET"${_vt100_n}" = "$paintdotnet" ("{_round($paintdotnet/$ips*100,0.1)}"%), "\
28410          ${_vt100_m}${_vt100_b}"Digikam"${_vt100_n}" = "$digikam" ("{_round($digikam/$ips*100,0.1)}"%), "\
28411          ${_vt100_c}${_vt100_b}"Standalone"${_vt100_n}" = "$standalone" ("{_round($standalone/$ips*100,0.1)}"%)."
28412      e[] "                "\
28413          ${_vt100_r}${_vt100_b}"Windows"${_vt100_n}" = "$windows" ("{_round($windows/$ips*100,0.1)}"%), "\
28414          ${_vt100_g}${_vt100_b}"Linux"${_vt100_n}" = "$linux" ("{_round($linux/$ips*100,0.1)}"%), "\
28415          ${_vt100_m}${_vt100_b}"Mac OS"${_vt100_n}" = "$macos" ("{_round($macos/$ips*100,0.1)}"%), "\
28416          ${_vt100_c}${_vt100_b}"BSD"${_vt100_n}" = "$bsd" ("{_round($bsd/$ips*100,0.1)}"%), "\
28417          ${_vt100_c}${_vt100_b}"Unknown"${_vt100_n}" = "$unknown" ("{_round($unknown/$ips*100,0.1)}"%)."
28418      e[] "                "\
28419          ${_vt100_b}"Prereleases"${_vt100_n}" = "$prereleases" ("{_round($prereleases/$ips*100,0.1)}"%)."
28420      _ovh2stats_versions
28421    fi
28422  endl done
28423  + / $N round.
28424  +z. 0,0,12,0 ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown,prereleases={^} rm.
28425  summary0,summary1="Total:      ","Average:    "
28426  e[] "  > "${summary{$N>1}}\
28427      ${_vt100_b}"Unique IPs"${_vt100_n}" = "$ips" (100%),"
28428  e[] "                "\
28429      ${_vt100_m}${_vt100_b}"GIMP"${_vt100_n}" = "$gimp" ("{_round($gimp/$ips*100,0.1)}"%), "\
28430      ${_vt100_c}${_vt100_b}"Krita"${_vt100_n}" = "$krita" ("{_round($krita/$ips*100,0.1)}"%), "\
28431      ${_vt100_g}${_vt100_b}"8bf"${_vt100_n}" = "$_8bf" ("{_round($_8bf/$ips*100,0.1)}"%), "\
28432      ${_vt100_r}${_vt100_b}"Paint.NET"${_vt100_n}" = "$paintdotnet" ("{_round($paintdotnet/$ips*100,0.1)}"%), "\
28433      ${_vt100_m}${_vt100_b}"Digikam"${_vt100_n}" = "$digikam" ("{_round($digikam/$ips*100,0.1)}"%), "\
28434      ${_vt100_c}${_vt100_b}"Standalone"${_vt100_n}" = "$standalone" ("{_round($standalone/$ips*100,0.1)}"%)."
28435  e[] "                "\
28436      ${_vt100_r}${_vt100_b}"Windows"${_vt100_n}" = "$windows" ("{_round($windows/$ips*100,0.1)}"%), "\
28437      ${_vt100_g}${_vt100_b}"Linux"${_vt100_n}" = "$linux" ("{_round($linux/$ips*100,0.1)}"%), "\
28438      ${_vt100_m}${_vt100_b}"Mac OS"${_vt100_n}" = "$macos" ("{_round($macos/$ips*100,0.1)}"%), "\
28439      ${_vt100_c}${_vt100_b}"BSD"${_vt100_n}" = "$bsd" ("{_round($bsd/$ips*100,0.1)}"%), "\
28440      ${_vt100_c}${_vt100_b}"Unknown"${_vt100_n}" = "$unknown" ("{_round($unknown/$ips*100,0.1)}"%)."
28441  e[] "                "\
28442      ${_vt100_b}"Prereleases"${_vt100_n}" = "$prereleases" ("{_round($prereleases/$ips*100,0.1)}"%)."
28443  _ovh2stats_versions
28444  rm
28445
28446_ovh2stats_versions :
28447  v + +rows. 100% l. ips={is}
28448    +f x a y sort -,x eo0={min(9,find(crop(),0))}
28449    if $eo0
28450      str= v=0 c=
28451      for i($v,0)>0" && "$v<9
28452        str.=$c${_vt100_b}{i($v,1)}${_vt100_n}" ("{i($v,0)}": "{_round(i($v,0)/$ips*100,0.1)}"%)"
28453        v+=1 c=", "
28454      done
28455      e[] "                "${_vt100_c}${_vt100_b}"Version"${_vt100_n}" = "$str".\n"
28456    fi
28457    rm
28458  endl
28459
28460#@cli padint : number,_size>0
28461#@cli : Return a integer with 'size' digits (eventually left-padded with '0').
28462padint : check "isint($1)" skip ${2=4}
28463  u "" repeat $2 u ${}{int($1/10^$<)%10} done
28464
28465#@cli path_cache
28466#@cli : Return a path to store G'MIC data files for one user (whose value is OS-dependent).
28467path_cache :
28468  if !narg({'$_path_cache'})
28469    _path_cache=$_path_rc
28470    if ['$GMIC_CACHE_PATH']!=0 _patch_cache=$GMIC_CACHE_PATH
28471    elif !${-is_windows}
28472      if isdir(['{/$HOME/.cache}']) _path_cache=$HOME/.cache/gmic/
28473      elif ['$XDG_CACHE_HOME']!=0 _path_cache=$XDG_CACHE_HOME/gmic/
28474      fi
28475      if !isdir('{/$_path_cache}') x "mkdir -p "$_path_cache fi
28476    fi
28477  fi
28478  u $_path_cache
28479
28480#@cli path_current
28481#@cli : Return current folder from where G'MIC has been run.
28482path_current :
28483  if !${-is_windows}
28484    filename=${-path_tmp}gmic_pwd
28485    l[] x "pwd > "$filename it $filename autocrop {'\n'} autocrop {'" "'} u {t}/ rm onfail u "./" endl
28486  else u "./"
28487  fi
28488
28489#@cli path_gimp
28490#@cli : Return a path to store GIMP configuration files for one user (whose value is OS-dependent).
28491path_gimp :
28492  if !narg({'$_path_gimp'})
28493    if narg({'${GIMP2_DIRECTORY}'}) _path_gimp=${GIMP2_DIRECTORY}
28494    elif narg({'${USERPROFILE}'}) _path_gimp=${USERPROFILE}
28495    elif narg({'${HOME}'}) _path_gimp=${HOME}
28496    fi
28497    if ${-is_windows} sep={`92`} else sep=/ fi
28498    if isdir(['{/$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.10}'])
28499      _path_gimp=$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.10${sep}
28500    elif isdir(['{/$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.8}'])
28501      _path_gimp=$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.8${sep}
28502    elif isdir(['{/$_path_gimp${sep}.config${sep}GIMP${sep}2.10}'])
28503      _path_gimp=$_path_gimp${sep}.config${sep}GIMP${sep}2.10${sep}
28504    elif isdir(['{/$_path_gimp${sep}.gimp-2.8}'])
28505      _path_gimp=$_path_gimp${sep}.gimp-2.8${sep}
28506    elif isdir(['{/$_path_gimp${sep}.gimp-2.6}'])
28507      _path_gimp=$_path_gimp${sep}.gimp-2.6${sep}
28508    else
28509      _path_gimp=${-path_tmp}
28510    fi
28511  fi
28512  u $_path_gimp
28513
28514#@cli path_tmp
28515#@cli : Return a path to store temporary files (whose value is OS-dependent).
28516path_tmp :
28517  if !narg({'$_path_tmp'})
28518    if narg({'${TMP}'}) _path_tmp=${TMP}
28519    elif narg({'${TEMP}'}) _path_tmp=${TEMP}
28520    elif narg({'${TMPDIR}'}) _path_tmp=${TMPDIR}
28521    elif narg({'${HOME}'}) _path_tmp="/tmp"
28522    fi
28523    if ${-is_windows} _path_tmp=$_path_tmp{`92`} else _path_tmp=$_path_tmp/ fi
28524  fi
28525  u $_path_tmp
28526
28527#@cli remove_copymark : "image_name"
28528#@cli : Remove copy mark from names of selected images.
28529remove_copymark :
28530  u {`"name = ['$1'];
28531       i = find(name,'_c');
28532       i>=0 && i<size(name) - 2 && (c=name[i+2])>=_'1' && c<=_'9'?(
28533         ext = ['"{$>,x}"'];
28534         size(ext)?copy(name[i],[_'.',ext,0]):(name[i]=0);
28535       ); name"`}
28536
28537# render_donations: Parse the PayPal .CSV file and generate 'latest-months' donation gfx.
28538# $1 = filename.csv
28539# $2 = number of latest months.
28540render_donations : skip "${1="$HOME"/work/src/private_tschumperle/gmic_donations.csv}",${2=0}
28541  use_vt100
28542  nb_months={$2?$2:4}
28543  l[]
28544    # Parse CSV data.
28545    "$1"
28546    eur2usd={1/0.7988} # Change rate from EUR to USD (Paypal conversion rate).
28547    nb_entries={h}
28548    repeat $nb_entries,e
28549      date_$e={`I(0,$e)`}
28550      name_$e={`I(1,$e)`}
28551      type_$e={`I(2,$e)`}
28552      currency_$e={`lowercase(I(3,$e))`}
28553      donation_$e={{`I(4,$e)`}}
28554      charge_paypal_$e={{`I(5,$e)`}}
28555      charge_lila_$e={{`I(6,$e)`}}
28556      mail_$e={`I(7,$e)`}
28557      message_$e={`I(8,$e)`}
28558
28559      # Deduced values.
28560      l[]
28561        ('${date_$e}') s -,{'/'} day_$e,month_$e,year_$e={0,t},{1,t},{2,t} rm
28562        edate_$e={${day_$e}+100*(${month_$e}+100*${year_$e})}
28563      endl
28564      donation_charged_$e={${donation_$e}+${charge_paypal_$e}+${charge_lila_$e}}
28565
28566      if '${currency_$e}'=='eur'
28567        donation_eur_$e=${donation_$e}
28568        donation_charged_eur_$e=${donation_charged_$e}
28569        donation_usd_$e={${donation_$e}*$eur2usd}
28570        donation_charged_usd_$e={${donation_charged_$e}*$eur2usd}
28571      else
28572        donation_usd_$e=${donation_$e}
28573        donation_charged_usd_$e=${donation_charged_$e}
28574        donation_eur_$e={${donation_$e}/$eur2usd}
28575        donation_charged_eur_$e={${donation_charged_$e}/$eur2usd}
28576      fi
28577
28578      # Print infos.
28579      if ${donation_$e}>=15 col=$_vt100_r else col= fi
28580      e[] "- ["$_vt100_c$_vt100_b"#"$e$_vt100_n" - "$_vt100_c${date_$e}$_vt100_n"] "\
28581          $col$_vt100_b{_${donation_$e}}" "${currency_$e}$_vt100_n\
28582          " (paypal: "{_${charge_paypal_$e}}" "${currency_$e},\
28583          " lila: "{_${charge_lila_$e}}" "${currency_$e}" ->"\
28584          " "{_${donation_charged_$e}}" "${currency_$e}" ="\
28585          " "{_${donation_charged_eur_$e}}" eur),"\
28586          " from "${name_$e}" ("${mail_$e}") : '"$_vt100_b${message_$e}$_vt100_n"'"
28587    done
28588    rm
28589
28590    # Compute image for donations of the last months.
28591    e[] ""
28592    all_eur,all_usd,all_charged_eur,all_charged_usd,asep_eur,asep_usd=
28593    repeat $nb_months
28594      edate={y=date(0);m=date(1)-1-$>;while(m<=0,--y;m+=12);100*y+m}
28595      s_month=${"arg "$edate%100",January,February,March,April,May,June,July,August,\
28596                                  September,October,November,December"}
28597      s_year={int($edate/100)}
28598
28599      month_eur,month_charged_eur,month_usd,month_charged_usd,msep_eur,msep_usd=
28600      nb_sponsors=0
28601      repeat $nb_entries,e if $edate==int(${edate_$e}/100)
28602        currency=${currency_$e}
28603        val_eur=${donation_eur_$e}
28604        val_charged_eur=${donation_charged_eur_$e}
28605        val_usd=${donation_usd_$e}
28606        val_charged_usd=${donation_charged_usd_$e}
28607        nb_sponsors+=1
28608
28609        if '${currency_$e}'=='eur'
28610          all_eur.=$asep_eur$val_eur
28611          all_charged_eur.=$asep_eur$val_charged_eur
28612          month_eur.=$msep_eur$val_eur
28613          month_charged_eur.=$msep_eur$val_charged_eur
28614          asep_eur=, msep_eur=,
28615        else
28616          all_usd.=$asep_usd$val_usd
28617          all_charged_usd.=$asep_usd$val_charged_usd
28618          month_usd.=$msep_usd$val_usd
28619          month_charged_usd.=$msep_usd$val_charged_usd
28620          asep_usd=, msep_usd=,
28621        fi
28622      fi done
28623
28624      month_sum_eur={sum(0$month_eur)+sum(0$month_usd)/$eur2usd}
28625      month_sum_charged_eur={sum(0$month_charged_eur)+sum(0$month_charged_usd)/$eur2usd}
28626
28627      e[] "* "$_vt100_c$_vt100_b$s_month" "$s_year": "$_vt100_n\
28628          $_vt100_b{_$month_sum_eur}" eur"$_vt100_n" (charged: "{_$month_sum_charged_eur}" eur)"\
28629          " = "{_sum(0$month_eur)}" eur"$_vt100_n" (charged: "{_sum(0$month_charged_eur)}" eur)"\
28630          " + "{_sum(0$month_usd)}" usd (charged: "{_sum(0$month_charged_usd)}" usd)"
28631
28632      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
28633      shape_circle 24 s. x,2 s[-2,-1] y,2
28634      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]
28635      channels. 0  *.. . *. 255 a[-2,-1] c
28636      r. {[w,h]+4},1,100%,0,0,0.5,0.5
28637      sh. 100% dilate_circ. 3 b. 1 rm.
28638
28639      t. $s_month" "$s_year,0.02~,0.5~,24,0.5,0,0,0,255
28640      t. {_round($month_sum_eur)}" \37 = "{_round($month_sum_eur*$eur2usd)}" $",0.5~,0.5~,24,1,0,0,0,255
28641      t. $nb_sponsors" sponsor"${"if "$nb_sponsors"!=1 u s else u \"\" fi"},0.97~,0.5~,24,0.5,0,0,0,255
28642    done
28643    rv
28644
28645    all_max={max($all_eur,[$all_usd]/$eur2usd)}
28646    all_min={min($all_eur,[$all_usd]/$eur2usd)}
28647    all_sum_eur={sum($all_eur)}
28648    all_sum_usd={sum($all_usd)}
28649    all_sum_charged_eur={sum($all_charged_eur)}
28650    all_sum_charged_usd={sum($all_charged_usd)}
28651    all_avg={avg($all_eur,[$all_usd]/$eur2usd)}
28652    all_med={med($all_eur,[$all_usd]/$eur2usd)}
28653
28654    0 t. "Avg: "{_round($all_avg,0.1)}"\37 / "\
28655         "Med: "{_round($all_med,0.1)}"\37 / "\
28656         "Min: "{_round($all_min,0.1)}"\37 / "\
28657         "Max: "{_round($all_max,0.1)}"\37",0,0,24,1,1
28658    *. 200 i[-2] 100%,100%,1,3 a[-2,-1] c
28659
28660    rows -5,100% a y,0.5 r2dx 480
28661    if $2==0 o $HOME/work/src/gmic/html/img/donations_latest_months.png fi
28662  endl
28663
28664  total_eur={$all_sum_eur+$all_sum_usd/$eur2usd}
28665  total_charged_eur={$all_sum_charged_eur+$all_sum_charged_usd/$eur2usd}
28666  e[] "\n==> "$_vt100_c${_vt100_b}"TOTAL : "$_vt100_n\
28667      $_vt100_b{_floor($total_eur)}" eur"$_vt100_n" (charged : "{_floor($total_charged_eur)}" eur)"\
28668      " = "{_floor($all_sum_eur)}" eur (charged: "{_floor($all_sum_charged_eur)}" eur)"\
28669      " + "{_floor($all_sum_usd)}" usd (charged: "{_floor($all_sum_charged_usd)}" usd)\n"
28670
28671  # Generate report image for Twitter.
28672  sp gmicky,{h}
28673  l[-2,-1]
28674    rgb={I(w-1)} to_rgba rv
28675    frame 10,10,0,0,0,0
28676    drgba $rgb a x
28677    shape_heart 82 +r. 100%,100%,1,3 hsv2rgb. rv[-2,-1] *. 255 a[-2,-1] c
28678    r. ..,..,1,100%,0,0,0.23,0.15 drop_shadow. 3,3,2,0,0
28679    blend alpha,0.7 r2dx 500
28680    if $2==0 o $HOME/Desktop/donations_latest_months.jpg,85 rm return fi
28681  endl
28682
28683  # Generate coin graphics.
28684  l[]
28685    $HOME/work/src/gmic/html/img/icon_coin.png
28686    N=0
28687    text$N,r$N,g$N,b$N,file$N="2 \37",32,48,32,"don_2eur.png" N+=1
28688    text$N,r$N,g$N,b$N,file$N="5 \37",200,200,200,"don_5eur.png" N+=1
28689    text$N,r$N,g$N,b$N,file$N="10 \37",190,100,100,"don_10eur.png" N+=1
28690    text$N,r$N,g$N,b$N,file$N="+ \37",255,128,0,"don_moreeur.png" N+=1
28691    text$N,r$N,g$N,b$N,file$N="2 $",32,48,32,"don_2usd.png" N+=1
28692    text$N,r$N,g$N,b$N,file$N="5 $",200,200,200,"don_5usd.png" N+=1
28693    text$N,r$N,g$N,b$N,file$N="10 $",190,100,100,"don_10usd.png" N+=1
28694    text$N,r$N,g$N,b$N,file$N="+ $",255,128,0,"don_moreusd.png" N+=1
28695    repeat $N +l[0]
28696      frame. 10,10,0,0,0,0
28697      sh. 0,2
28698      (${r$>}^${g$>}^${b$>}) rgb2hsl[-2,-1] H,S={[R,G]} rm.
28699      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
28700      hsl2rgb. rm.
28701      sh. 100% dilate_circ. 10 rm.
28702
28703      100%,100% t. ${text$>},0.5~,0.5~,45%,1,255 dilate_circ. 10 +dilate_circ. 15 to_rgb.. a[-2,-1] c
28704      blend[-2,-1] alpha drop_shadow. 1,1 r2dx. 48 frame 10,5,0,0,0,0
28705      outfile=$HOME/work/src/gmic/html/img/${file$>}
28706      if !isfile('$outfile') o. $outfile fi
28707      rm.
28708    endl done
28709    rm[0]
28710  endl
28711
28712#@cli reset
28713#@cli : Reset global parameters of the interpreter environment.
28714reset :
28715  e[^-1] "Reset global parameters of the interpreter environment."
28716  db3d m3d md3d f3d l3d sl3d ss3d
28717
28718#@cli rgb
28719#@cli : Return a random int-valued RGB color.
28720rgb :
28721  u {round(u(255))},{round(u(255))},{round(u(255))}
28722
28723RGB : rgb
28724
28725#@cli rgba
28726#@cli : Return a random int-valued RGBA color.
28727rgba :
28728  u {round(u(255))},{round(u(255))},{round(u(255))},{round(u(255))}
28729
28730RGBA : rgba
28731
28732#@cli shell_cols
28733#@cli : Return the estimated number of columns of the current shell.
28734shell_cols :
28735  if ${-is_windows} u 80
28736  else
28737    file_rand filename=${}
28738    l[] cols=80 x "tput cols > "$filename it $filename if isint({t}) cols={{t}} fi rm onfail cols=80 rm endl
28739    delete $filename
28740    u $cols
28741  fi
28742
28743#@cli size_value
28744#@cli : Return the size (in bytes) of an image value.
28745size_value :
28746  u {str=['$_pixeltype'];echo(str);str=='float'?4:str=='double'?8:0}
28747
28748
28749#@cli std_noise
28750#@cli : Return the estimated noise standard deviation of the last selected image.
28751std_noise :
28752  if $! +laplacian. -. {ic} abs. u {1.4826*ic/sqrt(d==1?20:42)} rm. else u 0 fi
28753
28754#@cli str : string
28755#@cli : Print specified string into its binary, octal, decimal and hexadecimal representations.
28756str : skip $1
28757  dec={'$*'}
28758  e[^-1] "Convert string '$*' to binary '"${dec2bin\ $dec}"', octal '"${dec2oct\ $dec}"', decimal '"$dec"' and
28759           hexadecimal '"${dec2hex\ $dec}"'."
28760
28761#@cli str2hex : "string"
28762#@cli : Convert specified string argument into a sequence of hexadecimal values (returned as a string).
28763#@cli : See also: ''hex2str''.
28764#@cli : $ hex=${"str2hex \"Hello my friends\""} echo $hex
28765#@cli : \n~~~\n[gmic]-0./ Start G'MIC interpreter.\n[gmic]-0./ 48656c6c6f206d7920667269656e6473\n\
28766# [gmic]-0./ End G'MIC interpreter.\n~~~\n
28767str2hex :
28768  ('"$*"') y. r. 2,100% f. 'v=if(x,i%16,int(i/16));if(v<=9,48+v,87+v)' u {t} rm.
28769
28770#@cli strcapitalize : string
28771#@cli : Capitalize specified string.
28772strcapitalize :
28773  l[] ('{/"$1"}')
28774    f "(i<=_' ' || i==_'_') && i!=_'\n'?_' ':i" # Replace blank characters and underscores
28775    f ">
28776       t(s) = ( unref(ss); const ss = size(s); lowercase(crop(x,ss))!=s || j[ss]!=_' ');
28777       !x || (p=j[-1])==_'.' || p==_':' || p==_';' || p==_'\"' || (find(#0,_' ',x)<0 && p==_' ') ||
28778       (p==_' ' || p==_'-') &&
28779        t('a') && t('an') && t('and') && t('as') && t('at') && t('but') && t('by') && t('for') && t('from') &&
28780        t('in') && t('into') && t('like') && t('near') && t('nor') && t('of') && t('off') && t('on') &&
28781        t('onto') && t('or') && t('over') && t('past') && t('per') && t('than') && t('the') && t('to') &&
28782        t('up') && t('upon') && t('via') && t('with')?uppercase(i):i"
28783    u {t} rm endl
28784
28785#@cli strcontains : string1,string2
28786#@cli : Return 1 if the first string contains the second one.
28787strcontains :
28788  u {"find(['$1'],['$2'])>=0"}
28789
28790#@cli strlen : string1
28791#@cli : Return the length of specified string argument.
28792strlen : skip "${1=}"
28793  u {narg({'"$1"'})}
28794
28795#@cli strreplace : string,search,replace
28796#@cli : Search and replace substrings in an input string.
28797strreplace : skip "${3=}"
28798  if narg("$3")
28799    ls=${strlen\ "$2"}
28800    lr={${strlen\ "$3"}-1}
28801    l[] ('"$1"':y) s +,{'"$2"'} s y,-$ls
28802    repeat $! if [{$>,^}]==['"$2"'] rows[$>] 0,$lr f[$>] {'"$3"'} fi done
28803    a y u {t} rm endl
28804  else
28805    l[] ('"$1"') s -,{'"$2"'} a y u {t} rm endl
28806  fi
28807
28808#@cli strlowercase : string
28809#@cli : Return a lower-case version of the specified string.
28810strlowercase :
28811  ('"$*"') u {`lowercase([{^}])`} rm.
28812
28813#@cli struppercase : string
28814#@cli : Return an upper-case version of the specified string.
28815struppercase :
28816  ('"$*"') u {`uppercase([{^}])`} rm.
28817
28818#@cli strvar : "string"
28819#@cli : Return a simplified version of the specified string, that can be used as a variable name.
28820#@cli : (version that creates a lowercase result, no longer than 128 chars).
28821strvar :
28822  _strvar_fn="lowercase(c)" _strvar "$*"
28823
28824#@cli strcasevar : "string"
28825#@cli : Return a simplified version of the specified string, that can be used as a variable name.
28826#@cli : (version that keeps original case of specified string, no longer than 128 chars).
28827strcasevar :
28828  _strvar_fn="c" _strvar "$*"
28829
28830_strvar :
28831  l[]
28832    ('"$*"':y) f. "c = i; inrange(c,_'0',_'9') || inrange(c,_'a',_'z') ||
28833                          inrange(c,_'A',_'Z') || c==_'_'?"$_strvar_fn":_'_'"
28834    rows. 0,{min(h,128)-1} autocrop. {'_'}
28835    if inrange(i,_'0',_'9') i.. ('_') a[-2,-1] y fi
28836    do h={h} replace_str. "__","_" while h!=$h
28837    u {t} rm
28838  endl
28839
28840#@cli strver : _version
28841#@cli : Return the specified version number of the G'MIC interpreter, as a string.
28842#@cli : Default value: 'version=$_version'.
28843strver : skip "${1=}"
28844  if isnum("$1")" && $1>0" ver="$1" else ver=$_version noarg fi
28845  ('$ver') r. {2*w-1} f. 'if(x%2,_'.',i)' u {t} rm.
28846
28847#@cli tic
28848#@cli : Initialize tic-toc timer.
28849#@cli : Use it in conjunction with 'toc'.
28850tic :
28851  e[^-1] "Initialize timer."
28852  if !narg($_ticpos) _ticpos=0 fi _tic$_ticpos=$| _ticpos+=1
28853
28854#@cli toc
28855#@cli : Display elapsed time of the tic-toc timer since the last call to 'tic'.
28856#@cli : This command returns the elapsed time in the status value.
28857#@cli : Use it in conjunction with 'tic'.
28858toc :
28859  v=$^ _ticpos-=1 u {_$|-${_tic$_ticpos}}
28860  v 0 e[^-1] "Elapsed time: "${}" s". v $v
28861
28862#@cli to_clutname : "string"
28863#@cli : Return simplified name that can be used as a CLUT name, from specified input string.
28864to_clutname :
28865  0 nm. "$1" nm={b} rm.
28866  u {`"ss = lowercase([['"{/$nm}"'],0]);
28867       const N = 2*size(ss);
28868       sd = vectorN();
28869       for (ps = pd = 0, ss[ps], ++ps,
28870         ss[ps]<=_' '?(sd[pd++] = _'_'):
28871         (ss[ps]==_'(' || ss[ps]==_')' ||
28872          ss[ps]==_'{' || ss[ps]==_'}' ||
28873          ss[ps]==_'[' || ss[ps]==_']' ||
28874          ss[ps]==_'\'' || ss[ps]==_'\"')?0:
28875         (ps && ss[ps]>=_'0' && ss[ps]<=_'9' && ss[ps-1]>=_'a' && ss[ps-1]<=_'z')?(sd[pd++] = _'_'; sd[pd++] = ss[ps]):
28876         (sd[pd++] = ss[ps]);
28877       ); sd[pd] = 0; sd"`}
28878
28879#@cli uchar2base64 : _encoding={ 0=base64 | 1=base64url }
28880#@cli : Encode the values of the latest of the selected images as a base64-encoded string.
28881#@cli : The string can be decoded using command 'base642uchar'.
28882#@cli : Selected images must have values that are integers in [0,255].
28883#@cli : Default values: 'encoding=0'.
28884uchar2base64 : skip "${1=0}"
28885  if isnum("$1") encoding=$1 else encoding=0 noarg fi
28886  {ceil(whds*4/3)+([0,2,1])[whds%3]}
28887  eval "
28888    hash = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
28889    "$encoding"?(hash[62] = _'-'; hash[63] = _'_');
28890    od = ov = n = 0;
28891    for (os = 0, os<whds#-2,
28892      v = i[#-2,os++]&255;
28893      n==0?(i[#-1,od++] = hash[v>>2]; ov = v; n = 1):
28894      n==1?(i[#-1,od++] = hash[((ov&3)<<4) | (v>>4)]; ov = v; n = 2):
28895      (i[#-1,od++] = hash[((ov&15)<<2) | (v>>6)]; i[#-1,od++] = hash[v&63]; n = 0);
28896    );
28897    n==1?(i[#-1,od++] = hash[((ov&3)<<4)]; copy(i[#-1,od],_'=',2,1,0); od+=2):
28898    n==2?(i[#-1,od++] = hash[((ov&15)<<2)]; i[#-1,od++] = _'=');
28899  "
28900  u {t} rm.
28901
28902#-------------------------------------
28903#
28904#@cli :: Other Interactive Commands
28905#
28906#-------------------------------------
28907
28908#@cli demos : _run_in_parallel={ 0=no | 1=yes | 2=auto }
28909#@cli : Show a menu to select and view all G'MIC interactive demos.
28910demos : check "isint(${1=2}) && $1>=0 && $1<=2" check_display $0
28911  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
28912  strver=${-strver}
28913  if $_prerelease strver=${strver}_pre#$_prerelease fi
28914  e[] "\n
28915------ "${g}"G\47MIC demos"$n" ------------------\n
28916----\n
28917---- "${c}"Mouse button"$n" to select a demo.\n
28918---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
28919---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
28920---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
28921----\n
28922-------------------------------------"
28923  l[]
28924  entries="2048 game","Blobs Editor","Bouncing Balls","Connect Four","Fire Effect","Fireworks","Fish-Eye Effect",\
28925          "Fourier Filtering","Tower of Hano\357","Histogram Demo","Hough Transform","Jawbreaker","Virtual Landscape",\
28926          "The Game of Life","Light Effect","Mandelbrot Explorer","3D Metaballs","Minesweeper","Minimal Path",\
28927          "Pacman","Paint","Plasma Effect","RGB Quantization","3D Reflection","3D Rubber Object","Shade Bobs",\
28928          "Spline Editor","3D Starfield","Tetris","Tic-Tac-Toe","Image Waves","Fractal Whirls","Color Curves"
28929  commands=x_2048,x_blobs,x_bouncing,x_connect4,x_fire,x_fireworks,x_fisheye,\
28930           x_fourier,x_hanoi,x_histogram,x_hough,x_jawbreaker,x_landscape,x_life,\
28931           x_light,x_mandelbrot,x_metaballs3d,x_minesweeper,x_minimal_path,x_pacman,x_paint,\
28932           x_plasma,x_quantize_rgb,x_reflection3d,x_rubber3d,x_shadebobs,x_spline,x_starfield3d,\
28933           x_tetris,x_tictactoe,x_waves,x_whirl,_demo_color_curves
28934  nb_entries={narg($entries)}
28935  parallel_mode={1-if($1!=2,$1,$_cpus>=2)}
28936  strver=${-strver}
28937  if $_prerelease strver.=_pre#$_prerelease fi
28938
28939  # Generate menu graphics.
28940  l[]
28941    repeat $nb_entries
28942      arg 1+$>,$entries entry=${}
28943      0 t. $entry,0,0,24,1,1
28944    done
28945    r ${-max_wh},1,1,0,0,0.5,0.5
28946    frame 12,6,0 a z
28947    +n[0] 0,255
28948    +shift[0] 1,3,0,0 max[0,-1]
28949    +f[0] z+1
28950    1,1,100%,3,u(128,255) r. 1,[0],[0],3 *. 'y/(h-1)' r. [0],[0],[0],3,3
28951
28952    100%,100%,1,1,"x = min(x,w-1-x); y = min(y,h-1-y); (x*y/wh)^0.7>0.01"
28953    +dilate. 3 xor.. .
28954    +[0] ..
28955    *.. 100 +[1,-2]
28956    *[2,3] .
28957    rm.
28958    frame 4,4,0
28959    a c s z
28960    append_tiles 3 s c
28961    to_rgb[1] a[3-5] c
28962
28963    0 t. "G\'MIC demos",2,2,32,1,1,1,1
28964    if $_prerelease 0 t. "Version:\n"$strver,40,1,18,1,1,1,1 rows. 2,100%
28965    else 0 t. "Version: "$strver,40,1,18,1,1,1,1 rows. -2,100%
28966    fi
28967    a[-2,-1] x,0.5
28968    b. 0.5 n. 0,1
28969    (32;255^255;255^0;0) r. ..,..,1,3,3 *[-2,-1] round. 1,-1 +!=. 0 channels. 1
28970
28971    s=8
28972    r[^-2,-1] 100%,{h+h#0+2*$s+6},1,100%,0,0,0,1
28973    rectangle[0] 0,$s,100%,{$s+6+h-1},1,0.6
28974    rectangle[1] 0,$s,100%,{$s+6+h-1},1,120,120,32
28975    j[0] .,{(w#0-w)/2},{$s+3},0,0,-1
28976    j[1] ..,{(w#0-w)/2},{$s+3},0,0,1,. rm[-2,-1]
28977    c[0] 0,1
28978    nm menu_opac,menu_fgcol,menu_ind,menu_bgcol
28979  endl
28980
28981  # Generate 3D cursors.
28982  arrow3d 20,20,0,0,0,0,20%,40%,40% col3d. 255,128,0 nm. cursor3d
28983
28984  # Generate 3D background object.
28985  l[]
28986    n=16
28987    chromeball64x64[] 200,100,64 n. 0,230 s. c,-3
28988    rgb2hsv.. r.. 100%,100%,$n,3 f.. "[z*360/d,G,B]" hsv2rgb..
28989    N={6*$n} P={2*$N-1}
28990    i[0] ('CImg3d') i[1] ($N,$P) i[2] 3,$N # Header, points
28991    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
28992    l[4] # Colors
28993      s z i[0--2] (-128,{w},{h},3) 4,{$N-$n},1,1,'x==0?-128:x==1?y%$n:0' # Colors
28994      3,{$P-$N},1,1,200 y a y
28995    endl
28996    l[5] # Opacities
28997      n 0,0.5 i[0] (-128,{w},{h},1) 4,{$N-1},1,1,-128,0,0,0
28998      1,{$P-$N},1,1,0.25 y a y
28999    endl
29000    y a y
29001    nm background3d
29002  endl
29003
29004  # Generate background.
29005  {menu_fgcol,[w,h]},1,3 +plasma. 1,1,5 n. 0,230 water. 100
29006  (0.1;0.03^0;0.1^0.2;0.1) ri. ..,3 *[-2,-1]
29007  (0;1) r. ..,..,1,1,3 pow. 1.5 n. 0.2,1.15 *[-2,-1] n. 0,128
29008  nm. background
29009  w. -1,-1,0,"[G'MIC - "$strver"]" cursor 0 w[] -1,-1,0,0,{([{*,u,v}]-[{*,w,h}])/2}
29010
29011  # Enter event loop.
29012  omb,ind_clicked,cfx,cfy,cfz,alpha=0
29013  nfx,nfy,nfz={[g,g,g]} time0={$|-4}
29014  do
29015    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}]}
29016    ind={menu_ind,i($mx,$my)}
29017    if $mb" && "!$ind_clicked ind_clicked=$ind fi
29018
29019    # Render current view.
29020    [background]
29021
29022    3,$N,1,1,"const t = 0.8*"$|"; const a = "$alpha"; const oma = 1 - a;
29023              x==0?oma*cos("$cfx"*y + t) + a*cos("$nfx"*y + t):
29024              x==1?oma*sin("$cfy"*y + t) + a*sin("$nfy"*y + t):
29025                   oma*sin("$cfz"*y + t) + a*sin("$nfz"*y + t)"
29026    y.
29027    j[background3d] .,0,8 rm.
29028    +r3d[background3d] 1,2,3,{20*$|} *3d. {menu_fgcol,[w,h]/2-30},300 +3d. 0,0,300
29029    j3d.. .,50%,50%,0,1,1,0,0,200 rm.
29030    if $|-$time0>5 alpha+=0.02 fi
29031    if $alpha>1 alpha-=1 cfx,cfy,cfz=$nfx,$nfy,$nfz nfx,nfy,nfz={[g,g,g]} time0={$|-u*3} fi
29032
29033    if $ind>0 +==[menu_ind] # Draw selection background
29034      $ind j.. [menu_bgcol],0,0,0,0,{$mb" && "$ind_clicked==$ind?0.6:1},. rm.
29035    fi
29036    j. [menu_fgcol],0,0,0,0,1,[menu_opac]
29037
29038    if $mx>0
29039      +r3d[cursor3d] 1,1.3,0.6,{50*cos($|)}
29040      j3d.. .,$mx,$my,0,1,4,0,0,800,{-2,[w,h]/2},-1000,0.7 rm.
29041    fi
29042
29043    w. -1,-1,0
29044    if {*,CTRLLEFT}" && "{*,D} w. 150%,150% elif {*,CTRLLEFT}" && "{*,C} w. 100%,100% fi
29045    rm.
29046
29047    # Manage user selection.
29048    if !$mb" && "$omb" && "$ind_clicked" && "$ind_clicked==$ind
29049      m "com : v 1 "${arg\ $ind,$commands} parallel $parallel_mode,"l[] com rm endl" um com
29050    elif !$mb ind_clicked=0
29051    fi
29052    omb=$mb
29053
29054    wait 20
29055  while {*}" && "!{*,ESC}" && "!{*,Q}
29056  rm w 0 endl v 0 e[] ""
29057
29058_demo_color_curves :
29059  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29060  if !narg($__demo_color_curve)
29061    e[] "\n
29062------ "${g}"Color curves"$n" ----------------------------------------------------------------------------\n
29063----\n
29064---- "${c}"Left mouse button"$n" on a curve creates a new control point (or moves an existing one).\n
29065---- "${c}"Right mouse button"$n" on a control point deletes it.\n
29066---- "${c}"Left mouse button"$n" on the main image window shows the initial image until button is released.\n
29067---- "${c}"Right mouse button"$n" on the main image window adds a keypoint to all curves from picked color.\n
29068---- Key '"${c}"R"$n"' on a curve resets it.\n
29069---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
29070---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
29071---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
29072---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n
29073----\n
29074------------------------------------------------------------------------------------------------"
29075    __demo_color_curve=1 l[] do rm sp ? while s!=3 endl x_color_curves rgb __demo_color_curve=
29076  else
29077    e[] "\n
29078------ "${g}"Color curves"$n" ----------------------------------------------------------------------------\n
29079----\n
29080---- Only "${c}"one session"$n" allowed at the same time !\n
29081----\n
29082------------------------------------------------------------------------------------------------"
29083  fi
29084
29085#@cli tixy : "expression"
29086#@cli : Animate specified mathematical expression with a 16x16 grid of circles, \
29087# using the rules described at <https://tixy.land>.
29088tixy : skip "${1=sin(t-sqrt((x-8)^2+(y-8)^2))}" check_display $0
29089  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r b=$_vt100_b
29090  if narg("$*") expr="$*" else expr="$1" fi
29091  l[] ('$expr')
29092    replace_str "%","%25"
29093    replace_str "&","%26"
29094    replace_str "(","%28"
29095    replace_str ")","%29"
29096    replace_str "+","%2B"
29097    replace_str ",","%2C"
29098    replace_str ";","%2C"
29099    replace_str "/","%2F"
29100    replace_str "[","%5B"
29101    replace_str "]","%5D"
29102    replace_str "|","%7C"
29103    replace_str "^","**"
29104    replace_str " ","+"
29105    url=https://tixy.land?code={t}
29106  rm endl
29107
29108  e[] "\n
29109------ "${g}"tixy"$n" ------------------------------------------------------------------------------------\n
29110----\n
29111---- tixy - creative code golfing: "${g}"https://tixy.land"$n"\n
29112----\n
29113---- "$r${b}"Expression:"$n" "$expr"\n
29114----\n
29115---- "$r${b}"Corresponding URL:"$n" "$url"\n
29116----\n
29117---- "$r${b}"Rules:"$n"\n
29118---- . Specified expression depends on 4 variables "${g}"(t,i,x,y)"$n" and is rendered as an animation.\n
29119---- . Variables "${g}"x"$n" and "${g}"y"$n", in range "${c}"[0,15]"$n", are the spatial position of each dot.\n
29120---- . Variable "${g}"i"$n", in range "${c}"[0,255]"$n", is the dot index, i.e. "${c}"i = x + 16*y"$n".\n
29121---- . Variable "${g}"t>=0"$n" is the (decimal) number of elapsed seconds.\n
29122---- . Specified expression "${g}"func(t,i,x,y)"$n" must return a complex value "${c}"c"$n".\n
29123---- . Variable "${g}"z"$n" can be used as a shortcut for "${g}"[ x,y ]"$n".\n
29124---- . Variable "${g}"j"$n" is the complex imaginary number "${g}"[ 0,1 ]"$n".\n
29125---- . "${c}"cabs(c)"$n" in "${g}"[0,1]"$n" determines the radius of each dot at "${c}"(x,y)"$n".\n
29126---- . "${c}"carg(c)"$n" in "${g}"[-pi,pi]"$n" determine the color of each dot at "${c}"(x,y)"$n".\n
29127----\n
29128---- Key '"${c}"SPACE"$n"' starts/stops recording animation as video file '"${c}"out.mp4"$n"'.\n
29129---- Key '"${c}"ENTER"$n"' takes a screenshot of current image.\n
29130---- Key '"${c}"A"$n"' enables/disables anti-aliasing.\n
29131---- Key '"${c}"F"$n"' displays formula on the image.\n
29132---- Key '"${c}"R"$n"' resets time.\n
29133---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
29134---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
29135---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
29136---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n
29137------------------------------------------------------------------------------------------------"
29138  w[] 400,400,0,"[G'MIC] tixy: "$expr
29139  t0=$| is_recording=0 is_antialias=1 is_screenshot=0 is_formula=0 nb_frames=0
29140  360,1,1,3,[x,abs(cos(x/w*pi))^0.5,1] hsv2rgb. shift. -180,0,0,0,2 nm. cmap
29141  strvar $expr basename=${}
29142
29143  do
29144
29145    # Manage user-events.
29146    is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
29147    if $is_ctrl" && "{*,-D} w[] {1.25*[{*,w,h}]} rmn formula,mformula
29148    elif $is_ctrl" && "{*,-C} w[] {0.8*[{*,w,h}]} rmn formula,mformula
29149    elif $is_ctrl" && "{*,R} w[] 400,400 rmn formula,mformula
29150    elif {*,-A} is_antialias={!$is_antialias}
29151    elif {*,-F} is_formula={!$is_formula}
29152    elif {*,R} t0=$|
29153    elif {*,-SPACE}
29154      if $is_recording o[] $basename.mp4,15,0,0 nb_frames=0
29155      else t0=$| fi
29156      is_recording={!$is_recording}
29157    elif {*,-ENTER} is_screenshot=1
29158    fi
29159
29160    # Render animation frame.
29161    m={min({*,w,h})} _is_antialias={$is_antialias" && "$m<512}
29162    {m=$m*($_is_antialias?2:1);[m,m]},1,3
29163
29164    16,16,1,1,">begin(
29165        const b = w#-1/16;
29166        const t = "$|-$t0";
29167        j = [ 0,1 ];
29168      );
29169      i = x + 16*y;
29170      ref([ x,y ],z);
29171      val = ("$expr");
29172      n = cabs(val);
29173      r = floor(0.48*b*cut(n,0,1));
29174      a = (360+carg(val)*180/pi)%360;
29175      c = I[#"$cmap",a];
29176      r>1?ellipse(#-1,b*(x + 0.5),b*(y + 0.5),r,r,0,1,c):
29177      r>0?ellipse(#-1,b*(x + 0.5),b*(y + 0.5),1,1,0,0.5,c)"
29178    rm.
29179
29180    if $_is_antialias r. 50%,50%,1,3,2 fi
29181    if {*,w}!={*,h} -. 128 r. {*,w,h},1,3,0,0,0.5,0.5 +. 128 fi
29182    if $is_formula
29183      if !narg($formula)
29184        0 t. {``$expr},0,0,48,1,255 frame. 5,5,0 +dilate. 11 max. 128 to_rgb..
29185        if w>0.75*{*,w} r2dx[-2,-1] {0.75*{*,w}} fi
29186        mv[-2,-1] 0 nm[0,1] formula,mformula
29187      fi
29188      j. [formula],0.5~,1~,0,0,1,[mformula],255
29189    fi
29190    if $is_screenshot o. $basename.png is_screenshot=0 fi
29191    if $is_recording o. $basename.mp4,15,0,1 nb_frames+=1 to. "Recording video\n[Frame \#"$nb_frames"]",0,0,5% fi
29192    w. rm. wait 40
29193  while {*}" && "!{*,ESC}" && "!{*,Q}
29194  rmn cmap,formula,mformula
29195  w[] 0
29196
29197#@cli x_2048
29198#@cli : Launch the 2048 game.
29199x_2048 : check_display $0
29200  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29201  e[] "\n
29202------ "${g}"2048"$n" -----------------------------------------------\n
29203----\n
29204---- Join the numbers and get to the "${g}"2048"$n" tile!\n
29205----\n
29206---- Use your "${c}"arrow keys"$n" to move the tiles. When two tiles\n
29207---- with the same number touch, they merge into one!\n
29208---- This command is a port of the '"${c}"2048"$n"' game originally\n
29209---- designed by "${c}"Gabriele Cirulli"$n", and available at:\n
29210---- "${g}"http://gabrielecirulli.github.io/2048/"$n"\n
29211----\n
29212---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29213----\n
29214--------------------------------------------------------------"
29215  l[]
29216  score=0 f3d 50 m3d 0
29217  m "_x_2048_setrandom : +==[0] 0 f. 'if(i,4*y+x,-1)' discard. -1
29218     off={i[round(u(h-1))]} rm. x={$off&3} y={$off>>2} n={if(u<0.75,1,2)}
29219     =[0] $n,$x,$y [{2+$n}] c3d.
29220     repeat 6 j3d[1] .,{78+$x*121},{190+$y*121},{10*$<},{(1+$>)/6} w[1] wait 20 done
29221     rm."
29222  m "_x_2048_object3d : +f[0] 'if(i,i*16+4*y+x,-1)' discard. -1
29223     N={h} repeat h v={-{1+$>},@$>} ++3d[{2+($v>>4)}] {$v&3},{($v>>2)&3} done
29224     +3d[-$N--1] rm.."
29225  i[0] 4,4
29226
29227  # Pre-render game canvas and numbered titles.
29228  b0=204,192,179 b1=238,228,218 b2=237,224,200 b3=242,177,121
29229  b4=245,149,99 b5=246,124,95 b6=246,94,59 b7=237,207,114
29230  c0=119,110,101 c1=249,246,242 s0=" "
29231  520,630,1,3 fc. 250,248,239
29232  t. "2048",20,10,86,1,$c0
29233  t. "Join the numbers and get to the 2048 tile!",20,90,20,1,$c0
29234  rectangle. 422,20,501,75,1,187,173,160 t. "SCORE",439,25,15,1,238,228,218
29235  repeat 12
29236    107,107,1,3 fc. ${b{min($>,7)}} +fc. ${c{$>>2}}
29237    s1={2^$>} 0 t. ${s{$>>0}},0,0,52,1,1
29238    r. ..,..,1,1,0,0,0.5,0.5 dilate_circ. 3 b. 0.5
29239    j... ..,0,0,0,0,1,.,1 rm[-2,-1]
29240  done
29241  frame_round[2--1] 10,5,1,0.5,187,173,160 frame[2--1] 7,7,187,173,160 to_rgb[2--1]
29242  r[2] 400%,400%,1,3,0,2 j[1] [2],18,130
29243  sprite3d[3--1]
29244
29245  # Run game.
29246  w[1] 100%,100%,0,"[G"{`39`}"MIC] 2048" insert_new=1
29247  repeat 2 _x_2048_setrandom done
29248  do
29249
29250    # Render game graphics at current iteration.
29251    if $insert_new
29252      _x_2048_object3d *3d. 121 j[1] [2],18,130 j3d[1] .,78,190 rm.
29253      80,25,1,3 fc. $c1 0 t. $score,0,0,25,1,1,1,1 ri. ..,0,0,0.5,0.8
29254      rectangle[1] 422,45,501,69,1,187,173,160 j[1] ..,422,45,0,0,1,. rm[-2,-1]
29255      w[1] insert_new=0
29256    fi
29257
29258    # Check for the end of the game.
29259    ++[0] 1 f. 'j(-1)==i||j(1)==i||j(0,-1)==i||j(1,0)==i||i==1'
29260    if {0,iM==11} # Game won.
29261      alert "Game Over","\nCongratulations! You got the 2048 title!\n\n   Your score: "$score,"OK"
29262      break
29263    elif !iM # Game lost.
29264      alert "Game Over","\nBad luck! You lost the game!\n\n   Your score: "$score,"OK"
29265      break
29266    fi
29267    rm.
29268
29269    # Manage user events.
29270    wait
29271    is_shift=0 um shift2048,ishift2048,vshift2048
29272    if {*,ARROWLEFT}
29273      m "shift2048:" m "ishift2048:" m "vshift2048:"
29274      is_shift=1
29275    elif {*,ARROWRIGHT}
29276      m "shift2048: rotate 180" m "ishift2048: rotate 180"
29277      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"
29278      is_shift=1
29279    elif {*,ARROWUP}
29280      m "shift2048: rotate -90" m "ishift2048: rotate 90"
29281      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"
29282      is_shift=1
29283    elif {*,ARROWDOWN}
29284      m "shift2048: rotate 90" m "ishift2048: rotate -90"
29285      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"
29286      is_shift=1
29287    fi
29288    if {*,r} w[1] 100%,100% fi
29289
29290    # Manage tile shifts and fusions.
29291    if $is_shift
29292      wait -1
29293      shift2048[0]
29294      repeat 2
29295
29296        # Tile shifts.
29297        _x_2048_object3d
29298        +s[0] y discard[-4--1] 0 y[-4--1] x r[-4--1] 4,1,1,1,0,0 a[-4--1] y
29299        +==[0,-1] insert_new={$insert_new||!im} rm.
29300        +f[0,-1] 'if(i,x,-1)' discard[-2,-1] -1 rv[-2,-1] -[-2,-1] rv[0,-2] rm..
29301
29302        if (im||iM)&&!{*,k} # Render animation for shift.
29303          /. 5 z. 0,2 y.
29304          repeat 5
29305            j.. .,0,8,0,0,-1
29306            j[1] [2],18,130 +vshift2048.. *3d. 121 j3d[1] .,78,190 rm.
29307            w[1] wait 20
29308          done
29309        fi
29310        rm[-2,-1]
29311
29312        # Tile fusions.
29313        if !$> dscore=0
29314          [0] +f[0] 'if(i,i*16+4*y+x,-1)' discard. -1
29315          repeat h
29316            x={i[$>]&3} y={(i[$>]>>2)&3} n={i[$>]>>4}
29317            if $x>0" && "{0,i($x-1,$y)}==$n
29318              =[0] 0,$x,$y =[0] {$n+1},{$x-1},$y =.. 0,$x,$y insert_new=1 dscore+={2^($n+1)}
29319            else =. -1,0,$>
29320            fi
29321          done
29322          score+=$dscore
29323
29324          if iM<0 rm[-2,-1]
29325          else # At least one tile fusions.
29326            discard. -1
29327            rv[0,-2] _x_2048_object3d rv[0,-3] vshift2048. *3d. 121 # Only tiles that do not move in this step.
29328            j[1] [2],18,130 j3d[1] .,78,190 rm[-3,-1]
29329            N={h} repeat h v={-{1+$>},@$>} ++3d[{2+($v>>4)}] {$v&3},{($v>>2)&3} done # Only tiles that move.
29330            +3d[-$N--1] rm..
29331            0 t. +$dscore,0,0,33,1,1 100%,100%,1,3 fc. $c0
29332            repeat 6  # Render animation for fusion.
29333              +vshift2048... *3d. 121 +j3d[1] .,78,190
29334              j. ...,440,{40-3*$>},0,0,{min(1,$</3)},[-4]
29335              w. rm[-2,-1] -3d... {1/6},0,0 wait 20
29336            done
29337            rm[-3--1]
29338          fi
29339
29340        fi
29341      done
29342      ishift2048[0]
29343
29344      # Insert new tile.
29345      if $insert_new _x_2048_setrandom fi
29346    fi
29347
29348  while {*}" && "!{*,ESC}" && "!{*,Q}
29349  rm w 0 endl um _x_2048_setrandom,_x_2048_object3d
29350
29351#@cli x_blobs
29352#@cli : Launch the blobs editor.
29353#@cli : ![x_blobs](../img/x_blobs.jpg)
29354x_blobs : check_display $0
29355  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29356  e[] "\n
29357------ "${g}"Blobs editor"$n" --------------------------\n
29358----\n
29359---- "${c}"Mouse"$n" to insert/move/delete blobs.\n
29360---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
29361---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
29362---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29363----\n
29364-----------------------------------------------------"
29365
29366  # Create background image [0].
29367  l[] (0;0^0;128^0;255) r. 450,450,1,3,3 flower. 30,8,0,0,50%,50%,1 water. 20
29368  w {f=h<0.5*{*,v}?1.5:1;[w,h]*=f},0,0,"[G"{`39`}"MIC] Blobs Editor"
29369
29370  # Start event loop.
29371  moving=-1
29372  do
29373    x={{*,x}*{0,w}/{*,w}}
29374    y={{*,y}*{0,h}/{*,h}}
29375    b={*,b} nearest=-1
29376    fps=${-fps}
29377
29378    # Render image of blobs and find nearest blob to mouse pointer.
29379    if $!>1
29380      {0,[w,h]},1,2
29381      repeat h#1
29382        r={1,i[2]*(1+i[3]*cos(i[4]+i[5]*$|*1000))}
29383        ellipse. {1,@0,1},$r,$r,0,1,{1,@6-7}
29384        d={sqrt(($x-{1,@0})^2+($y-{1,@1})^2)}
29385        if $d<$r nearest=$> fi
29386      shift[1] 0,-1,0,0,2 done
29387      b. 15
29388      +norm. +>=. 50 <=.. 40 *[-3,-1]
29389      +*[0,-1] rm.. rv[-2,-1] *. 1.6 c. 0,255 +[-2,-1]
29390      if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi
29391      w.
29392      if {*,CTRLLEFT}" && "{*,D} w[] {2*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi
29393      rm.
29394    else
29395      +to[0] "G\47MIC Blobs Editor",75,100,35,3,1,200,128,255
29396      to. "* Left mouse button : Create and move blobs.\n\n\
29397           * Right mouse button : Remove blob.\n\n\
29398           * Middle mouse button : Remove all blobs.\n\n\
29399           * Key 'ESC' or 'Q' : Quit.\n\n\
29400           * Colors and sizes of appearing blobs are\n   chosen randomly",\
29401           50,180,18,1,1,255
29402      w.
29403      if {*,CTRLLEFT}" && "{*,D} w[] {1.5*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi
29404      rm.
29405    fi
29406    wait 20
29407
29408    # Manage blob insertion, removal or move.
29409    if $x<0||$y<0 continue fi
29410    if $b&1
29411      if $nearest>=0" || "$moving>=0 # Move existing blob.
29412        if $moving<0 moving=$nearest fi
29413        =[1] $x,0,$moving =[1] $y,1,$moving
29414        else # Insert new blob.
29415          ($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
29416          moving={h-1}
29417        fi
29418    elif $b&2 # Remove existing blob.
29419      if $nearest>=0 l[1] s y rm[$nearest] a y endl nearest=-1 fi
29420    elif $b&4 # Remove all blobs.
29421      k[0]
29422    else
29423      moving=-1
29424    fi
29425
29426  while {*}" && "!{*,ESC}" && "!{*,Q}
29427  rm w 0 endl
29428
29429#@cli x_bouncing
29430#@cli : Launch the bouncing balls demo.
29431x_bouncing : check_display $0
29432  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29433  e[] "\n
29434------ "${g}"Bouncing balls"$n" ------------------------------\n
29435----\n
29436---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
29437---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
29438---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
29439----\n
29440-----------------------------------------------------"
29441  l[]
29442  520,320,1,3 plasma 1,1,9 n 0,220
29443  N=12
29444  repeat $N
29445    ball[] {round(u(32,80))},${-rgb}
29446    t$>={u(200)} x$>={0,u(10,w-10)} h$>={u(150,300)} vx$>={if(u<0.5,1,-1)*u(1,8)}
29447  done
29448  mv[0] $!
29449  w. {f=w<0.5*{*,u}?1.5:1;[w,h]*=f},0,"[G"{`39`}"MIC] Bouncing Balls"
29450  (0;0.7;1) r. {-2,w},70,1,1,3
29451
29452  do
29453    [$N]
29454    repeat $N
29455      bw={$>,w} bh={$>,h}
29456      y={${h$>}*abs(cos(${t$>}*pi/60))-$bh/2}
29457      dt=1
29458      if $y<0 d={-$y} y=0 bh-=$d bw+=$d dt={max(0.2,1-($d/$bh)^2)} else dt=1 fi
29459      if ${x$>}+$bw/2>w
29460        d={${x$>}+$bw/2-w} bw-=$d bh+={0.5*$d}
29461        if ${x$>}+$bw/4>w vx$>={-${vx$>}} fi
29462      fi
29463      if ${x$>}-$bw/2<0
29464        d={$bw/2-${x$>}} bw-=$d bh+={0.5*$d}
29465        if ${x$>}-$bw/4<0 vx$>={-${vx$>}} fi
29466      fi
29467      +r[$>] $bw,$bh,1,4,3 s. c,-3
29468      j... ..,{max(0,min({$N,w-$bw},${x$>}-$bw/2))},{{$N,h}-{h}-$y-70},0,0,1,.,255 rm[-2,-1]
29469      t$>+=$dt
29470      x$>+={$dt*${vx$>}}
29471    done
29472
29473    +rows. {h-2*70},{h-1-70} mirror. y *. [{$N+1}]
29474    j.. .,0,{-2,h-71},0,0,0.5 rm.
29475    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi
29476    if {*,CTRLLEFT}" && "{*,D} w[] {1.5*w},{1.5*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
29477    w. rm. wait 20
29478  while {*}" && "!{*,ESC}" && "!{*,Q}
29479  w 0 rm endl
29480
29481#@cli x_color_curves : _colorspace={ rgb | cmy | cmyk | hsi | hsl | hsv | lab | lch | ycbcr | last }
29482#@cli : Apply color curves on selected RGB[A] images, using an interactive window.
29483#@cli : Set 'colorspace' to 'last' to apply last defined color curves without opening interactive windows.
29484#@cli : Default value: 'colorspace=rgb'.
29485x_color_curves : skip ${1=rgb}
29486  if ['"$1"']!='last'&&!{*,u} error[0--3] "Command '$0': No display available." return fi
29487  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29488  e[^-1] "Apply color curves of image$?, in the '$1' colorspace."
29489  if ['"$1"']=='last'
29490    if !narg($_xcc_colorbase) return fi
29491    __x_color_curves[] $_xcc_colorbase
29492  else
29493    e[] "\n
29494------------------------------------------------------------------------------------------------\n
29495----\n
29496---- "${c}"Left mouse button"$n" on a curve creates a new control point (or moves an existing one).\n
29497---- "${c}"Right mouse button"$n" on a control point deletes it.\n
29498---- "${c}"Left mouse button"$n" on the main image window shows the initial image until button is released.\n
29499---- "${c}"Right mouse button"$n" on the main image window adds a keypoint to all curves from picked color.\n
29500---- Key '"${c}"R"$n"' on a curve resets it.\n
29501---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
29502---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
29503---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
29504---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n
29505----\n
29506------------------------------------------------------------------------------------------------"
29507    __x_color_curves[] $1 _xcc_colorbase=$1
29508  fi
29509
29510  to_color repeat $! l[$>]
29511
29512    if ['"$1"']!='last' # Open interactive windows to set color curves.
29513      +r[0] ${fitscreen[]\ {0,[w,h,1]},128,70%},1,100%,3
29514      +l. # Compute additional info for each image channel (histogram and color axis).
29515        xcc_goto s c histogram 256,0,255 xcc_info
29516      endl
29517      __C0= __C1= __C2= __C3= __C4=
29518      if narg($__xcc_C0) __C0=$__xcc_C0 fi
29519      if narg($__xcc_C1) __C1=$__xcc_C1 fi
29520      if narg($__xcc_C2) __C2=$__xcc_C2 fi
29521      if narg($__xcc_C3) __C3=$__xcc_C3 fi
29522      if narg($__xcc_C4) __C4=$__xcc_C4 fi
29523
29524      x={1,({*,u}-560-w)/2} y={1,({*,v}-h)/2}
29525      if $!==5 # 3 channels.
29526        parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d... __C0,"$_color0"",\
29527                 "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d.. __C1,"$_color1"",\
29528                 "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d. __C2,"$_color2"",\
29529                 "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-4]"
29530      elif $!==6 # 4 channels.
29531        parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d[-4] __C0,"$_color0"",\
29532                 "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d... __C1,"$_color1"",\
29533                 "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d.. __C2,"$_color2"",\
29534                 "w[] 256,256,0,0,"{$x+280}","{$y+300}",\"Curve: "$_title3"\" x_select_function1d. __C3,"$_color3"",\
29535                 "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-5]"
29536      elif $!==7 # 5 channels.
29537        parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d[-5] __C0,"$_color0"",\
29538                 "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d[-4] __C1,"$_color1"",\
29539                 "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d... __C2,"$_color2"",\
29540                 "w[] 256,256,0,0,"{$x+280}","{$y+300}",\"Curve: "$_title3"\" x_select_function1d.. __C3,"$_color3"",\
29541                 "w[] 256,256,0,0,"{$x+280}","{$y+600}",\"Curve: "$_title4"\" x_select_function1d. __C4,"$_color4"",\
29542                 "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-6]"
29543      fi
29544      k[0]
29545    fi
29546
29547    # Apply color curves on fullres image.
29548    xcc_goto
29549    repeat s function1d[] 1,${__xcc_C$>} *. {255%} r. 256,1,1,1,5 c. 0,255 sh[0] $> map. .. rm[-2,-1] done
29550    xcc_backto
29551
29552  endl done
29553  um xcc_goto,xcc_backto,xcc_info
29554
29555_x_color_curves :
29556  title={0,b} if narg({'{0,x}'}) title=$title.{0,x} fi ('$title') discard. {'~'} title={t} rm.
29557  +drgba. w. 100%,100%,0,"[G"{`39`}"MIC] Image: "$title rm.
29558  xcc_goto. .
29559
29560  oC0= oC1= oC2= oC3= oC4= viewmode=0
29561  do
29562    wait 100 need_refresh=0
29563
29564    # Manage user events.
29565    oviewmode=$viewmode is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}} x={*,x} y={*,y}
29566    if {*,r} need_refresh=1 # Window resize.
29567    elif $is_ctrl" && "{*,-D} w[] {{*,w}*125%},{{*,h}*125%} need_refresh=1 # Increase window size.
29568    elif $is_ctrl" && "{*,-C} w[] {{*,w}*80%},{{*,h}*80%} need_refresh=1 # Decrease window size.
29569    elif $is_ctrl" && "{*,-R} w[] {w},{h} need_refresh=1 # Reset window size.
29570    elif {*,b}&1 viewmode={x={*,x};if(x<w/3,1,if(x<2*w/3,2,3))} # Change viewmode.
29571    elif {*,b}&2" && "$x>=0" && "$y>=0 # Add control point from picked color.
29572      xc={$x*w/{*,w}} yc={$y*h/{*,h}} +z[0] $xc,$yc,$xc,$yc
29573      repeat s (${__C$>},{i[$>]/255%},{i[$>]/255%}) r. 2,{w/2},1,1,-1 sort. +,y __C$>={^} rm. done
29574      rm. wait -1
29575    else viewmode=0
29576    fi
29577    need_refresh={$need_refresh||$oviewmode!=$viewmode}
29578
29579    # Update result.
29580    repeat s if ['_${oC$>}']!=['_${__C$>}'] # Channel must be updated.
29581      function1d[] 1,${__C$>} *. {255%} r. 256,1,1,1,5 c. 0,255
29582      +channels[0] $> map. .. j[1] .,0,0,0,$> rm[-2,-1]
29583      need_refresh=1 oC$>=${__C$>}
29584    fi done
29585
29586    # Display view.
29587    if $need_refresh
29588      if $viewmode==0 # Modified view.
29589        +xcc_backto[1]
29590      elif $viewmode%2 # Split view.
29591        w2={0,int(w/2)} b={$viewmode==1} +z[{!$b}] 0,{$w2-1} +z[$b] $w2,100%
29592        xcc_backto.. xcc_backto. a[-2,-1] x line. 50%,0,50%,100%,1,0
29593      else # Original view.
29594        +xcc_backto[0]
29595      fi
29596      if s>3 drgba. fi w. rm. refresh=0
29597    fi
29598
29599  while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,SPACE}" && "!{*,ENTER}
29600  w 0
29601
29602  # Transfer curves to output variable and request curve widgets to close.
29603  repeat 5 if narg(${__C$>}) __xcc_C$>=${__C$>} __C$>=-1 fi done
29604
29605# Define colorspace conversion functions.
29606__x_color_curves :
29607  if ['"$1"']=='rgb'
29608    _color0="255,180,180" _color1="180,255,180" _color2="180,180,255" _color3="220,220,220"
29609    _title0=Red _title1=Green _title2=Blue _title3=Alpha
29610    m "xcc_goto:" m "xcc_backto:"
29611    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
29612       a[0,-3] y a[1,-2] y a[2,-1] y"
29613  elif ['"$1"']=='cmy'
29614    _color0="180,255,255" _color1="255,180,255" _color2="255,255,100" _color3="220,220,220"
29615    _title0=Cyan _title1=Magenta _title2=Yellow _title3=Alpha
29616    m "xcc_goto: s c,-3 rgb2cmy[0] a c" m "xcc_backto: s c,-3 cmy2rgb[0] a c"
29617    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
29618       a[0,-3] y a[1,-2] y a[2,-1] y"
29619  elif ['"$1"']=='cmyk'
29620    _color0="180,255,255" _color1="255,180,255" _color2="255,255,100" _color3="180,180,180" _color4="220,220,220"
29621    _title0=Cyan _title1=Magenta _title2=Yellow _title3=Key _title4=Alpha
29622    m "xcc_goto: s c,-3 rgb2cmyk[0] a c" m "xcc_backto: s c,-4 cmyk2rgb[0] a c"
29623    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
29624       a[0,-4] y a[1,-3] y a[2,-2] y a[3,-1] y"
29625  elif ['"$1"']=='hsi'
29626    _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220"
29627    _title0=Hue _title1=Saturation _title2=Intensity _title3=Alpha
29628    m "xcc_goto: s c,-3 rgb2hsi8[0] a c" m "xcc_backto: s c,-3 hsi82rgb[0] a c"
29629    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))
29630       hsi82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
29631  elif ['"$1"']=='hsl'
29632    _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220"
29633    _title0=Hue _title1=Saturation _title2=Lightness _title3=Alpha
29634    m "xcc_goto: s c,-3 rgb2hsl8[0] a c" m "xcc_backto: s c,-3 hsl82rgb[0] a c"
29635    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))
29636       hsl82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
29637  elif ['"$1"']=='hsv'
29638    _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220"
29639    _title0=Hue _title1=Saturation _title2=Value _title3=Alpha
29640    m "xcc_goto: s c,-3 rgb2hsv8[0] a c" m "xcc_backto: s c,-3 hsv82rgb[0] a c"
29641    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))
29642       hsv82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
29643  elif ['"$1"']=='lab'
29644    _color0="180,180,180" _color1="220,180,220" _color2="220,220,180" _color3="220,220,220"
29645    _title0=Lightness _title1=Chroma-A _title2=Chroma-B _title3=Alpha
29646    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"
29647    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))
29648       lab82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
29649  elif ['"$1"']=='lch'
29650    _color0="180,180,180" _color1="220,180,220" _color2="255,220,220" _color3="220,220,220"
29651    _title0=Lightness _title1=Chroma _title2=Hue _title3=Alpha
29652    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
29653       rgb2srgb[0] a c"
29654    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))
29655       lch82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
29656  elif ['"$1"']=='ycbcr'
29657    _color0="180,180,180" _color1="220,220,255" _color2="255,220,220" _color3="220,220,220"
29658    _title0=Luma _title1=Blue\ chroma _title2=Red\ chroma _title3=Alpha
29659    m "xcc_goto: s c,-3 rgb2ycbcr[0] a c" m "xcc_backto: s c,-3 ycbcr2rgb[0] a c"
29660    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))
29661       ycbcr2rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y"
29662  else error[0--3] "Command 'x_color_curves': Unknown specified color space '$1'."
29663  fi
29664
29665#@cli x_colorize : _is_lineart={ 0 | 1 },_max_resolution={ 0 | >=128 },_multichannels_output={ 0 | 1 },\
29666# _[palette1],_[palette2],_[grabber1]
29667#@cli : Colorized selected B&W images, using an interactive window.
29668#@cli : When >0, argument 'max_resolution' defines the maximal image resolution used in the interactive window.
29669#@cli : Default values: 'is_lineart=1', 'max_resolution=1024' and 'multichannels_output=0'.
29670x_colorize : skip ${1=0},${3=0},${4=0},${5=0},${6=0} check "${2=1024}==0 || $2>=128" check_display $0
29671  s0="image" s1="lineart" s2="multichannel" s3="merged" use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29672  e[^-1] "Colorize selected B&W "${s{!$1}}"$? interactively, with maximum resolution $2 and "${s{2+!$3}}" output."
29673  e[] "\n
29674--------------------------------------------------------------------------------------\n
29675----\n
29676---- "${c}"Left mouse button"$n" creates a new colored control point (or moves an existing one).\n
29677---- "${c}"Right mouse button"$n" or key '"${c}"X"$n"' over a control point deletes it.\n
29678---- "${c}"Right mouse button"$n" or key '"${c}"P"$n"' anywhere else picks a color from the image.\n
29679---- "${c}"Mouse wheel"$n", or keys '"${c}"CTRL+arrows UP/DOWN"$n"' zoom view in/out.\n
29680---- '"${c}"CTRL+mouse wheel"$n"', '"${c}"SHIFT+mouse wheel"$n"' or "${c}"arrow keys"$n" move image in zoomed view.\n
29681---- Key '"${c}"SPACE"$n"' updates the extrapolated color field.\n
29682---- Key '"${c}"TAB"$n"' toggles between markers view modes.\n
29683---- Key '"${c}"BACKSPACE"$n"' deletes the last control point added.\n
29684---- Key '"${c}"PAGE UP"$n"' increases image contrast.\n
29685---- Key '"${c}"PAGE DOWN"$n"' decreases image contrast.\n
29686---- Key '"${c}"R"$n"' toggles color replace mode.\n
29687---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
29688---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
29689---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
29690---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n
29691----\n
29692--------------------------------------------------------------------------------------"
29693  N=$!
29694  thread_main="_x_colorize[0] ${1--1}"
29695  thread_color="w[] 400,320,0,\"Palette: main\" x_select_color[] __color,255,255,255"
29696
29697  is_palette1=${"is_image_arg[] $4"}
29698  if $is_palette1
29699    pass$4 1 ('{b}') discard. {'~'} palette_title1={t} rm.
29700    thread_palette1="w[] 400,400,0,\"Palette: "$palette_title1"\" x_select_palette["{$!-1}"] __color"
29701  fi
29702
29703  is_palette2=${"is_image_arg[] $5"}
29704  if $is_palette2
29705    pass$5 1 ('{b}') discard. {'~'} palette_title2={t} rm.
29706    thread_palette2="w[] 400,400,0,\"Palette: "$palette_title2"\" x_select_palette["{$!-1}"] __color"
29707  fi
29708
29709  is_grabber=${"is_image_arg[] $6"}
29710  if $is_grabber
29711    pass$6 1 ('{b}') discard. {'~'} palette_grabber={t} rm.
29712    thread_grabber="w[] ${\"fitscreen[] {[w,h,1]},128,50%\"},0,\"Grabber: "$palette_grabber"\"
29713                    x_grab_color["{$!-1}"] __color"
29714  fi
29715
29716  __color=255,255,255
29717
29718  if !$is_palette1" && "!$is_palette2" && "!$is_grabber
29719    repeat $N l[$>]
29720      parallel $thread_main,$thread_color
29721    endl done
29722  else
29723    repeat $N l[$>,$N--1]
29724      parallel $thread_main,$thread_color,$thread_palette1,$thread_palette2,$thread_grabber
29725    endl done
29726  fi
29727  k[0-{$N-1}]
29728
29729_x_colorize :
29730
29731  # Init variables and images.
29732  name={n} title={b} if narg({x}) title=$title.{x} fi
29733  w={w} h={h}
29734
29735  if $1 # Line-art.
29736    if s==4 sh. 3 if abs(im-iM)>64 +*. -1 rm.. +. 255 else rm. sh. 0 fi
29737    else sh. 0
29738    fi
29739    n 0,255
29740    else # Regular image.
29741    if s==1 sh. 0
29742    else +luminance.
29743    fi
29744  fi
29745
29746  nm. img
29747
29748  fdim=${fitscreen[]\ $w,$h} ww={arg(1,$fdim)} wh={arg(2,$fdim)} x0=0 y0=0 x1={w-1} y1={h-1}
29749  selection=-1 view_markers=2 contrast=9 xpan=-1 ypan=-1 replace_color= current_replace_color=
29750
29751  if narg($_gui_control_points)>=6 # Import list of control points from plug-in GUI.
29752    ($_gui_control_points) r. {w/6},6,1,1,-1
29753  else 0 # Empty list of control points.
29754  fi
29755  nm. points
29756
29757  # Compute potential map.
29758  if $2>0 if $w>$h +r2dx[img] {min($2,$w)},2 else +r2dy[img] {min($2,$h)},2 fi else [img] fi
29759  __x_colorize. $1
29760  pw={potential,w} ph={potential,h}
29761
29762  # Start event loop.
29763  do
29764
29765    # Handle user events for zoom/navigation/resizing.
29766    if narg($replace_color)" && "{*,x}<0" && "{*,y}<0 wait 200 else wait fi
29767    x={*,x} y={*,y} b={*,b} o={*,-o}
29768    is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
29769    is_shift={{*,SHIFTLEFT}" || "{*,SHIFTRIGHT}}
29770    is_mouseout={$x<0" || "$y<0}
29771    x={$x0+$x*($x1-$x0+1)/$ww} y={$y0+$y*($y1-$y0+1)/$wh}
29772    oww=$ww owh=$wh ox0=$x0 oy0=$y0 ox1=$x1 oy1=$y1
29773
29774    if {*,r} # When window resized.
29775      nww={*,d} nwh={*,e} m={min($nww,$nwh)}
29776      cx={($x0+$x1)/2} cy={($y0+$y1)/2} dx={$nww*($x1-$x0+1)/$ww} dy={$nwh*($y1-$y0+1)/$wh}
29777      x0={$cx-$dx/2} x1={$cx+$dx/2}
29778      y0={$cy-$dy/2} y1={$cy+$dy/2}
29779      ww=$nww wh=$nwh
29780    elif $is_ctrl" && "{*,-D} # Increase window size.
29781      nww={min({*,u},$ww*1.25)} nwh={min({*,v},$wh*1.25)} m={min($nww,$nwh)}
29782      if $m==$nww ww=$m wh={$h*$m/$w} else ww={$w*$m/$h} wh=$m fi
29783    elif $is_ctrl" && "{*,-C} # Decrease window size.
29784      nww={$ww/1.25} nwh={$wh/1.25}
29785      if min($nww,$nwh)>=64 ww=$nww wh=$nwh fi
29786    elif $is_ctrl" && "{*,R} # Reset window size.
29787      fdim=${fitscreen[]\ $w,$h} ww={arg(1,$fdim)} wh={arg(2,$fdim)}
29788      x0=0 y0=0 x1={$w-1} y1={$h-1}
29789    elif ($is_shift" && "$o<0)" || "{*,ARROWLEFT} # Go left.
29790      dx={($x1-$x0)/6} x0-=$dx x1-=$dx
29791    elif ($is_shift" && "$o>0)" || "{*,ARROWRIGHT} # Go right.
29792      dx={($x1-$x0)/6} x0+=$dx x1+=$dx
29793    elif ($is_ctrl" && "$o>0)" || "({*,ARROWUP}" && "!$is_ctrl) # Go up.
29794      dy={($y1-$y0)/6} y0-=$dy y1-=$dy
29795    elif ($is_ctrl" && "$o<0)" || "({*,ARROWDOWN}" && "!$is_ctrl) # Go down.
29796      dy={($y1-$y0)/6} y0+=$dy y1+=$dy
29797    elif $o>0" || "($is_ctrl" && "{*,ARROWUP}) # Zoom in.
29798      if $x1-$x0>16" && "$y1-$y0>16
29799        cx={if($x>=0" && "!{*,ARROWUP},$x,($x0+$x1)/2)}
29800        cy={if($y>=0" && "!{*,ARROWUP},$y,($y0+$y1)/2)}
29801        x0={$cx+($x0-$cx)*0.75} y0={$cy+($y0-$cy)*0.75}
29802        x1={$cx+($x1-$cx)*0.75} y1={$cy+($y1-$cy)*0.75}
29803      fi
29804    elif $o<0" || "($is_ctrl" && "{*,ARROWDOWN}) # Zoom out.
29805      zfactor={max(($x1-$x0+1)/$w,($y1-$y0+1)/$h)}
29806      if $zfactor<1.3
29807        cx={if($x>=0" && "!{*,ARROWDOWN},$x,($x0+$x1)/2)}
29808        cy={if($y>=0" && "!{*,ARROWDOWN},$y,($y0+$y1)/2)}
29809        x0={$cx+($x0-$cx)/0.75} y0={$cy+($y0-$cy)/0.75}
29810        x1={$cx+($x1-$cx)/0.75} y1={$cy+($y1-$cy)/0.75}
29811        dx={$zfactor^2*($w-$x0-$x1)/2} dy={$zfactor^2*($h-$y0-$y1)/2}
29812        x0+=$dx x1+=$dx y0+=$dy y1+=$dy
29813      else
29814        dx={($w-$x0-$x1)/2} dy={($h-$y0-$y1)/2}
29815        x0+=$dx x1+=$dx y0+=$dy y1+=$dy
29816      fi
29817    elif $b&4" && "!$is_mouseout # Pan.
29818      if $panx<0" && "$pany<0 panx=$x pany=$y
29819      else dx={round($panx-$x)} dy={round($pany-$y)} x0+=$dx y0+=$dy x1+=$dx y1+=$dy
29820      fi
29821    else panx=-1 pany=-1
29822    fi
29823    if $ww!=$oww" || "$wh!=$owh" || "$ox0!=$x0" || "$oy0!=$y0" || "$ox1!=$x1" || "$oy1!=$y1 rm[baseview] fi
29824
29825    # Handle events related to control points management.
29826    N={points,w}
29827    if narg($baseview)" && "($b&3" || "{*,X}" || "{*,P})" && "$x>=0" && "$y>=0" && "$x<$w" && "$y<$h
29828      if $selection==-1" && "$N # Check for selection of an existing point.
29829        ($x;$y) r. $N,2 -. [points] *. {max($ww,$wh)/max($x1-$x0,$y1-$y0)} sqr. s. y +[-2,-1]
29830        dmin={im} selection={if($dmin>25,-1,xm)} rm.
29831      fi
29832      if narg($replace_color) # Go back from 'Replace color' mode.
29833        replace_color= wait -1
29834      elif $selection>=0
29835        if $b&1" && "$view_markers # Move existing point.
29836          +columns[points] $selection ox={i[0]} oy={i[1]} =. $x =. $y,0,1 j[points] .,$selection rm.
29837          rm[view]
29838        elif ($b&2" || "{*,X})" && "$view_markers # Remove existing point.
29839          if $N>1 +z[points] {$selection+1},100% j[points] .,$selection rm. r[points] {$N-1},100%,1,1,0
29840          else rm[points] 0 nm. points fi
29841          wait -1 rm[view]
29842        fi
29843      elif $b&1 # Add new point
29844       ($x;$y;0) ($__color) y. y +. 1 a[-2,-1] y a[points,-1] x selection=$N
29845       if !$view_markers view_markers=2 fi
29846       rm[view]
29847      elif $b&2" || "{*,P} # Pick color from image.
29848        __color={colors,I($x*$pw/$w,$y*$ph/$h)}
29849      fi
29850    else selection=-1
29851      if {*,-SPACE}" && "narg($colors) replace_color= rm[colors] # Update color map.
29852      elif {*,-TAB} view_markers={($view_markers-1)%3} rm[view] wait -1 # Toggle markers.
29853      elif !$is_ctrl" && "{*,-R}  # Switch color replace mode.
29854        if narg($replace_color) replace_color= else replace_color=$__color fi
29855        rm[baseview] wait -1
29856      elif {*,PAGEDOWN} contrast={max(0,$contrast-1)} rm[view] wait -1 # Decrease contrast.
29857      elif {*,PAGEUP} contrast={min(9,$contrast+1)} rm[view] wait -1 # Increase contrast.
29858      elif {*,BACKSPACE}" && "$N # Remove last point.
29859        if $N>1 z[points] 0,{$N-2} else i=$points rm[points] i[$i] 0 nm[$i] points fi
29860        rm[view] wait -1
29861      fi
29862    fi
29863
29864    # Manage zoomed view bounds.
29865    w2={round(($x1-$x0)/2)} h2={round(($y1-$y0)/2)}
29866    if $x0<-$w2 x1-={$x0+$w2} x0=-$w2 fi
29867    if $y0<-$h2 y1-={$y0+$h2} y0=-$h2 fi
29868    if $x1>=$w+$w2 x0+={$w-1+$w2-$x1} x1={$w-1+$w2} fi
29869    if $y1>=$h+$h2 y0+={$h-1+$h2-$y1} y1={$h-1+$h2} fi
29870
29871    # Render color map.
29872    if !narg($colors)
29873      N={points,w}
29874      if narg($view) to[view] "Processing...",5,5,20,2 w[view] fi
29875      if $N
29876        [points]
29877        sh. 0,0,0,0 *. {$pw/$w} rm.
29878        sh. 1,1,0,0 *. {$ph/$h} rm.
29879        pointcloud. -1,$pw,$ph
29880
29881        # Additional term that depends on marker's positions.
29882        +compose_channels. max !=. 0 distance. 1 *. 0.02 +. 1 ^. -1 +. [potential]
29883        if !$1 dilate.. 3 fi
29884        watershed.. . rm. -. 1
29885      else [potential],[potential],1,3,255
29886      fi
29887      nm. colors
29888      if narg($baseview) rm[baseview] fi
29889    fi
29890
29891    # Manage replace color mode.
29892    if !narg($replace_color)" && "narg($points_replaced)
29893      rm[points,colors,view] nm[colors_replaced] colors nm[points_replaced] points current_replace_color=
29894    elif narg($replace_color)" && "['$__color']!=['$current_replace_color']
29895      if narg($colors_replaced) rm[colors_replaced,points_replaced] fi
29896      current_replace_color=$__color
29897      if {points,w}
29898        +replace_color[colors] 0,0,$replace_color,$current_replace_color
29899        +rows[points] 3,5 permute. xzcy -. 1
29900        replace_color. 0,0,$replace_color,$current_replace_color
29901        +. 1 permute. xcyz +j[points] .,0,3 rm..
29902      else 0 0
29903      fi
29904      nm.. colors_replaced
29905      nm. points_replaced
29906      if narg($baseview) rm[baseview] fi
29907    fi
29908
29909    # Render base image.
29910    if !narg($baseview)
29911      nx0={$x0*$pw/$w} ny0={$y0*$ph/$h}
29912      nx1={$x1*$pw/$w} ny1={$y1*$ph/$h}
29913      +z[img] $x0,$y0,$x1,$y1
29914      r. $ww,$wh,1,100%,{if($ww<w" && "$wh<h,2,1)}
29915      if narg($replace_color)" && "{points,w} icolors=$colors_replaced else icolors=$colors fi
29916      +z[$icolors] $nx0,$ny0,$nx1,$ny1
29917      r. $ww,$wh,1,100%,{if($ww<w" && "$wh<h,2,3)}
29918      if $1 *.. -1 +.. 255 channels.. -3,0 blend. ..,alpha rm..
29919      else rgb2ycbcr. j. ..,0,0,0,0 rm.. ycbcr2rgb.
29920      fi
29921      nm. baseview
29922      if narg($view) rm[view] fi
29923    fi
29924
29925    # Render view.
29926    if !narg($view)
29927      [baseview] r. 100%,100%,1,3
29928      if $contrast<9 /. {10-$contrast} +. {128*(1-1/(10-$contrast))} fi
29929      if $view_markers
29930        if $view_markers==2 rad1=5 rad2=3 else rad1=3 rad2=2 fi
29931        if narg($replace_color)" && "{points,w} ipoints=$points_replaced else ipoints=$points fi
29932        repeat w#$ipoints
29933          +columns[$ipoints] $>
29934          x={(i[0]-$x0)*$ww/(1+$x1-$x0)}
29935          y={(i[1]-$y0)*$wh/(1+$y1-$y0)}
29936          col={i[3]-1},{i[4]-1},{i[5]-1}
29937          rm.
29938          circle. $x,$y,$rad1,1,0 circle. $x,$y,$rad2,1,$col
29939        done
29940      fi
29941
29942      if narg($replace_color)
29943        to. "Replace      by",5,5,20,2
29944        rectangle. 80,8,111,25,1,0 rectangle. 82,10,109,23,1,$replace_color
29945        rectangle. 150,8,181,25,1,0 rectangle. 152,10,179,23,1,$current_replace_color
29946      fi
29947
29948      nm. view
29949      w[view] $ww,$wh,0,$title
29950    fi
29951
29952  while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER}
29953
29954  # Recompute colors at full resolution.
29955  if narg($view) to[view] "Processing fullres...",5,5,20,2 w[view] fi
29956  k[0,img,points]
29957  N={points,w} status=
29958  if $N
29959    status={points,^}
29960    [img] __x_colorize. $1
29961    pointcloud[points] -1,$w,$h
29962    +compose_channels[points] max !=. 0 distance. 1 *. 0.02 +. 1 ^. -1 +[potential,-1]
29963    if !$1 zfact={{img,max(w,h)}/{potential,max(w,h)}} dilate[points] {int(3*$zfact)} fi
29964    watershed[points] [potential] -[points] 1 nm[points] colors
29965  else [img],[img],1,3,255 nm. colors
29966  fi
29967
29968  if $3 # Multichannels output.
29969    k[0,colors] a c
29970  else # Merge for single layer output.
29971    k[0,img,colors]
29972    if $1 +*[img] -1 +. 255 channels. -3,0 blend[colors,-1] alpha rm[0,img]
29973    else rgb2ycbcr[colors] j[colors] [img],0,0,0,0 rm[0,img] ycbcr2rgb[colors]
29974    fi
29975  fi
29976  a c nm $name
29977
29978  __color=-1 # Force color selectors to close.
29979  u $status  # Return control points.
29980  w 0
29981
29982# Compute potential function.
29983__x_colorize :
29984  if $1 # Potential for lineart.
29985    n. 0,1 ^. 5 repeat 4 +b. 0.5% min done
29986  else  # Potential for generic grayscale image.
29987    gradient_norm. n. 0,255 normalize_local. 3,3 *. -1 n. 0,255
29988    b. 0.05% n. 0,1 sqr. +b. 0.5% n[-2,-1] 0,1 min[-2,-1]
29989  fi
29990  nm. potential
29991
29992#@cli x_connect4
29993#@cli : Launch the Connect Four game.
29994x_connect4 : check_display $0
29995  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
29996  e[] "\n
29997------ "${g}"Connect Four"$n" --------------------------------------------\n
29998----\n
29999---- Connect four tokens in a row, column or diagonally\n
30000---- to win the game.\n
30001----\n
30002---- "${c}"Left mouse button"$n" on a column inserts a new token.\n
30003---- Keys '"${c}"SPACE"$n"' or '"${c}"ENTER"$n"' lets the computer play the turn\n
30004---- (or restart game when it's over).\n
30005---- Key '"${c}"ENTER"$n"' also enables autoplay for the current player.\n
30006---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' close the window.\n
30007----\n
30008----------------------------------------------------------------"
30009  l[]
30010
30011  # Create sprite graphics.
30012  7,6 nm. board
30013  R={board,u={*,u};v={*,v};int(0.5*min(u/w,v/h))}
30014
30015  {2*$R},{2*$R} circle. 50%,50%,32%,1,1 b. 2% g. xy +[-2,-1] n. 0,1
30016  +n. -1,0.5 abs. negate. +f. 200 rv[-3--1] a[-3--1] c hsv2rgb.
30017  to_rgba. circle. 50%,50%,27%,1,0
30018
30019  {4*$R},{4*$R} circle. 50%,50%,20%,1,1 b. 1% g. xy +[-2,-1] n. 0,1 negate.
30020  pow. 0.5 +n. -1,0.8 abs. negate. +f. 10 rv[-3--1] a[-3--1] c
30021  . sh. 0 f. 60 rm. hsv2rgb[-2,-1]
30022  100%,100% circle. 50%,50%,30%,1,255 a[-3,-2] .,c rm.
30023
30024  r2dx[-3--1] $R s[-3--1] c,-3 rm...
30025  n[^0] 0,255 round[^0] nm[^0] cache,cachem,token0,token1,tokenm
30026  +b[cachem] 2% shift. 1%,1%,0,0,2 max[cachem,-1]
30027  r[cache,cachem] 100%,{board,h*100}%,1,100%,0,2
30028
30029  # Define board evaluation function.
30030  evalf="const op = 3 - p;
30031         case(dx,dy) = (
30032           pgood = pbad = 0;
30033           for (k = -2, k<2, ++k,
30034             X = x + k*dx;
30035             Y = y + k*dy;
30036             if (X>=0 && X<w && Y>=0 && Y<h,
30037               val = i(X,Y);
30038               if (pgood>=0 && val==op, pgood = -1, pgood+=!!val);
30039               if (pbad>=0 && val==p,  pbad = -1, pbad+=!!val);
30040             );
30041           );
30042           pgood = arg(2+pgood,0,1,2,4,100,1e8);
30043           pbad = arg(2+pbad,0,1,2,4,10000,1e8);
30044           pgood - pbad;
30045         );
30046         case(1,0) + case(0,1) + case(1,1) + case(1,-1)"
30047
30048  # Start game.
30049  do
30050
30051    # Initialize game.
30052    if !narg($visu)
30053      {board,[w,h]*$R},1,3,64
30054      repeat h#$board,y repeat w#$board,x
30055        val={board,i($x,$y)} if $val j. [token{$val-1}],{$x*$R},{$y*$R},0,0,1,[tokenm],255 fi
30056      done done
30057      +r[cache,cachem] {board,[w,h]*$R},1,100%,0,2 a[-2,-1] c blend[-2,-1] alpha
30058      nm. visu w[visu] 100%,100%,0,"[G'MIC] Connect Four"
30059      turn=0 is_falling=0 x=-1 yv=0 dyv=0 winner=
30060      autoplayer0=0 autoplayer1=0
30061    fi
30062
30063    # Estimate ymax for each column.
30064    if !narg($ymax)
30065      {board,w},1,1,1,"y = -1; repeat (h#"$board",k, if(!i(#"$board",x,k),y = k)); y" nm. ymax
30066      if iM<0 winner=-1,-1,-1 fi # Draw game
30067    fi
30068
30069    if narg($winner) # End of game animation
30070      [visu] nm. tmpvisu
30071      x={arg(1,$winner)} y={arg(2,$winner)} c={arg(3,$winner)}
30072      if $x<0 # Draw game
30073        if !narg($text)
30074          0 t. "Draw game",0,0,57,1,1 r2dx. {tmpvisu,w/2} expand_xy. 3,0 +dilate. 5 n.. 0,255 to_rgb..
30075          nm[-2,-1] text,textm
30076        fi
30077        j[tmpvisu] [text],{tmpvisu,([w,h]-[w#$text,h#$text])/2},0,0,{0.7+0.3*sin(5*$|)},[textm]
30078      else
30079        repeat 4
30080          [token$turn]
30081          rgb2hsv. sh. 1,2 +. {0.4*sin(5*$|)} c. 0,1 rm. hsv2rgb.
30082          j. [cache],0,0,0,0,1,[cachem],255
30083          j[tmpvisu] .,{$R*[$x,$y]},0,0,1 rm.
30084          x+={$c!=2?1:0} y+={$c==1?0:$c==4?-1:1}
30085        done
30086      fi
30087      w[tmpvisu] 100%,100% rm[tmpvisu] wait 20
30088
30089      if {*,-SPACE}" || "{*,-ENTER} # Restart new game
30090        rm[visu,ymax] f[board] 0 winner=
30091      fi
30092
30093    elif !$is_falling # Manage column selection
30094
30095      if !${autoplayer$turn}
30096        x={visu,X={*,x};X<0?X:int(X*w#$board/w)}
30097        yM={i("#"$ymax,$x)}
30098        if {board,$x<0" || "$x>=w} w[visu] 100%,100%
30099        else
30100          [visu] $R,100%,1,3,($yM>=0?1:0)*($turn?[255,255,0]:[255,0,0])
30101          j.. .,{$x*$R},0,0,0,{$yM>=0?0.15:0.3} rm.
30102          w. 100%,100% rm.
30103        fi
30104        wait
30105      fi
30106
30107      if {*,-b}&1" && "$yM>=0" && "$x>=0 is_falling=1 yv=0 dyv=1 # Manual play
30108      elif ${autoplayer$turn}" || "{*,-SPACE}" || "{*,ENTER} # Computer play
30109        if {*,-ENTER} autoplayer$turn=1 fi
30110        max_score=-inf max_col=
30111        repeat w#$board,move1
30112          yM1={i("#"$ymax,$move1)}
30113          if $yM1>=0
30114            +=[board] {1+$turn},$move1,$yM1 nm. board1
30115            {board,w},1,1,1,"y = -1; repeat (h#"$board1",k, if(!i(#"$board1",x,k),y = k)); y" nm. ymax1
30116            opp_max_score=-inf opp_max_board={board,^}
30117            opp_turn={($turn+1)%2}
30118            repeat w#$board,move2
30119              yM2={i("#"$ymax1,$move2)}
30120              if $yM2>=0
30121                +=[board1] {1+$opp_turn},$move2,$yM2
30122                +f. "const p = 1 + "$opp_turn"; "$evalf score={is+u} rm.
30123                if $score>$opp_max_score opp_max_score=$score opp_max_board={^} fi
30124                rm.
30125              fi
30126            done
30127            rm[board1,ymax1]
30128
30129            {board,[w,h,1,1]},$opp_max_board
30130            f. "const p = 1 + "$turn"; "$evalf score={is+u} rm.
30131            if $score>$max_score max_score=$score max_col=$move1 fi
30132          fi
30133        done
30134        x=$max_col is_falling=1 yv=0 dyv=1
30135      fi
30136
30137    else # Manage token falling animation
30138      if !narg($column)
30139        $R,{board,h*$R},1,3,64
30140        repeat h#$board v={board,i($x,$>)} if $v j. [token{$v-1}],0,{$>*$R},0,0,1,[tokenm],255 fi done
30141        nm. column
30142      fi
30143
30144      yM={i("#"$ymax,$x)}
30145      [column]
30146      j. [token$turn],0,$yv,0,0,1,[tokenm],255
30147      j. [cache],0,0,0,0,1,[cachem],255
30148      [visu] nm. tmpvisu
30149      j[tmpvisu] ..,{$R*$x},0,0,0 rm.. w[tmpvisu] 100%,100%
30150      if $yv>=$yM*$R
30151        j[visu] [tmpvisu] is_falling=0
30152        =[board] {$turn+1},$x,$yM
30153        rm[ymax,column]
30154
30155        # Check end of game.
30156        +f[board] "if (!i,0,
30157                      case_h = i==j(1) && i==j(2) && i==j(3);
30158                      case_v = i==j(0,1) && i==j(0,2) && i==j(0,3);
30159                      case_d1 = i==j(1,1) && i==j(2,2) && i==j(3,3);
30160                      case_d2 = i==j(1,-1) && i==j(2,-2) && i==j(3,-3);
30161                      case_h?1:case_v?2:case_d1?3:case_d2?4)"
30162        if iM winner={[xM,yM,i(xM,yM)]} # Player won !
30163        else turn={($turn+1)%2}
30164        fi
30165        rm.
30166      fi
30167      rm[tmpvisu]
30168      yv={min($yM*$R,$yv+$dyv)} dyv+={visu,h/100}
30169      wait 20
30170    fi
30171
30172  while {*}" && "!{*,ESC}" && "!{*,Q}
30173  rm w 0 endl
30174
30175#@cli xz : eq. to 'x_crop'
30176xz :
30177  _gmic_s="$?" v + _x_crop
30178
30179#@cli x_crop
30180#@cli : Crop selected images interactively.
30181#@cli : (eq. to 'xz').
30182x_crop :
30183  _gmic_s="$?" v + _$0
30184
30185_x_crop :
30186  e[0--3] "Crop image"$_gmic_s" interactively."
30187  repeat $! l[$>]
30188    w ${"fitscreen "{[w,h,d]}},1,"[G'MIC] "{n}" - Interactive crop"
30189    +select 2,{round([w,h,d]/2)},0,1 u={^} z.. $u rm.
30190    w[] 0 is_change
30191  endl done u $u
30192
30193#@cli x_cut
30194#@cli : Cut selected images interactively.
30195x_cut :
30196  e[^-1] "Cut image"$_gmic_s" interactively."
30197  repeat $! l[$>]
30198    wsiz0=${"fitscreen ."}
30199    value0,value1=-1,-1
30200    w[] $wsiz0,0,"[G'MIC] "{n}" - Interactive cut"
30201    0
30202    for {*}" && "!{*,ESC}
30203
30204      if [w#1,h#1]!=[{*,w,h}] # Generate image view
30205       rm[1] +slices[0] 50% r. {*,w,h},1,100%,1 w.
30206      fi
30207
30208      mx,my,mb={*,x,y,b}
30209      if $mb" && "$mx>=0" && "$my>=0
30210        value0,value1={"dw1 = "{*,w}" - 1; value0 = "$mx"*100/dw1;
30211                        dh1 = "{*,h}" - 1; value1 = "$my"*100/dh1;
30212                        [ value0,value1 ];"}
30213        update_view=1
30214      fi
30215
30216      if $update_view
30217        if $value0>=0" && "$value1>=0
30218          +c[1] $value0%,$value1% n. 0,255
30219          to. "Min: "{_round($value0,0.1)}%"\n"\
30220              "Max: "{_round($value1,0.1)}%,1%,1%,{max(13,3.5*h%)}
30221          w. rm.
30222        else w.
30223        fi
30224        update_view=0
30225      fi
30226
30227      wait
30228      if {*,r} update_view=1 fi
30229      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D}
30230        w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 update_view=1
30231      fi
30232      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C}
30233        w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 update_view=1
30234      fi
30235      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R}
30236        w[] $wsiz0 wait -1 update_view=1
30237      fi
30238    done
30239    w[] 0 rm. u $value0%,$value1% c ${}
30240 endl done
30241
30242#@cli x_fire
30243#@cli : Launch the fire effect demo.
30244x_fire : skip "${1=G\47MIC}" check_display $0
30245  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30246  e[] "\n
30247------ "${g}"Fire effect"$n" ------------------------\n
30248----\n
30249---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
30250---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
30251---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30252----\n
30253-------------------------------------------"
30254
30255  # Init image data.
30256  i[0] 100,32 w[0] {[4.5*w,6.75*h]},0,"[G"{`39`}"MIC] Fire Effect"
30257  if {*,w}<0.5*{*,u} w[] {[{*,w},{*,h}]*1.5} fi
30258  i[1] (0,255,255,255,255^0,0,255,255,255^0,0,0,128,255) r[1] 256,1,1,3,3
30259  i[2] (0,0,0;0,0,0;1,1,1;0,1,0) *[2] 0.21
30260  text3d "$1",33,3,1
30261  mv. 3 c3d[3] n3d[3] *3d[3] 320 col3d[3] 255,205,130 db3d 0 f3d 300
30262  100,100 rand. 0,255 ellipse. 50%,50%,5,5,0,1,300 b. 10
30263  sharpen. 1000 shrink_xy. 1 n. 0,255 to_rgb. light3d . rm.
30264
30265  # Start animation loop.
30266  angle=0
30267  do
30268    correlate[0] [2]                                 # Apply fire effect.
30269    {0,w},1 rand. 128,256 j[0] .,0,{{0,h}-1} rm.     # Add new random values at the bottom line.
30270    +r[0] 400,200,1,1,3 map. [1]                     # Map fire palette
30271    +r3d[3] 0,1,0,$angle j3d.. .,50%,50%,0,1,5,0,0   # Draw 3D object.
30272    *3d. 0.25,0.16,1 j3d[0] .,50%,50%,0,1,3,0,0
30273    rm.
30274    angle+=3                                         # Update 3D angle.
30275    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,1,0.2 fi
30276    w.
30277    if {*,CTRLLEFT}" && "{*,D} w[] {1.5*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi
30278    rm. wait 40
30279  while {*}" && "!{*,ESC}" && "!{*,Q}
30280
30281  # Exit properly.
30282  rm[0-3] w 0
30283
30284#@cli x_fireworks
30285#@cli : Launch the fireworks demo.
30286x_fireworks : check_display $0
30287  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30288  e[] "\n
30289------ "${g}"Fireworks"$n" --------------------------\n
30290----\n
30291---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
30292---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
30293---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30294----\n
30295--------------------------------------------"
30296  l[]
30297  (16;64^64;32^128;32) r 320,160,1,3,3             # [-2] = Background (color gradient).
30298  .                                                 # [-1] = Rendered image.
30299  w. {1.5*w},{1.5*h},0,"[G"{`39`}"MIC] Fireworks"  # Display window.
30300  time=0
30301  do                                               # Start animation loop.
30302    time-=1
30303    if $!==2\ ||\ $time<0   # Insert new rocket.
30304      i[0] ({u(w)},\        # X-position
30305            {h},\           # Y-position
30306            {u(-3,3)},\     # X-velocity
30307            {u(2)-5},\      # Y-velocity
30308            {30+u(20)},\    # Time of explosion
30309            1.5,\           # Radius
30310            255,255,255)    # Color
30311      time={u(20)}          # Elapsed time until next rocket.
30312    fi
30313    *. 0.99                 # Create fading effect with previous frames.
30314    j. ..,0,0,0,0,0.2       # Add background.
30315    i=0
30316    repeat $!-2
30317      to_be_removed=0
30318      radius={if({$i,@4}>0,{$i,@5}/3,{$i,@5}*(1+2*({$i,@4}+2)/120))}
30319      ellipse. {$i,@0},{$i,@1},{$i,@5},{max(1,$radius)},{atan2({$i,@3},{$i,@2})*180/pi},0.6,{$i,@6-8}  # Draw rocket.
30320      ({$i,@2},{$i,@3},0,0.09,-1,0,0,0,0) +[$i,-1] # Compute new position of the rocket.
30321      if {$i,@0}<0\ ||\ {$i,@0}>=w\ ||\ {$i,@1}>=h\ ||\ $radius<0 to_be_removed=1 fi # Discard if rocket disappear.
30322      if {$i,@4}<0\ &&\ {$i,@4}>=-1 # In case of explosion -> Split current rocket into several colorful rockets.
30323        color={min(255,80+u(200))},{min(255,80+u(200))},{min(255,80+u(200))}
30324        radius={u(10)}
30325        N={5+u(10)}
30326        repeat $N
30327          angle={$>*2*pi/$N}
30328          i... ({$i,@0,1},{2*cos($angle)+{$i,@2}/1.5},{2*sin($angle)+{$i,@3}/1.5},-2,$radius,$color)
30329        done
30330        to_be_removed=1
30331      fi
30332      if $to_be_removed rm[$i] else i+=1 fi  # If processed rocket has to be removed.
30333    done
30334    fps=${-fps} if $fps>0 to. $fps" fps",3,{h-20},14,1,0.2 fi
30335    w. wait 20  # Display rendered frame.
30336    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
30337  while {*}" && "!{*,ESC}" && "!{*,Q}
30338  rm w 0 endl
30339
30340#@cli x_fisheye
30341#@cli : Launch the fish-eye effect demo.
30342x_fisheye : check_display $0
30343  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30344  e[] "\n
30345------ "${g}"Fish-eye effect"$n" --------------------\n
30346----\n
30347---- "${c}"Mouse pointer"$n" moves fish-eye center.\n
30348---- "${c}"Mouse buttons"$n" set fish-eye size.\n
30349---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
30350---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
30351---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30352----\n
30353-------------------------------------------"
30354
30355  if $!>0 a x n 0,255 r2dy 220 else
30356  120,90,1,3 rand. 0,255 plasma. 0.3,3 n 0,255
30357  t "  G\47MIC\nFISH-EYE\n EFFECT",20,13,23,1,255 scale3x b 5 sharpen 1000
30358  f i+150-3*abs(y-h/2) c. 0,255 frame_fuzzy. 15,10,15,1.5,0 to_rgb.
30359  fi
30360  torus3d 20,6 col3d. {u(30,255)},{u(30,255)},{u(30,255)} +r3d. 1,0,0,90
30361  col3d. {u(30,255)},{u(30,255)},{u(30,255)} +3d. 15 +3d[-2,-1] *3d. 4 db3d 0 c3d.
30362  R=30
30363  w.. {1.25*{-2,w}},{1.25*{-2,h}},0,"[G"{`39`}"MIC] Fish-Eye Effect"
30364  do
30365    wait 40
30366    if {*,b}==1 R={min(80,$R+8)} fi
30367    if {*,b}==2 R={max(3,$R-8)} fi
30368    +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
30369    if {*,x}>=0
30370    fisheye. {{*,x}*100/{*,w}},{{*,y}*100/{*,h}},$R
30371    fi
30372    w.
30373    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
30374    rm.
30375    if {*}==0" || "{*,ESC}" || "{*,Q} rm[-2,-1] w 0 return fi
30376  while 1
30377
30378#@cli x_fourier
30379#@cli : Launch the fourier filtering demo.
30380x_fourier : check_display $0
30381  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30382  e[] "\n
30383------ "${g}"Fourier-filtering"$n" ----------------------------------------\n
30384----\n
30385---- "${c}"Mouse buttons"$n" on the right image to set min/max frequencies.\n
30386---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
30387---- Keys '"${c}"CTRL+C"$n"' to decrease window size.\n
30388---- Keys '"${c}"CTRL+R"$n"' to reset window size.\n
30389---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30390----\n
30391-----------------------------------------------------------------"
30392
30393  if !$! sp ? r2dx 400 fi
30394
30395  repeat $! l[$>]
30396
30397    # Init variables.
30398    need_update=1  # need_update (boolean)
30399    freqmin=0      # min freq. (in %)
30400    freqmax=100    # max freq. (in %)
30401
30402    if w>3*{*,u}/5 r2dx. {3*{*,u}/10} fi  # Reduce image size if necessary.
30403    if h>3*{*,v}/5 r2dy. {3*{*,v}/5} fi
30404
30405    # Compute fourier transform.
30406    +fft. nm.. real nm. imag
30407
30408    # Generate log-magnitude image.
30409    +sqr[real,imag] +[-2,-1] sqrt. +. 1 log.
30410    n. 0,255 shift. {round(w/2)},{round(h/2)},0,0,2 to_colormode. {-2,s}
30411    nm. logmag
30412
30413    +r2dy. 128 frame. 1,1,0 nm. thumb
30414    w[0,-2] -1,-1,0,"[G"{`39`}"MIC] Fourier Filtering"
30415
30416    l
30417    if !narg($first_time)
30418      parallel 0,"alert[thumb] \"[G"{`39`}"MIC Fourier Filtering]\",\
30419            \"The G\47MIC Fourier filtering demo illustrates the effect\n\
30420            of bandpass frequency filtering on an image. Use your mouse\n\
30421            buttons to select low and high bounds for the frequencies\n\
30422            displayed on the Fourier representation of the image\n\
30423            (right image).\",\
30424            \"OK\""
30425      first_time=0
30426    fi
30427
30428    # Enter user event-loop.
30429    do
30430
30431      if $need_update # If image must be updated.
30432
30433        # Generated filtering mask.
30434        [logmag],[logmag] nm. mask
30435        r={sqrt(w^2+h^2)*$freqmax/200} ellipse[mask] 50%,50%,$r,$r,0,1,1
30436        r={max(0,sqrt(w^2+h^2)*$freqmin/200-1)} if $r ellipse[mask] 50%,50%,$r,$r,0,1,0 fi
30437
30438        # Compute filtered log-magnitude.
30439        +*[logmag] [mask] +. [mask] /. 2 n. 0,255
30440
30441        # Compute filtered fourier representation.
30442        shift[mask] -{mask,round(w/2)},-{mask,round(h/2)},0,0,2
30443        +*[real,imag] [mask]
30444        rm[mask]
30445
30446        # Compute filtered image by inverse fourier.
30447        ifft[-2,-1] rm. n. 0,255
30448
30449        # Display filtered image.
30450        rv[-2,-1]
30451        if {*} r[-2,-1] {{*,w}/2},{*,h} fi
30452        t. "Freq. Min/Max = "{int($freqmin)}"% / "{int($freqmax)}"%",5,5,13,1,255
30453        w[-2,-1] rm[-2,-1]
30454        need_update=0
30455
30456      fi
30457
30458      wait
30459
30460      if {*,b}" && "{*,x}>={*,w}/2  # If mouse button pressed on the right pane.
30461        r={200*sqrt(({*,x}-3*{*,w}/4)^2+({*,y}-{*,h}/2)^2)/\ # Compute selected radius (in %).
30462           sqrt(({*,w}/2)^2+{*,h}^2)}
30463        if {*,b}&1 freqmax=$r        # Update max freq. if left button.
30464        else freqmin={max(0,$r-3)}   # Update min freq. if other button.
30465        fi
30466        if $freqmin>=$freqmax freqmin=$freqmax fi   # Check that the min/max freq. are ordered.
30467        need_update=1                               # Tell that the image must be updated.
30468      fi
30469
30470      if {*,r} need_update=1 fi
30471
30472      # Increase window size.
30473      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} need_update=1 fi
30474
30475      # Decrease window size.
30476      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} need_update=1 fi
30477
30478      # Reset window size.
30479      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {2*{0,w}},{0,h} need_update=1 fi
30480
30481    while {*}" && "!{*,ESC}" && "!{*,Q}
30482    w 0
30483    endl
30484    rm[^0] # Clean images and window.
30485  endl done rm
30486
30487#@cli x_grab_color : _variable_name
30488#@cli : Open a color grabber widget from the first selected image.
30489#@cli : Argument 'variable_name' specifies the variable that contains the selected color values at any time.
30490#@cli : Assigning '-1' to it forces the interactive window to close.
30491#@cli : Default values: 'variable_name=xgc_variable'.
30492x_grab_color : skip ${1=xgc_variable} check_display $0
30493  if !$! error[0--3] "Command '$0': Missing specified input image." fi
30494  l[0] nm={n} nm. img
30495  e[^-1] "Open "${arg\ {0,s},GRAY,GRAYA,RGB,RGBA}" color grabber widget for image$?, with variable name '$1'."
30496
30497  if !{*} w[] ${fitscreen[]\ {[w,h,1]},128,50%},0,0,-1,-1,"Grab a color" fi
30498  _x_grab_color +dilate. 3 nm. icon_mask *.. 255 to_rgb.. nm.. icon_sprite
30499
30500  xc=5 yc=5 o$1=$$1
30501
30502  cursor[0] 0
30503  do
30504    if !narg($visu0)
30505      +r[img] {*,w},{*,h},1,100%,2 drgba. w. nm. visu0
30506    fi
30507
30508    x={*,x} y={*,y} b={*,b} mouse_over={$x>=0" && "$y>=0}
30509    hc={narg($$1)?40:24}
30510    yc={visu0,nhc=h-$hc-8;!$mouse_over?$yc:$y<$hc||$yc+$hc>=h?nhc:$y>=nhc?5:$yc}
30511
30512    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]
30513      [visu0] nm. visu
30514      if narg($color)
30515        24,$hc,1,[img] fc. $color
30516        if narg($$1) rectangle. 0,24,100%,100%,1,$$1 line. 0,24,100%,24,1,0 fi
30517        drgba. frame. 1,1,0 frame. 1,1,255 j[visu] .,$xc,$yc rm.
30518        0
30519        if narg($$1)
30520          t. "Position ("$X","$Y")\nColor    ("{``$color}")\nSelected ("{``$$1}")",1,0,15,1,255
30521        else
30522          t. "Position ("$X","$Y")\nColor ("{``$color}")",1,0,15,1,255
30523        fi
30524        +dilate. 5 r.. 100%,100%,1,3
30525        j[visu] ..,{30+$xc},$yc,0,0,0.85,.,255 rm[-2,-1]
30526      fi
30527
30528      if $mouse_over
30529        X={img,round($x*(w-1)/({*,w}-1))}
30530        Y={img,round($y*(h-1)/({*,h}-1))}
30531        color={img,round(I($X,$Y))}
30532        j[visu] [icon_sprite],$x,{icon_sprite,$y-h+1},0,0,1,[icon_mask]
30533        if $b&1 $1=$color fi
30534      fi
30535      w[visu] rm[visu]
30536
30537      ox=$x oy=$y ob=$b ohc=$hc oyc=$yc ocolor=$color o$1=$$1
30538    fi
30539
30540    if arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_' wait 50 else wait fi
30541    if {*,r} w[] -1 rm[visu0] yc=5 fi
30542    if ['$$1']=='-1' break fi # Close request
30543
30544  while {*}" && "!{*,ESC}" && "!{*,Q}
30545  w 0 k[img] nm $nm endl
30546  u $color
30547
30548# Define color grabber icon.
30549_x_grab_color :
30550  base642img[] \
30551"MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMSAzMzcgMSAxICMyNzcKeJyNiNlOwlAYBi9IDDcaY+LGpiJbj7tBKEbglTQE7dcbkZS1gfNXFCx"\
30552"lK6WE83w+gDaaeG0mmWTm67NSea5s1Pz6Ot80tnrb/d3BnhkYBq3QKDyOTA5nmfqDnW2W5nK77Mj60yLXgZvrqm6Oq47MYWc4ZhnCJE2wrgjmBWFwRm"\
30553"r/hNQ3ZqivKQIlCN0YQZdeQGgxzXOD1T3XWEMhVWMtz1Wmw4h1QHEOShogqQc67YPOBwpdmgpdWzDSY9DNFJS1wWUH/NZF926FTl4oel48tgui3C6KU"\
30554"rMo7rWCYCvJg4k/fvp/5/dJq9QyuUy48UXMOZ5H7ah9ND2YRMbhUXgUsoLDgOnxsf++0/TX1zRf1fcNa0iPgw=="
30555  -. 127 decompress_rle.
30556  frame. 10,10,0
30557  r2dx. 24
30558
30559#@cli x_hanoi
30560#@cli : Launch the Tower of Hanoi game.
30561x_hanoi : check_display $0
30562  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30563  e[] "\n
30564------ "${g}"Tower of Hanoï"$n" ---------------------\n
30565----\n
30566---- "${c}"Left button"$n" and "${c}"mouse"$n" to move a disk.\n
30567---- "${c}"Right button"$n" to rotate 3D view.\n
30568---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
30569---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
30570---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30571----\n
30572-------------------------------------------"
30573  l[]
30574  l[] # Create 3D rods
30575    cylinder3d 1,10 r3d 1,0,0,90
30576    ++3d 10,0,0  +-3d.. 10,0,0
30577    box3d 30,1,10 -3d. 15,0,5
30578    +3d nm rods3d
30579    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
30580    (86,50,50;132,36,12;218,109,66;231,207,180;255,193,140) permute. yzcx r. 32,1,1,3,3
30581    map.. . rm. b. 2% b. x,1% sharpen. 100 c. 0,255
30582    r3d.. 1,0,0,-90 texturize3d.. . r3d.. 1,0,0,90 rm.
30583  endl
30584
30585  l[] # Create 3D disks
30586    6,1,1,3,'[360*x/w,0.9*(1-(x/w)^0.5),0.9]' hsi2rgb.
30587    ytop0=0
30588    repeat w#0
30589      R,r={3-0.3*$>},{1.6-0.22*$>}
30590      torus3d $R,$r,36,10
30591      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]
30592      luminance,0.75 # Marble texture
30593      texturize3d.. . rm.
30594      /3d. 1,1,{0.3+$r} r3d. 1,0,0,90 -3d. 0,0.8,0
30595      a$>,x$>,y$>,h$>=0,0,$ytop0,{1.8*$r/(0.3+$r)}
30596      ytop0+=${h$>}
30597      nm. disk3d$>
30598    done
30599    rm[0]
30600  endl
30601
30602  w[] 640,400,0,"[G"{`39`}"MIC] Tower of Hano\357"
30603  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
30604  nb_moves,buttons,motion3d_x,motion3d_y=0
30605  x,rod,rod_source,rod_target,selected=-1
30606  fading=$| error=0
30607  do
30608
30609    # Display 3D view for current game state.
30610    repeat 6 +r3d[disk3d$>] 1,1,1,${a$>} +3d. {10*(${x$>}-1)},-${y$>},0 done +3d[-6--1] +3d. [rods3d]
30611    r3d. 1,0,0,20
30612    if !($buttons&2) r3d. 0,1,0.3,{5*cos(1.5*$|)} r3d. 0.3,0,1,{3*sin(0.8*$|)} fi
30613    r3d. 1,0,0,$motion3d_y r3d. 0,-1,0,$motion3d_x *3d. 20
30614    [background] j3d. ..,50%,70%,10,1,5,0,1,800,200,0,-3000,0.15,0.2
30615    t. "#Moves: "$nb_moves,2%,92%,20,1,255
30616
30617    if $error (255^0^0) ri. .. j.. .,0,0,0,0,$error error={max(0,$error-0.2)} rm. fi
30618    if $|-$fading<1  *. {$|-$fading} fi
30619    w. wait 40
30620    if {*,CTRLLEFT}" && "{*,D} w[] {w*1.5},{h*1.5} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
30621    rm[-2,-1]
30622
30623    # Get index of top disk for each rod.
30624    top0,top1,top2,ytop0,ytop1,ytop2=-1
30625    repeat 6
30626      rod={round(${x$>})}
30627      if $selected!=$>" && "${y$>}+${h$>}>${ytop$rod} ytop$rod={${y$>}+${h$>}} top$rod=$> fi
30628    done
30629
30630    # Manage user events.
30631    prev_buttons=$buttons
30632    mouse_x,mouse_y,buttons={*,x},{*,y},{*,b}
30633    if $mouse_x>=0 x={2.6*($mouse_x/{*,w}-0.5)+1} rod={round($x)} fi
30634
30635    if $mouse_x>=0" && "$buttons&2 # Right mouse button
30636      motion3d_x,motion3d_y={([$mouse_x,$mouse_y]/[{*,w},{*,h}]-0.5)*90}
30637
30638    elif $mouse_x>=0" && "$buttons&1 # Left mouse button
30639      if $selected<0 # Select a disk
30640        selected=${top$rod}
30641        rod_source={$selected<0?-1:$rod}
30642      fi
30643      if $selected>=0" && "$rod>=0 # Move a selected disk
30644        if ${y$selected}<11 y$selected={min(11,${y$selected}+3)}
30645        else
30646          x$selected+={d=$rod-${x$selected};sign(d)*min(0.3,abs(d))}
30647          y$selected={x=${x$selected};11+1.5*sin(pi*abs(x-round(x)))}
30648          a$selected={x=${x$selected};45*sin(pi*abs(x-round(x)))}
30649        fi
30650      fi
30651
30652    elif !$buttons # No mouse button
30653      if $rod>=0" && "$selected>=0
30654        if $rod_target<0
30655          if ${top$rod}<$selected rod_target=$rod nb_moves+={$rod_target!=$rod_source} # Allowed move
30656          else rod_target=$rod_source error=0.8 # Forbidden move
30657          fi
30658        fi
30659        x$selected=$rod_target
30660        a$selected=0
30661        ytop={max(0,${ytop$rod_target})}
30662        if ${y$selected}>$ytop y$selected={max($ytop,${y$selected}-3)}
30663        else x,rod,rod_source,rod_target,selected=-1
30664        fi
30665      fi
30666    fi
30667
30668    if !($buttons&2) # Slowly go back to initial 3D view
30669      motion3d_x-={sign($motion3d_x)*min(1,abs($motion3d_x))}
30670      motion3d_y-={sign($motion3d_y)*min(1,abs($motion3d_y))}
30671    fi
30672
30673  while {*}" && "!{*,ESC}" && "!{*,Q}
30674  w[] 0 rm endl
30675
30676#@cli x_histogram
30677#@cli : Launch the histogram demo.
30678x_histogram : check_display $0
30679  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30680  e[] "\n
30681------ "${g}"Histogram demo"$n" -------------------------------\n
30682----\n
30683---- "${c}"Mouse"$n" to set parameters.\n
30684---- "${c}"Right button"$n" or key '"${c}"SPACE"$n"' to reset.\n
30685---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30686----\n
30687-----------------------------------------------------"
30688
30689  if !$! sp ? to_rgb
30690  else k[0] to_rgb r2dy 300,2 if w>800 r 800,100%,1,3,2 fi n 0,255
30691  fi
30692
30693  # Prepare image layout.
30694  +frame. 1,1,0
30695  300,{h},1,3,220
30696
30697  t. "Gamma :",5,0,16,1,0
30698  t. "Contrast :",5,50,16,1,0
30699  t. "Brightness :",5,100,16,1,0
30700  t. "Smoothness :",5,150,16,1,0
30701  t. "Sharpness :",5,200,16,1,0
30702  t. "Clusters :",5,250,16,1,0
30703  a[-2,-1] x
30704  {w},200,1,3,255
30705  grid. 10%,10%,0,0,0.3,0xCCCCCCCC,0
30706  rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0
30707  axes. 0,255,1,0,13,1,0
30708  frame[-2,-1] 5,5,220
30709  a[-2,-1] y
30710
30711  # Initialize variables.
30712  clusters=64 sharpness=0 smoothness=0 contrast=1 brightness=0 gamma=1
30713
30714  # Start event loop.
30715  do
30716
30717    # Render corrected image and insert it in canvas.
30718    [0]
30719    ia={ia}
30720    if $gamma /. 255 ^. {1/$gamma} *. 255 fi
30721    -. $ia *. $contrast +. $brightness +. $ia
30722    b. $smoothness
30723    sharpen. $sharpness
30724    c. 0,255
30725    +j.. .,6,6
30726
30727    # Render parameter sliders.
30728    sx={{0,w}+12}
30729    _x_histogram. {$gamma*100/4} j.. .,$sx,25 rm.
30730    _x_histogram. {$contrast*100/4} j.. .,$sx,75 rm.
30731    _x_histogram. {($brightness+128)*100/256} j.. .,$sx,125 rm.
30732    _x_histogram. {$smoothness*100/10} j.. .,$sx,175 rm.
30733    _x_histogram. {$sharpness*100/2000} j.. .,$sx,225 rm.
30734    _x_histogram. {$clusters*100/256} j.. .,$sx,275 rm.
30735
30736    # Render corresponding histogram.
30737    +s.. c histogram[-3--1] $clusters,0,255 /[-3--1] {6*{0,wh}/$clusters} rm[-5]
30738    +z[-4] 5,{0,h+16},{{-4,w}-5},{{-4,h}-6}
30739    graph. [-4],3,0,1,0,0.2,255,0,0
30740    graph. ...,3,0,1,0,0.2,0,255,0
30741    graph. ..,3,0,1,0,0.2,0,0,255
30742    rm[-4--2]
30743    j.. .,5,{0,h+16} rm.
30744
30745    if {*,b}&1\ &&\ {*,x}<{0,w}\ &&\ {*,y}<{0,h}
30746      j. [0],6,6 to. Original,10,10,16
30747    fi
30748
30749    # Display rendering.
30750    w. {w},{h},0,"[G"{`39`}"MIC] Histogram Demo" rm.
30751    wait
30752
30753    # Manage user interactions.
30754    if {*,b}&1\ &&\ {*,x}>={0,w}-10
30755      if {*,y}>=25\ &&\ {*,y}<=42
30756        gamma={max(0,min(4,({*,x}-$sx)*4/280))}
30757      elif {*,y}>=75\ &&\ {*,y}<=92
30758        contrast={max(0,min(4,({*,x}-$sx)*4/280))}
30759      elif {*,y}>=125\ &&\ {*,y}<=142
30760        brightness={max(-128,min(128,({*,x}-$sx)*256/280-128))}
30761      elif {*,y}>=175\ &&\ {*,y}<=192
30762        smoothness={max(0,min(10,({*,x}-$sx)*10/280))}
30763      elif {*,y}>=225\ &&\ {*,y}<=242
30764        sharpness={max(0,min(2000,({*,x}-$sx)*2000/280))}
30765      elif {*,y}>=275\ &&\ {*,y}<=292
30766        clusters={max(2,min(256,({*,x}-$sx)*256/280))}
30767      fi
30768    fi
30769    if {*,b}&2\ ||\ {*,SPACE} clusters=64 sharpness=0 smoothness=0 contrast=1 brightness=0 gamma=1 fi
30770  while {*}" && "!{*,ESC}" && "!{*,Q}
30771  w 0 rm
30772
30773_x_histogram :
30774  val={max(0,min(100,$1))}
30775  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
30776  0 t. {round($val)}%,0,0,14,1,1 +*. -255 +. 255 r. 100%,100%,1,3
30777  j... .,{(280-w)/2},{(16-h)/2},0,0,1,.. rm[-2,-1]
30778  r. {w+2},{h+2},1,3,0,0,0.5,0.5
30779
30780#@cli x_hough
30781#@cli : Launch the hough transform demo.
30782x_hough : check_display $0
30783  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30784  e[] "\n
30785------ "${g}"Hough-transform"$n" -----------------------------------------\n
30786----\n
30787---- "${c}"Mouse buttons"$n" on the vote image to draw corresponding line.\n
30788---- "${c}"Mouse buttons"$n" on the image to vote for all lines crossing.\n
30789---- the clicked point.\n
30790---- Key '"${c}"SPACE"$n"' to reset the hough window.\n
30791---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30792----\n
30793-----------------------------------------------------------------"
30794
30795  if !$! l[] sp greece onfail testimage2d 400 endl fi
30796  n 0,255
30797
30798  repeat $! l[$>]
30799    r. ${fitscreen\ {[w,h]}},1,100%,3  # Resize to fit screen if necessary.
30800    if !narg($first_time)
30801      parallel 0,"+l[0] r2dy 128 frame 1,1,0 \
30802        alert \"[G"{`39`}"MIC Hough Transform]\",\
30803           \"The G\47MIC Hough transform demo illustrates the application\n\
30804           of the Hough transform to detect lines in an image. Use your\n\
30805           mouse buttons to explore the transform image and see how\n\
30806           lines in images are represented by points in the transform.\",\
30807           \"OK\" \
30808        rm endl"
30809      first_time=0
30810    fi
30811
30812    rhomax={sqrt(w^2+h^2)/2}
30813    +b. 1.5 hough. 512,400 b. 0.5 +. 1 log. n. 0,255
30814    w.. -1,-1,0,"[G"{`39`}"MIC] Image" w1. -1,-1,0,"[G"{`39`}"MIC] Hough Transform"
30815
30816    do
30817      wait
30818
30819      if {*,b} # When clicking on the image.
30820        x0={{*,x}-{*,w}/2}
30821        y0={{*,y}-{*,h}/2}
30822        rho0={sqrt(($x0)^2+($y0)^2)}
30823        theta0={atan2($y0,$x0)}
30824        (0,{2*pi}) ($theta0,{$theta0-2*pi})
30825        r[-2,-1] {-3,w},1,1,1,3
30826        cos. *. $rho0 +<. 0 abs..
30827        *. {pi} +[-3,-1] %.. {2*pi}
30828        *.. {0.5*{-3,w}/pi} *. {{-3,h}/$rhomax}
30829        a[-2,-1] y
30830        repeat w point.. {i($>,0)},{i($>,1)},0,0.3,255 done
30831        rm. w1.
30832
30833      elif {*1,x}>=0" && "{*1,b} # When clicking on the vote window.
30834        theta={{*1,x}*2*pi/{*1,w}}
30835        rho={{*1,y}*$rhomax/{*1,h}}
30836        x={{-2,w}/2+$rho*cos($theta)}
30837        y={{-2,h}/2+$rho*sin($theta)}
30838        x0={$x+1000*sin($theta)}
30839        y0={$y-1000*cos($theta)}
30840        x1={$x-1000*sin($theta)}
30841        y1={$y+1000*cos($theta)}
30842        ..
30843        line. $x0,$y0,$x1,$y1,1,0x0F0F0F0F,255
30844        line. {$x0+1},$y0,$x1,$y1,1,0x0F0F0F0F,255
30845        line. $x0,{$y0+1},$x1,$y1,1,0x0F0F0F0F,255
30846        line. $x0,$y0,$x1,$y1,1,0xF0F0F0F0,0
30847        line. {$x0+1},$y0,$x1,$y1,1,0xF0F0F0F0,0
30848        line. $x0,{$y0+1},$x1,$y1,1,0xF0F0F0F0,0
30849        w. rm.
30850
30851      elif {*,SPACE}" || "{*1,SPACE}
30852        rm. +b. 1.5 hough. 512,400 b. 0.5 +. 1 log. n. 0,255
30853        w1. -1,-1,0,"Hough Transform"
30854
30855      elif {*,r} w..
30856      elif {*1,r} w1.
30857      fi
30858
30859    while {*}" && "{*1}" && "!{*,ESC}" && "!{*,Q}" && "!{*1,ESC}" && "!{*1,Q}
30860    w 0 w1 0
30861    rm. endl
30862    if !{*}" || "!{*1} break fi
30863  done rm
30864
30865#@cli x_jawbreaker : 0<_width<20,0<_height<20,0<_balls<=8
30866#@cli : Launch the Jawbreaker game.
30867x_jawbreaker : check "${1=12}>0 && $1<20 && ${2=13}>0 && $2<20 && ${3=5}>0 && $3<=8" check_display $0
30868  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
30869  e[] "\n
30870------ "${g}"Jawbreaker"$n" --------------------------------------------\n
30871----\n
30872---- The goal of the game is to "${c}"remove the maximum number of\n
30873---- balls on the board"$n", simply by clicking on them. But a\n
30874---- colored ball can disappear only if it is grouped with at\n
30875---- least one ball of the same color. The score is higher if\n
30876---- you destroy larger sets of connected colored balls.\n
30877----\n
30878---- "${c}"Left mouse button"$n" to select/destroy balls on board.\n
30879---- Key '"${c}"BACKSPACE"$n"' or '"${c}"SPACE"$n"' to undo the last move.\n
30880---- Key '"${c}"S"$n"' to save snapshot of the current view.\n
30881---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
30882----\n
30883--------------------------------------------------------------"
30884
30885  # Init images and variables.
30886  $1,$2 nm. board rand[board] 1,$3 round[board] 1
30887  . nm. undo
30888  40,40,1,4 nm. balls _x_jawbreaker_ball.
30889  autocrop. 0 expand_xy. 1,0 *. 1.5 c. 0,255 r. {{board,w}*w},{{board,h}*h},1,1,0,2 /. 255
30890  {w},{h},1,3 nm. back l.
30891    rand 0,255 blur_xy 6,20 equalize 100,0,255 blur_xy 2,4
30892    sh 0 sh.. 1 sh... 2 /... 4 /.. 8 /. 2 rm[-3--1]
30893  endl
30894  [back] nm. visu
30895  score=0
30896  undoscore=0
30897  render_board=1
30898  shapescorey=0
30899  shapescore=0
30900
30901  # Enter user-event loop.
30902  do
30903
30904    # Render board graphics.
30905    if $render_board
30906      +abs[board] -. 1 *. {360/$3} +>=[board] 0 *. 0.9 +!=[board] 0
30907      ri[-3--1] [balls] [balls] *[-2,-1] a[-3--1] c hsv2rgb.
30908      +compose_channels. + >. 0 dilate. 3
30909      j[visu] [back] j[visu] ..,0,0,0,0,1,. rm[-2,-1]
30910      if !$shapescorey w[visu] {back,w},{back,h},0,"[G"{`39`}"MIC] Jawbreaker (Score : "$score")" fi
30911      render_board=0
30912    fi
30913
30914    # Add shape score sprite if necessary.
30915    if $shapescorey
30916      +t[visu] "+"$shapescore,{*,x},{{*,y}-64+$shapescorey},32,{($shapescorey-1)/31},255
30917      shapescorey={max(0,$shapescorey-1)}
30918      w. {back,w},{back,h},0,"[G"{`39`}"MIC] Jawbreaker (Score : "$score")" rm. wait 25
30919    else wait fi
30920
30921    # Check for the end of the game.
30922    +f[board] "if(i,j(-1)==i || j(1)==i || j(0,1)==i || j(0,-1)==i,0)"
30923    if !is rm. break fi rm.
30924
30925    # Manage user-events
30926    if {*,r} render_board=1                 # Will resize window to initial size, if resized.
30927    elif {*,S} o[visu] gmic_jawbreaker.png  # Save snapshot if requested.
30928    elif {*,BACKSPACE}" || "{*,SPACE}       # Manage undo move.
30929      abs[undo] j[board] [undo]
30930      score=$undoscore
30931      render_board=1
30932
30933    elif {*,x}>=0" && "{*,b}                # Manage button click.
30934
30935      # Retrieve board coordinates.
30936      wait -1
30937      x={"int("{*,x}"*"{board,w}"/"{*,w}")"}
30938      y={"int("{*,y}"*"{board,h}"/"{*,h}")"}
30939
30940      # When selecting a ball -> display selection and init new shape score sprite.
30941      if {{board,i($x,$y)}>0}
30942        abs[board] flood[board] $x,$y,0,0,0,1,-{board,i($x,$y)}
30943        +>=[board] 0 -. 1
30944        shapescore={(is+1)^2} shapescorey={if($shapescore,32,0)} rm.
30945
30946      # When confirming selection of a ball -> remove set of connected balls.
30947      elif {board,i($x,$y)}
30948
30949        +flood[board] $x,$y,0,0,0,1,-1 ==. -1
30950        if is>1 # If selected ball is connected to at least one ball.
30951
30952          # Save undo state.
30953          j[undo] [board]
30954          undoscore=$score
30955
30956          # Manage board shifts (vertical and horizontal).
30957          flood[board] $x,$y,0,0,0,1,0
30958          repeat w#$board
30959            +columns[board] $> mirror. y
30960            h={board,h} l. s -,0 a y if $! r 1,$h,1,1,0 mirror y else i 1,$h fi endl
30961            j[board] .,$> rm.
30962          done
30963          rows[board] -1,100% f[board] "if(y==0,if(i(x,h-1),x,w),i)" sort[board] +,x rows[board] 1,100%
30964
30965          # Update score.
30966          score+={int((is-1)^2)}
30967
30968        fi
30969        rm. # Remove selection mask.
30970
30971      else abs[board]  # Remove previous selection if clicked outside balls.
30972      fi
30973
30974      render_board=1
30975    fi
30976
30977  while {*}" && "!{*,Q}" && "!{*,ESC}
30978
30979  # Game over.
30980  if {*}" && "!{*,ESC}
30981    w[] {visu,w},{visu,h},0,"[G"{`39`}"MIC] Jawbreaker (Final Score : "$score")"
30982    260,85 nm. gameover t. "Game Over!",3,0,53,1,1 t. "Score : "$score,23,53,32,1,1
30983    +dilate. 5 nm. "mgameover" *.. 255 r.. 100%,100%,1,3
30984    repeat 25
30985      +r[gameover,mgameover] {400-12*($>+1)}%,{400-12*($>+1)}%
30986      +j[visu] ..,{({visu,w}-w)/2},{({visu,h}-h)/2},0,0,{$>/25},.
30987      w. rm[-3--1] wait 25
30988    done
30989    do
30990      wait if {*,r} w[] {*,w},{*,h} wait -1 fi
30991      while {*}" && "!{*,Q}" && "!{*,ESC}" && "!{*,b}
30992    rm[gameover,mgameover]
30993  fi
30994
30995  # End properly.
30996  rm[board,undo,balls,back,visu]
30997  w 0
30998
30999_x_jawbreaker_ball :
31000  mwh={min(w,h)}
31001  sh 3 f. 0 rm.
31002  ellipse {0.5*$mwh},{0.5*$mwh},{0.5*$mwh-4},{0.5*$mwh-4},0,1,240,240,240,1
31003  sh 0,2 *. '($mwh+y-x)/(2*w)' rm.
31004  sh 3 *.. . dilate. 5 rm.
31005  sh 0,2 +. 'if(i&&(!j(-1)||!j(1)||!j(0,-1)||!j(0,1)),240/6,0)' rm.
31006  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
31007
31008#@cli x_landscape
31009#@cli : Launch the virtual landscape demo.
31010x_landscape : check_display $0
31011  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31012  e[] "\n
31013------ "${g}"Virtual landscape"$n" -------------------------------------\n
31014----\n
31015---- Enjoy the view!\n
31016----\n
31017---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
31018---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
31019---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31020----\n
31021--------------------------------------------------------------"
31022  l[]
31023  W=150 H=350
31024
31025  # Generate global map + colors.
31026  900,900 plasma. 1,1,6 b. 0.07% n. 0,255 nm. map
31027  +g. *. 0.5 +[-2,-1] n. 0,1 ^. 2 n. -150,330
31028  equalize[map] 256 n[map] -400,160 c[map] 0,100% # Add water.
31029  (0,102,51;149,175,124;102,42,0;255,255,255) permute. yzcx srgb2rgb. r. 256,1,1,3,3 rgb2srgb.
31030  +n[map] 0,255 map. .. rm..
31031  +. .. rm.. c. 0,255 nm. colors # Colors.
31032
31033  # Pre-compute some images used on each frame.
31034  $W,$H,1,1,'x' y. x nm. x # Increasing x.
31035  $W,$H,1,1,'1+x+y*w' y. x nm. offsets # Offsets (+1).
31036  $W,$H,1,1,'0.5*y' nm. gmap Mgmap={iM} # Z-increment for altitude map.
31037  $W,$H,1,3 fc. 60,80,135 nm. ccolors # Color for the horizon.
31038  $W,$H,1,1,'(y/$H)^2' nm. mcolors # Mask for the horizon.
31039  $W,400,1,1,'b=h-1-$Mgmap;if(y>=b,256+(y-b)*255/(h-1-b),y*255/b)' round. # Background.
31040
31041  (96^16^128) (0^200^255) a[-2,-1] x r. 256,1,1,3,3
31042  (0^32^0) (0^64^128) a[-2,-1] x r. 256,1,1,3,3
31043  a[-2,-1] x map.. . rm.
31044
31045  nm. background
31046  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.
31047  (64^16^0) r. $W nm. groundcolor # Ground color.
31048
31049  # Start animation.
31050  w[] 600,400,0,"[G"{`39`}"MIC] Virtual Landscape"
31051  do
31052
31053    # Get part of the map to display.
31054    t={$|*0.03}
31055    xm={map,w/2+(w-$H/2)/2*cos(3.1*$t)}
31056    ym={map,h/2+(h-$H/2)/2*sin(2.8*$t)}
31057    u={map,(w-$H/2)*cos(2.5*$t)}
31058    v={map,(h-$H/2)*sin(9.7*$t)}
31059    a={atan2($v,$u)*180/pi}
31060    +r3d[viewrange3d] 0,0,1,$a y. x
31061    ({$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..
31062    r. $W,$H,1,2,3 +warp[map,colors] .,0,1,0 rm...
31063    nm.. lmap nm. lcolors
31064
31065    # Add color shading and altitude to local maps.
31066    +!=[lmap] 0 nm. ground
31067    +[lmap] [gmap]
31068    j[lcolors] [ccolors],0,0,0,0,1,[mcolors]
31069    j[lcolors] [groundcolor]
31070    +round[lmap] f. '>m=abs(j(0,-1));i>m?i:-m' nm. y0  # Compute visible top points.
31071    +shift. 0,1 abs. +. 1 nm. y1 # Compute visible bottom points.
31072    *[y0,y1] [ground] rm[ground]
31073    r[lcolors,y0,y1] {$W*$H},1,1,100%,-1
31074
31075    # Keep only visible primitives.
31076    +>[y0] 0 *. [offsets] discard. 0 y.
31077    if h # There is something to display (ground).
31078      -. 1 +warp[x] .,0,0,0 nm. lx
31079      warp[lcolors,y0,y1] ..,0,0,0 rm..
31080
31081      # Generate 3D object.
31082      N={h} ({'CImg3d'},{2*$N},$N)
31083      +a[lx] [y0],x rm[y0] +a[lx] [y1],x rm[lx,y1] a[-2,-1] y z. 0,2
31084      1,$N,1,1,2 +f. y ++. $N a[-3--1] x
31085      mv[lcolors] $! permute. cyzx
31086      1,$N,1,1,1
31087      y[-5--1] y a[-5--1] y *3d. -1,-1
31088      +j3d[background] .,{background,w-1},{background,h},0,1,1,0,0,0 rm[-3,-2]
31089    else # Case of nothing to display (only water).
31090      rm[-5--1] [background]
31091    fi
31092
31093    r. {*,w},{*,h},1,3
31094    fps=${-fps} if $fps>0 to. $fps" fps",5,5,24,2,0.2 fi
31095    w. -1,-1,0 rm.
31096    if {*,CTRLLEFT}" && "{*,D} w[] 900,600 elif {*,CTRLLEFT}" && "{*,C} w[] 600,400 fi
31097    wait 20
31098  while {*}" && "!{*,ESC}" && "!{*,Q}
31099  rm w 0 endl
31100
31101#@cli x_life
31102#@cli : Launch the game of life.
31103x_life : check_display $0
31104  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31105  e[] "\n
31106------ "${g}"The game of life"$n" --------------------------------------\n
31107----\n
31108---- The goal is to create the "${c}"biggest possible biological\n
31109---- system"$n". You start with a stock of cells which you can\n
31110---- spread over the board. For each new cells created\n
31111---- simultaneously and spontaneously by your system, you\n
31112---- gain more new cells to scatter.\n
31113----\n
31114---- "${c}"Left mouse button"$n" to scatter cells in stock.\n
31115---- "${c}"Right mouse button"$n" to reset game.\n
31116---- Key '"${c}"S"$n"' to save snapshot of the current view.\n
31117---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31118----\n
31119--------------------------------------------------------------"
31120
31121  i[0] 90,90,1,1,0                                             # Image[0] = game state.
31122  i[1] [0] f[1] 0                                              # Image[1] = generation counter.
31123  i[2] 400,400,1,3                                             # Image[2] = visualization.
31124  i[3] 1                                                       # Image[3] = colormap (to be initialized).
31125  iteration=0                                                  # Iteration counter.
31126  score=0                                                      # Current score.
31127  bestscore=0                                                  # Best score.
31128  stock=500                                                    # Remaining cells.
31129  w[0] 400,400,0,"[G"{`39`}"MIC] The Game of Life"             # Initialize display window.
31130  cursor[0] 0
31131
31132  # Start user-event loop.
31133  do
31134    (1,1,1;1,0,1;1,1,1) +correlate[0] .,0 rm..                # Count numbers of neighboring living cells.
31135    +ir. 2,2 &. [0] ir.. 3,3 -|[-2,-1]                        # Make the game evolve (kill or create cells).
31136    rv[0,-1]                                                  # Update game state.
31137    if {*,x}>0" && "{*,b}==1" && "$stock>0                    # Add random cells to the game if mouse button pressed.
31138      nb={u*7}
31139      repeat $nb
31140        x={{*,x}/{*,w}*{0,w}+u(-4,4)}
31141        y={{*,y}/{*,h}*{0,h}+u(-3,3)}
31142        =[0] 1,$x,$y
31143        =[1] $iteration,$x,$y
31144        point[2] {$x*{2,w}/{0,w}},{$y*{2,h}/{0,h}},0,0.8,255
31145      done
31146      stock={round(max(0,$stock-$nb))}
31147    fi
31148
31149    -. [0] *. -1                                              # Compute difference between consecutive states.
31150    stock-={2*(min(0,int(is/16*$score/150)))}                 # Increment available cells if the evolution is fast.
31151    +[1] [0]                                                  # Increment generation counter for still existing cells.
31152    min. 0 +. 1 *[1,-1]                                       # Reset generation counter for died cells.
31153
31154    if {*,b}==2                                               # Reset game if right mouse button has been pressed.
31155      f[0-2] 0 iteration=0 score=0 bestscore=0 stock=500 rm[3] i[3] 1
31156    fi
31157
31158    if {3,w}==1                                               # Create color palette if necessary.
31159      rm[3] i[3] {u(3,12)},1,1,3,u(100,255)
31160      r[3] {u(100,300)}%,1,1,3,4
31161      point[3] 0,0,0,1,0
31162      r[3] {u(100,600)}%,1,1,3,5 c[3] 0,255
31163    fi
31164
31165    +r[1] {2,w},{2,h} &. 7 b. {1+$score*0.05}                  # Render colored image of the game and display it.
31166    n. 0,{3,w} map. [3] *. 0.1 +[2,-1] /[2] 1.1
31167    [2] if {*,x}>0                                             # Add a small target icon at the mouse position.
31168      opac={0.7*min(1,$stock/500)} r={min(500,$stock)*cos($iteration)/100}
31169      ellipse. {*,x},{*,y},{15+$r},{15+$r},0,$opac,0,196,0
31170      ellipse. {*,x},{*,y},{10+$r},{10+$r},0,$opac,32,64,16
31171      ellipse. {*,x},{*,y},{5+$r},{5+$r},0,$opac,255,230,0
31172    fi
31173    t. "Living cells : "$score"\n"\                            # Add score description.
31174       "Stock : "$stock"\n"\
31175       "Score : "$bestscore,5,3,22,0.7,255
31176    w. {*,w},{*,h}
31177    if {*,S} o. gmic_life.png fi                               # Save snapshot if requested.
31178    rm.
31179
31180    if !($iteration%10) # Re-compute current and best scores, every 10th iterations
31181      score={0,is} bestscore={max($score,$bestscore)}
31182    fi
31183    wait 60
31184    iteration+=1
31185  while {*}" && "!{*,ESC}" && "!{*,Q}
31186
31187  # End game and quit properly.
31188  rm[0-3] w 0
31189
31190#@cli x_light
31191#@cli : Launch the light effect demo.
31192x_light : check_display $0
31193  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31194  e[] "\n
31195------ "${g}"Light effect"$n" ------------------------\n
31196----\n
31197---- Move light position with "${c}"mouse"$n".\n
31198---- "${c}"Mouse buttons"$n" fade light in/out.\n
31199---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
31200---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
31201---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31202----\n
31203-------------------------------------------"
31204
31205  # Create warping and color images.
31206  0 t. "    G\47MIC\nLight effect",0,0,80,1,255 expand_xy. 15,0 b. 3
31207  . n.. 0,1 r.. 100%,100%,1,3
31208  sh.. 0 *. 120 rm.
31209  sh.. 1 *. 70 rm.
31210  sh.. 0,50%,0,2 *. 120 rm.
31211  25%,25%,1,1 rand. -20,20 smooth. 10,0,1,1,4 ri. ..,3 b. 3 n. -100,100
31212  +[-2,-1] g. xy a[-2,-1] c n. -150,150
31213  w[] {1.5*{-2,w}},{1.5*{-2,h}},0,"[G"{`39`}"MIC] Light Effect"  # Init display window.
31214  cursor[0] 0
31215
31216  # Create a large light image.
31217  light=70
31218  640,640 gaussian. $light n. 0,255
31219  t=0
31220
31221  # Start animation.
31222  do
31223
31224    # Manage light position and intensity.
31225    if {*,x}>=0
31226      X={round((w-{*,x})/2)}
31227      Y={round((h-{*,y})/2)}
31228    else
31229      X={round((w-{-2,w}*(1+cos(2*$t)))/2)}
31230      Y={round((h-{-2,h}*(1+sin(2.5*$t)))/2)}
31231      t+=0.02
31232    fi
31233    if {*,b}&1 light={min(200,$light+10)} gaussian. $light n. 0,255 fi
31234    if {*,b}&2 light={max(10,$light-10)} gaussian. $light n. 0,255 fi
31235
31236    # Render lightened image.
31237    +z. $X,$Y,{$X+{-2,w}-1},{$Y+{-2,h}-1}
31238    warp. ...,1,0,1
31239    r. 100%,100%,1,3 +. [-4] c. 0,255
31240    fps=${-fps} if $fps>0 to. $fps" fps",5,5,16,1,0.2 fi
31241    w.
31242    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
31243    rm. if {*,x}>=0" && "!{*,b} wait else wait 20 fi
31244  while {*}" && "!{*,ESC}" && "!{*,Q}
31245  w[] 0 rm[-3--1]
31246
31247#@cli x_mandelbrot : _julia={ 0 | 1 },_c0r,_c0i
31248#@cli : Launch Mandelbrot/Julia explorer.
31249x_mandelbrot : skip ${1=0},${2=0.317},${3=0.03} check_display $0
31250  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31251  e[] "\n
31252------ "${g}"Mandelbrot/Julia explorer"$n" -----------------\n
31253----\n
31254---- Select zooming region with "${c}"mouse"$n".\n
31255---- "${c}"Click once"$n" to reset zoom factor.\n
31256---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31257---- Key '"${c}"C"$n"' to print current fractal coordinates.\n
31258----\n
31259--------------------------------------------------"
31260
31261  # Init variables and display.
31262  rm w 512,512,0 _x_mandelbrot_coords $1 _x_mandelbrot_palette
31263
31264  # Start event loop.
31265  do
31266    siz={min({*,w},{*,h})}                                                                  # Desired window dimension.
31267    $siz,$siz mandelbrot. {0,^},256,$1,{if($1,$2,0)},{if($1,$3,0)} map. [1]                 # Render fractal.
31268    if $1 w. $siz,$siz,0,"[G"{`39`}"MIC] Julia Set c=("{0,@0-1}")-("{0,@2-3}"), c0=($2,$3)" # Display on window.
31269    else w. $siz,$siz,0,"[G"{`39`}"MIC] Mandelbrot Set c=("{0,@0-1}")-("{0,@2-3}")" fi
31270    w={w} h={h} round. select. 2,0,0,0,1                                                    # Get the user selection.
31271    if i[0]>0                                                                               # If valid selection found.
31272      M={max(i[3]-i[0],i[4]-i[1])} # Compute max dimension of selected rectangle.
31273      if $M<5 _x_mandelbrot_coords $1 rm[1] _x_mandelbrot_palette mv. 1 # If selection too small, reset the view,
31274      else ({{0,@0}+{@0}*({0,@2}-{0,@0})/$w};\                          # Else compute new fractal coordinates.
31275             {{0,@1}+{@1}*({0,@3}-{0,@1})/$h};\
31276             {{0,@0}+({@0}+$M)*({0,@2}-{0,@0})/$w};\
31277             {{0,@1}+({@1}+$M)*({0,@3}-{0,@1})/$h})
31278      fi
31279      rm[0] mv. 0                                                           # Validate new coordinates.
31280    fi
31281    rm.                                                                     # Delete latest rendering.
31282    if {*,C}                                                                # If 'C' key has been pressed.
31283      if $1 e[] "Julia set, at c = ("{0,@0-1}")-("{0,@2-3}"), with c0 = ($2,$3)."
31284      else e[] "Mandelbrot set, at c = ("{0,@0-1}")-("{0,@2-3}")."
31285      fi
31286    fi
31287    if !{*}" || "{*,ESC}" || "{*,Q} break fi
31288    wait -1
31289  while 1 rm w 0
31290
31291_x_mandelbrot_coords :
31292  if $1 (-2;-2;2;2) else (-2.1;-1.5;1.2;1.5) fi
31293
31294_x_mandelbrot_palette :
31295  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
31296
31297#@cli x_mask_color : _colorspace={ all | rgb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq },\
31298# _spatial_tolerance>=0,_color_tolerance>=0
31299#@cli : Interactively select a color, and add an alpha channel containing the corresponding color mask.
31300#@cli : Argument 'colorspace' refers to the color metric used to compute color similarities, and can be basically
31301#@cli : one of { rgb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq }.
31302#@cli : You can also select one one particular channel of this colorspace, by setting 'colorspace' as
31303#@cli : 'colorspace_channel' (e.g. 'hsv_h' for the hue).
31304#@cli : Default values: 'colorspace=all', 'spatial_tolerance=5' and 'color_tolerance=5'.
31305x_mask_color : check "${2=5}>=0 && ${3=5}>=0" skip ${1=all} check_display $0
31306  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31307  e[^-1] "Interactively create color mask for image$?, with color space $1, spatial tolerance $2 and
31308          color tolerance $3."
31309  e[] "\n
31310----------------------------------------------------------------------------------------------------\n
31311----\n
31312---- "${c}"Left mouse button"$n" adds a wanted color to the selection.\n
31313---- "${c}"Right mouse button"$n" adds an unwanted colors to the selection.\n
31314---- "${c}"Middle mouse button"$n" or key '"${c}"R"$n"' resets color mask.\n
31315---- Key '"${c}"SPACE"$n"' or '"${c}"TAB"$n"' toggles view modes (masked RGB or mask).\n
31316---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
31317---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
31318---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
31319---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n
31320----\n
31321----------------------------------------------------------------------------------------------------"
31322  l[] _ac_$1 onfail error[0--3] "Command '$0' : Invalid colorspace '$*'." endl
31323  m _ac_forward:$_f
31324  repeat $! l[$>] slices 0 basename {0,n} nm=${}
31325    wh=${fitscreen\ {[w,h,1]},128,1024}
31326    +r $wh,1,100%,2
31327    +_ac_forward. channels. $_s
31328    if {1,s>3} channels[1] 0,2 fi to_rgb[1]
31329
31330    w[1] 100%,100%,0,$nm
31331    colors_add=-1 colors_sub=-1 visumode=0
31332    is_clicked=0 time=0 delay=0.1
31333    do
31334
31335      # Manage user events.
31336      time={$is_clicked?$time:$|}
31337      wait
31338      x={2,round({*,x}*(w-1)/({*,w}-1))}
31339      y={2,round({*,y}*(h-1)/({*,h}-1))}
31340      b={*,b}
31341      c=$x,$y,{2,I($x,$y)}
31342      is_add={arg(1,$colors_add)>=0}
31343      is_sub={arg(1,$colors_sub)>=0}
31344      is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
31345      is_resized=0
31346      refresh=0
31347
31348      if $x>=0" && "$b&1 # Add a color
31349        if $is_add colors_add=$colors_add,$c else colors_add=$c fi
31350        is_clicked=1
31351        refresh={$|-$time>$delay}
31352      elif $x>=0" && "$b&2 # Subtract a color
31353        if $is_sub colors_sub=$colors_sub,$c else colors_sub=$c fi
31354        is_clicked=1
31355        refresh={$|-$time>$delay}
31356      elif $b&4" || "{*,R} # Reset colors
31357        colors_add=-1 colors_sub=-1 refresh=1 is_clicked=1
31358      elif !$b
31359        refresh={$is_clicked==1}
31360        is_clicked=0
31361      fi
31362      if {*,-TAB}" || "{*,-SPACE} visumode={($visumode+1)%3} refresh=1 fi # Change visu mode
31363      if {*,r} is_resized=1
31364      elif $is_ctrl" && "{*,-D} w[] {1,1.25*[w,h]} is_resized=1
31365      elif $is_ctrl" && "{*,-C} w[] {1,0.8*[w,h]} is_resized=1
31366      elif $is_ctrl" && "{*,R} w[] ${fitscreen\ {0,[w,h,1]},128,1024} is_resized=1
31367      fi
31368      if $is_resized rm[1,2] +r {*,d},{*,e},1,3,2 +_ac_forward. channels. $_s refresh=1 fi
31369
31370      # Refresh view.
31371      if $refresh
31372        _x_mask_color[2] {$2*w#2/w#0},$3,{``$colors_add},{``$colors_sub} delay=${}
31373        if $visumode==0 +. 64 c. 0,255 +a[1,-1] c drgba. w. -1,-1,$nm" [half-masked]" rm.
31374        elif $visumode==1 +a[1,-1] c drgba. w. -1,-1,$nm" [masked]" rm.
31375        else w. -1,-1,$nm" [mask]"
31376        fi
31377        rm.
31378        time=$|
31379      fi
31380
31381    while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER}
31382
31383    # Rescale to original image dimensions.
31384    if arg(1,$colors_add)>=0 ($colors_add)
31385      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)}'
31386      j.. . colors_add={-2,^} rm[-2,-1]
31387    fi
31388    if arg(1,$colors_sub)>=0 ($colors_sub)
31389      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)}'
31390      j.. . colors_sub={-2,^} rm[-2,-1]
31391    fi
31392    rm[-2,-1] +_ac_forward channels. $_s
31393    _x_mask_color. $2,$3,{``$colors_add},{``$colors_sub}
31394    rm.. a c
31395
31396  endl done
31397  um _ac_forward
31398
31399_x_mask_color : # Estimate color mask image.
31400  100%,100%
31401  is_add={arg(1,$3)>=0}
31402  is_sub={arg(1,$4)>=0}
31403  t0=$|
31404  if $is_add" || "$is_sub
31405    if $is_add
31406      ($3) r. {2+s#0},{w/(2+s#0)},1,1,-1
31407      N_add={h} M_add={"M = vectorw(); fill(M,k,med(crop(k,1)));M"} rm.
31408    fi
31409    if $is_sub
31410      ($4) r. {2+s#0},{w/(2+s#0)},1,1,-1
31411      N_sub={h} M_sub={"M = vectorw(); fill(M,k,med(crop(k,1)));M"} rm.
31412    fi
31413    f. "begin(
31414          invert(A,nA) = inv(A,nA); # For <v.2.9.2
31415          const is_add = "$is_add";
31416          const is_sub = "$is_sub";
31417          const ss = sqrt(2)*$1;
31418          const sc = sqrt(2)*$2;
31419          colors_add = [ $3 ];
31420          colors_sub = [ $4 ];
31421          M_add = [ 0"$M_add"];
31422          M_sub = [ 0"$M_sub"];
31423          const N_add = 0"$N_add";
31424          const N_sub = 0"$N_sub";
31425          const siz = 2 + s#0;
31426          const siz2 = sqr(siz);
31427          sigma = vectorsiz(sc);
31428          sigma[0] = sigma[1] = ss;
31429
31430          tensor(op) = (
31431            T = vectorsiz2();
31432            if (is_#op,
31433              for (k = 0, k<size(colors_#op), k+=siz, C = colors_#op[k,siz]-=M_#op; C*=sigma; T+=mul(C,C,siz));
31434              T/=1e-8 + N_#op;
31435              T+=eye(siz);
31436              T = invert(T);
31437            ); T
31438          );
31439
31440          T_add = tensor(add);
31441          T_sub = tensor(sub);
31442        );
31443        P = [ x,y,I#0 ];
31444        pot_add = is_add?(C = P - M_add; exp(-dot(C,T_add*C))):1;
31445        pot_sub = is_sub?(C = P - M_sub; exp(-dot(C,T_sub*C))):0;
31446        pot_add - pot_sub"
31447    c. 0,1 n. 0,512 c. 0,255
31448  else f. 255
31449  fi
31450  u {$|-$t0}
31451
31452#@cli x_metaballs3d
31453#@cli : Launch the 3D metaballs demo.
31454x_metaballs3d : check_display $0
31455  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31456  e[] "\n
31457------ "${g}"3D metaballs"$n" ---------------------------------------\n
31458----\n
31459---- "${c}"Mouse button"$n" or '"${c}"SPACE"$n"' key to toggle rendering mode.\n
31460---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
31461---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
31462---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31463----\n
31464-----------------------------------------------------------"
31465  l[]
31466  100,100 noise. 100,1 plasma. 1,0,10 r. 512,320,1,3 n. 0,1 b. 4,0 n. 0,255
31467  mix_channels. (0.7,0,0;0,0.9,0;0,0,1.2) c. 0,255 l3d
31468  0
31469  24,24,24,1,'X=x-w/2;Y=y-h/2;Z=z-d/2;exp(-(X*X+Y*Y+Z*Z)/100)'
31470  72,72,72 M=8 mode=3
31471  s0=Dots s1=Wireframe s2=Flat s3=Flat-shaded s4=Gouraud-shaded s5=Phong-shaded
31472  repeat $M fx$>={2*g} fy$>={2*g} fz$>={2*g} done
31473  w[0] -1,-1,0,"[G"{`39`}"MIC] 3D Metaballs"
31474  do
31475    repeat $M
31476      x$>={w/2+0.5*(w-{2,w}-4)*cos(${fx$>}*$|)}
31477      y$>={h/2+0.5*(h-{2,h}-4)*sin(${fy$>}*$|)}
31478      z$>={d/2+0.5*(d-{2,d}-4)*sin(${fz$>}*$|)}
31479    done
31480    f[3] 0 repeat $M j[3] [2],{${x$>}-{2,w/2}},{${y$>}-{2,h/2}},{${z$>}-{2,d/2}},0,-1 done
31481    +r[3] 28,28,28,1,2 isosurface3d. 0.4 -3d. 12,12,12 *3d. 13 rv3d.
31482    r3d. 1,2,1,{100*$|}
31483    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.
31484    if !$mode circles3d.. 4 fi
31485    if !{1,w}
31486      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
31487      +dilate. 3 +j[0] ..,5,3,0,0,1,.,255 mv. 1 rm[2,-2,-1]
31488    fi
31489    +j3d[1] ..,50%,50%,0,1,{if(!$mode,3,$mode)},0,0,300,0,0,-500,0.1,1.5
31490    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,2,0.2 fi
31491    w.
31492    if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
31493    rm[-3--1] wait 20
31494    if {*,b}" || "{*,SPACE} mode={($mode+if({*,b}&2,-1,1))%6} wait -1 rm[1] i[1] 0 fi
31495  while {*}" && "!{*,ESC}" && "!{*,Q}
31496  rm w 0 endl
31497
31498#@cli x_minesweeper : 8<=_width=<20,8<=_height<=20
31499#@cli : Launch the Minesweeper game.
31500x_minesweeper : check "${1=20}>=8 && $1<=30 && ${2=$1}>=8 && $2<=30" check_display $0
31501  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31502  e[] "\n
31503------ "${g}"Minesweeper"$n" -------------------------------------------\n
31504----\n
31505---- The goal is to "${c}"clear the minefield"$n" without detonating a\n
31506---- mine.\n
31507----\n
31508---- "${c}"Left mouse button"$n" to try clearing one square.\n
31509---- "${c}"Right mouse button"$n" to flag or unflag a square.\n
31510---- "${c}"Middle mouse button"$n" to reset mine field.\n
31511---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31512----\n
31513--------------------------------------------------------------\n"
31514
31515  # Generate random mine field and player board.
31516  # Labels : 0=mine, 1=empty, 2='1-near', 3='2-near', ..., 9='8-near', 10=still unknown.
31517  $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
31518  do x={round(u(w-1))} y={round(u(h-1))} while i($x,$y)!=1 # Find a good starting point.
31519  +f[field] 11 =. 12,$x,$y nm. board
31520
31521  # Generate sprite graphics.
31522  24,24,1,3,200 fc. 255,180,130
31523  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
31524  z. 1,1,{w-2},{h-2} frame. 1,1,0
31525  +fc. 230,250,255
31526  +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
31527  +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
31528  +t[-7] "7",9,5,13,1,128,0,0 +t[-8] "8",9,5,13,1,64,0,0
31529  +f. 'if(x<=1||y<=1||x>=w-2||y>=h-2,if(x<y,128,255),160+2*(y+x))'
31530  +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
31531  rv[-2,-1]
31532  . 100%,100%,1,3 line. 6,14,10,18,1,0,200,0 line. 10,18,16,6,1,0,200,0 dilate. 2
31533  +channels. 1 n. 0,0.7 dilate. 3 j... ..,0,0,0,0,1,. rm[-2,-1]
31534  a[-13--1] x nm. sprites
31535
31536  # Pre-calculate offsets and canvas for faster board rendering.
31537  (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
31538  .,.,1,3,255 frame. 1,1,0 frame. 23,23,255
31539  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.
31540  nm. canvas
31541
31542  # Start user interaction loop.
31543  failed=0 succeeded=0 nb_flags=0 started=0
31544  do
31545
31546    # Render board.
31547    +*[board] 24 r. [offsets],[offsets] channels. 0,1 +. [offsets] +warp[sprites] .,0,0 rm..
31548    j[canvas] .,24,24 rm.
31549
31550    # Wait for user's selection.
31551    wait -1
31552    if $failed
31553      0 t. "Game\nOver!",3,3,38,1,255 r. 100%,100%,1,4 sh. 3 dilate. 5 /. 2 rm.
31554      drop_shadow. 5,5,1 blend[canvas,-1] alpha
31555      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.
31556      do w[canvas] {w},{h} wait while {*}" && "!{*,ESC}" && "!{*,Q}
31557    elif $succeeded
31558      0 t. "Success!",3,3,38,1,255 r. 100%,100%,1,4 sh. 3 dilate. 5 /. 2 rm.
31559      drop_shadow. 5,5,1 blend[canvas,-1] alpha
31560      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
31561      negate. j[canvas] .,0,3 rm.
31562      do w[canvas] {w},{h} wait while {*}" && "!{*,ESC}" && "!{*,Q}
31563    else
31564      +==[board] 10 nb_flags={is} rm.
31565      do
31566        if !$started tic=$| fi
31567        0 t. "Elapsed time : "{round($|-$tic)}" s / Flags : "$nb_flags,0,0,18,1,255,200,0
31568        r. {canvas,w},100%,1,3,0,0,0.5,0.5
31569        negate. j[canvas] .,0,3 rm.
31570        wait 50
31571        x={int(({*,x}-24)/24)} y={int(({*,y}-24)/24)} b={*,b}
31572        w[canvas] {w},{h},0,"[G"{`39`}"MIC] Minesweeper"
31573      while {*}" && "!{*,ESC}" && "!{*,Q}" && "!$b
31574    fi
31575
31576    # Manage selected square.
31577    if $x>=0\ &&\ $y>=0\ &&\ $x<{board,w}\ &&\ $y<{board,h}
31578      if $b&1  # Try to clean square.
31579        started=1 val={field,i($x,$y)}
31580        if $val==0 +==[field] 0 j[board] [field],0,0,0,0,1,. rm. failed=1  # Found a mine -> boom!
31581        elif $val==1 # Found an empty area
31582          +flood[field] $x,$y,0,0,1,1,-1 ==. -1 dilate. 3 j[board] [field],0,0,0,0,1,. rm.
31583        else =[board] $val,$x,$y # Close to one or several mines
31584        fi
31585      elif n={board,i($x,$y)};$b&2" && "n>=10" && "n<=11
31586        =[board] {if({board,i($x,$y)}==11,10,11)},$x,$y # Flag or unflag a square.
31587      elif $b&4 f[board] 10  # Reset minefield.
31588      fi
31589    fi
31590
31591    if $nb_mines==$nb_flags\ &&\ {board,iM}!=11 succeeded=1 fi  # Check if board is cleared.
31592  while {*}" && "!{*,ESC}" && "!{*,Q}
31593  w 0
31594
31595#@cli x_minimal_path
31596#@cli : Launch the minimal path demo.
31597x_minimal_path : check_display $0
31598  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31599  e[] "\n
31600------ "${g}"Minimal path"$n" ------------------------------------------\n
31601----\n
31602---- "${c}"Click on two points"$n" to compute and display the minimal\n
31603---- path between those points. The ending point is then\n
31604---- chosen as the next starting point for another path.\n
31605---- Key '"${c}"S"$n"' to save snapshot of the current view.\n
31606---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31607----\n
31608--------------------------------------------------------------"
31609
31610  if !$! sp ? fi
31611  n 0,200 round 1
31612  repeat $! l[$>]
31613    w[0] -1,-1,0,"[G"{`39`}"MIC] Select Starting Point P0"
31614
31615    if !narg($first_time)
31616      parallel 0,"+l[0] r2dy 128 frame 1,1,0 \
31617        alert \"[G"{`39`}"MIC Minimal Path]\",\
31618           \"The G\47MIC minimal path demo illustrates how minimal paths\n\
31619           can be computed in images to detect and track edge points.\n\
31620           Use your mouse to select desired starting and ending points,\n\
31621           and see what is the minimal path computed between these points.\",\
31622           \"OK\" \
31623        rm endl"
31624      first_time=0
31625    fi
31626
31627    +gradient_norm b. 1 f. exp(-i/10)
31628    to_rgb[0] +select[0] 0 P0={^}
31629    ellipse[0] {@0,1},3,3,0,1,255,0,255
31630    ellipse[0] {@0,1},3,3,0,1,0xFFFFFFFF,255,255,255
31631    rm.
31632    if min($P0)>=0
31633      p=1
31634      do
31635        w[0] -1,-1,0,"[G"{`39`}"MIC] Select Ending Point P"$p
31636        +select[0] 0
31637        if {*,S}
31638          rm.
31639          +to[0] "Saving snapshot...",5,5,13,1,1,255,255,255 w. rm.
31640          o[0] gmic_minimal_path.png
31641          wait -1
31642        else
31643          P1={^}
31644          ellipse[0] {@0,1},3,3,0,1,255,0,255
31645          ellipse[0] {@0,1},3,3,0,1,0xFFFFFFFF,255,255,255
31646          rm.
31647          if min($P1)>=0
31648            +to[0] "Processing...",5,5,13,1,1,255,255,255 w. rm.
31649            +minimal_path[1] $P0,$P1,1 transpose.
31650            pointcloud. 0 *. 255 r. 100%,100%,1,[0],0,0,0,0,0,0.5 ri. [0],0 -|[0,-1]
31651            P0=$P1 p+=1
31652          fi
31653        fi
31654      while {*}" && "!{*,ESC}" && "!{*,Q}
31655    fi
31656    rm[1] w 0
31657  endl done
31658
31659#@cli x_morph : _nb_frames>=2,_preview_fidelity={ 0=coarsest | 1=coarse | 2=normal | 3=fine | 4=finest }
31660#@cli : Launch the interactive image morpher.
31661#@cli : Default values: 'nb_frames=16' and 'preview_fidelity=3'.
31662x_morph : check "isint(${1=16}) && isint(${2=3}) && $2>=0 && $2<=4" check_display $0
31663  if $!<2 error[0--3] "Command '$0': Requires at least two input images!" return fi
31664  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r b=$_vt100_b
31665  e[] "\n
31666------ "${g}"Interactive image morpher"$n" ---------------------------\n
31667----\n
31668---- "${b}"Source/target window:"$n"\n
31669---- "${c}"Left mouse button"$n": Add new keypoint on current image\n
31670----  and move it on the other one.\n
31671---- "${c}"Right mouse button"$n": Add/move keypoint on current image.\n
31672---- Key '"${c}"DELETE"$n"' or "${c}"middle mouse button"$n": Delete keypoint.\n
31673---- Key '"${c}"SPACE"$n"' or "${c}"mouse wheel"$n": Toggle source/target.\n\n
31674---- "${b}"In-between window:"$n"\n
31675---- "${c}"Mouse wheel"$n": Change morphing time, from 0 to 1.\n
31676---- "${c}"Left mouse button"$n": Reset morphing time to 0.5.\n\n
31677---- "${b}"Both windows:"$n"\n
31678---- Key '"${c}"TAB"$n"': Change keypoint radius.\n
31679---- Key '"${c}"ENTER"$n"': Play/stop in-between animation.\n
31680---- Key '"${c}"R"$n"': Reset keypoints.\n
31681---- Key '"${c}"K"$n"': Show/hide keypoints.\n
31682---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"': Process fullres and exit.\n
31683----\n
31684-----------------------------------------------------------"
31685
31686  offset=0
31687  radius_keypoints=3
31688  repeat $!-1 l[{[$>,$>+1]+$offset}] nm0={0,n} nm1={1,n}
31689    to_colormode 0
31690    rr2d ${-max_wh},2
31691    nm img0,img1
31692    256,1,1,1,"x>=4?x-2:1" map. 2 nm. colormap
31693
31694    # Start interactive window.
31695    selected_keypoint=-1
31696    view_keypoints=1
31697    time_inbetween=0.5
31698    animate_inbetween=0
31699    move_other=0
31700    frame=0
31701
31702    do
31703      # Generate set of keypoints (x0,y0,x1,y1,index_color)
31704      if !narg($keypoints)
31705        if narg($__x_morph_keypoints) ($__x_morph_keypoints) r. 1,{w/5},1,5,-1
31706        else 1,4,1,5,"p = y%2; q = int(y/2); [ [ p,q,p,q ]*100,y ]"
31707        fi
31708        N0={h} nm. keypoints
31709        rmn warp0,warp1
31710      fi
31711
31712      # Generate base images.
31713      if !narg($imgb0)" || "!narg($imgb1)
31714        if {*} wdims=${"fitscreen "{*,d},{img0,{*,d}*h/w}}
31715        else wdims=${"fitscreen "{img0,[w,h,1]},128,50%}
31716        fi
31717        w[] $wdims,0,"[G'MIC] Interactive Morph"
31718        repeat 2 +to_color[img$>] r. $wdims,1,100%,2 n. 0,255 nm. imgb$> done
31719        rmn imgr,warp0,warp1
31720      fi
31721
31722      # Generate warp fields.
31723      if (!narg($warp0)" || "!narg($warp1))
31724        subsamp={arg(1+$2,8,6,4,2,1)}
31725        +_x_warp_rbf[keypoints] {imgb0,round([w,h]/$subsamp)}
31726        +l[keypoints] s c,-2 rv[0,1] a c endl +_x_warp_rbf. {imgb0,round([w,h]/$subsamp)} rm..
31727        *[-2,-1] $subsamp
31728        r[-2,-1] {imgb0,[w,h]},1,100%,3
31729        nm[-2,-1] warp0,warp1
31730        rmn imgm
31731      fi
31732
31733      # Render visualization.
31734      if !narg($imgr)
31735        [imgb$frame] nm. imgr
31736        if s==4 drgba. fi
31737        if $view_keypoints
31738          eval[keypoints] "*
31739            begin(
31740              fact = ([ w#"$imgb0",h#"$imgb0" ] - 1)/100;
31741              const radius1 = "$radius_keypoints";
31742              const radius2 = radius1 + 2;
31743              const opacity = min(1,3/"($radius_keypoints-1)");
31744            );
31745            X = round((I)[2*"$frame",2]*fact);
31746            y=="$selected_keypoint"?(ellipse(#-1,X,radius2+2,radius2+2,0,1,255));
31747            ellipse(#-1,X,radius2,radius2,0,opacity,0);
31748            ellipse(#-1,X,radius1,radius1,0,opacity,I[#"$colormap",i4]); I"
31749        fi
31750        w. -1,-1,0,"[G'MIC] Interactive Morph ("${"s0=Source s1=Target u ${s"$frame"}"}")"
31751      fi
31752
31753      # Generate morph image
31754      if !narg($imgm)
31755        t,onemt={t=$animate_inbetween?0.5*(1+sin(2*($time_inbetween+$|-$animate_inbetween))):$time_inbetween;[t,1-t]}
31756        +*[warp1] $t *. -1 +warp[imgb0] .,1,2,3 rm.. *. $onemt
31757        +*[warp0] $onemt *. -1 +warp[imgb1] .,1,2,3 rm.. *. $t
31758        +[-2,-1] c. 0,255 nm. imgm
31759        w1. -1,-1,0,"[G'MIC] Interactive Morph (In-between)"
31760      fi
31761
31762      # Manage user interaction
31763      if $animate_inbetween wait 40 else wait fi
31764      mb={*,b} mxy={[{*,x,y}]*100/([{*,w,h}]-1)} mouse_over={{*,x}>=0}
31765
31766      if $mouse_over" && "($mb" || "{*,DELETE})" && "$selected_keypoint<0" && "h#$keypoints>0
31767
31768        # Determine selected keypoint.
31769        selected_keypoint={keypoints,"dmin = inf; kmin = -1; fact = ([w#"$imgr",h#"$imgr"]-1)%;
31770          repeat (h,k,
31771            dist = norm(((I[k])[2*"$frame",2] - ["$mxy"])*fact);
31772            dist<dmin?(dmin = dist; kmin = k)
31773          );
31774          kmin>=0 && dmin<"max(8,1.5*$radius_keypoints)"?kmin:-1"}
31775      fi
31776
31777      if {*,-o}" || "{*,-SPACE}" || "{*1,-SPACE} frame={!$frame} rmn imgr fi # Swap frames
31778      if {*,-K}" || "{*1,-K} view_keypoints={!$view_keypoints} rmn imgr fi # Show/hide keypoints
31779      if {*,-R}" || "{*1,-R} rmn keypoints,imgr __x_morph_keypoints= fi # Reset keypoints
31780      if {*,-TAB}" || "{*1,-TAB} # Change keypoint radius
31781        radius_keypoints={max(2,($radius_keypoints+2)%8)} view_keypoints=1 rmn imgr
31782      fi
31783      if {*,-ENTER}" || "{*1,-ENTER} animate_inbetween={$animate_inbetween?0:$|} fi # Start in-between animation
31784      if {*,r} rmn imgb0,imgb1 fi # Window resize
31785      if {*1,r} rmn imgm fi
31786      if {*1,o} # Change time of in-between
31787        time_inbetween={max(0,min(1,$time_inbetween+0.05*{*1,-o}))} animate_inbetween=0 rmn imgm
31788      fi
31789      if {*1,-b} time_inbetween=0.5 animate_inbetween=0 rmn imgm fi # Reset in-between time
31790
31791      if $mouse_over" && "($mb==1" || "$mb==2)" && "$selected_keypoint<0 # Add keypoint
31792        if $mb==1" && "!$move_other frame={!$frame} move_other=1 fi # Move on other window
31793        ({keypoints,[$mxy,$mxy,h]})
31794        permute. zycx a[keypoints,-1] y
31795        selected_keypoint={keypoints,h-1}
31796        rmn imgr
31797
31798      elif $mouse_over" && "($mb==1" || "$mb==2)" && "$selected_keypoint>=0 # Move keypoint
31799        if $mb==1" && "!$move_other frame={!$frame} move_other=1 fi # Move on other window
31800        ({[$mxy]}) permute. zycx j[keypoints] .,0,$selected_keypoint,0,{2*$frame} rm.
31801        rmn imgr
31802
31803      elif $mouse_over" && "$selected_keypoint>=0" && ("{*,-DELETE}" || "$mb==4") && "h#$keypoints>4 # Delete keypoint
31804        1,1,1,5,-1 j[keypoints] .,0,$selected_keypoint rm. discard[keypoints] -1 r[keypoints] 1,{keypoints,h/5},1,5,-1
31805        N0-={$selected_keypoint<$N0?1:0} selected_keypoint=-1
31806        rmn warp0,warp1,imgr
31807
31808      elif !{*,b}" && "$selected_keypoint>=0
31809        if $move_other frame={!$frame} move_other=0 fi
31810        selected_keypoint=-1
31811        rmn warp0,warp1,imgr
31812      fi
31813
31814      if $animate_inbetween rmn imgm fi
31815    while {*}" && "!{*,ESC}" && "!{*,Q}" && "{*1}" && "!{*1,ESC}" && "!{*1,Q}
31816    if !$< __x_morph_keypoints={keypoints,^} fi
31817
31818    # Render fullres morphing.
31819    rmn colormap,imgb0,imgb1,warp0,warp1,imgr,imgm
31820    +_x_warp_rbf[keypoints] {img0,[w,h]}
31821    +l[keypoints] s c,-2 rv[0,1] a c endl +_x_warp_rbf. {img0,[w,h]} rm..
31822    nm[-2,-1] warp0,warp1
31823
31824    repeat $1
31825      t,onemt={t=$>/max(1,$1-1);[t,1-t]}
31826      +*[warp1] $t *. -1 +warp[img0] .,1,2,3 rm.. *. $onemt
31827      +*[warp0] $onemt *. -1 +warp[img1] .,1,2,3 rm.. *. $t
31828      +[-2,-1] c. 0,255 nm. ${nm0}_$>
31829      if {*}" && "{*1} w1 0 fi
31830      if {*}" || "{*1}
31831        text="Processing frame \#"$>"/"{$1-1}"..."
31832        if {*} +r. {*,d,e},1,100%,2 to. $text,5,5,20,2 w. rm. fi
31833        if {*1} +r. {*1,d,e},1,100%,2 to. $text,5,5,20,2 w1. rm. fi
31834      fi
31835    done
31836    nm. $nm1 k[-$1--1]
31837    offset+={$!-2}
31838  endl done w0 0 w1 0
31839
31840#@cli x_pacman
31841#@cli : Launch pacman game.
31842x_pacman : check_display $0
31843  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
31844  e[] "\n
31845------ "${g}"Pacman"$n" -----------------------------------------------\n
31846----\n
31847---- This is a G\47MIC implementation of the "${g}"pacman"$n" game.\n
31848----\n
31849---- Move the pacman to eat all pacdots on the different levels.\n
31850---- Eating a pacgum makes pacman invincible for "${c}"10 seconds"$n",\n
31851---- which mean pacman can eat ghosts during this time.\n
31852---- Eating a ghost earns "${c}"100 pts"$n".\n
31853---- Eating a cherry earns "${c}"10 pts"$n".\n
31854---- Eating a strawberry earns "${c}"100 pts"$n".\n
31855---- Eating an orange earns "${c}"1000 pts"$n".\n
31856---- Eating a banana earns "${c}"5000 pts"$n".\n
31857----\n
31858---- "${c}"Arrow keys"$n" to control pacman.\n
31859---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
31860---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
31861---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
31862----\n
31863--------------------------------------------------------------"
31864  l[]
31865
31866  # Initialize characters gfx.
31867  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"
31868  repeat 4
31869    _pacman_ghost_standard_gfx 255,0,0,$> nm. ghost0_$>
31870    _pacman_ghost_standard_gfx 0,255,222,$> nm. ghost1_$>
31871    _pacman_ghost_standard_gfx 255,184,222,$> nm. ghost2_$>
31872    _pacman_ghost_standard_gfx 255,184,71,$> nm. ghost3_$>
31873    _pacman_ghost_afraid_gfx $> nm. ghosta_$>
31874    _pacman_ghost_base_gfx $> r. 16,16,1,1,2 nm. ghostm_$>
31875    _pacman_ghost_standard_gfx 0,0,0,$> nm. ghostd_$>
31876    _pacman_pacman_gfx $> nm.. pacman_$> nm. pacmanm_$>
31877  done
31878  +channels[ghostd_0] 0 !=. 0 nm. ghostdm
31879  _pacman_cherry_gfx nm. fruit0 _pacman_strawberry_gfx nm. fruit1
31880  _pacman_orange_gfx nm. fruit2 _pacman_banana_gfx nm. fruit3
31881  20,2,1,3,200 nm. gate
31882  score0,score1,score2,score3,score4=10,100,1000,5000,"Argh!"
31883  repeat 5
31884    0 t. ${score$>},0,0,13,1,255,255,255 autocrop. 0 expand_xy. 1,0 +dilate. 3
31885    nm. scorem$> nm.. score$>
31886  done
31887  time4=255,255,255 time3=255,255,32 time2=255,128,32 time1=255,32,32
31888  repeat 11 0 t. $<" s",0,0,23,1,${time{min(4,round(($<+1)/2))}} nm. time$< done
31889  0 t. "Get Ready!",0,0,32,1,255 autocrop. 0 expand_xy. 4,0 +dilate. 8 r.. 100%,100%,1,3
31890  nm.. get_ready nm. get_readym
31891  0 t. "Game\nOver!",0,0,53,1,255 autocrop. 0 expand_xy. 4,0 +dilate. 8 r.. 100%,100%,1,3
31892  nm.. game_over nm. game_overm
31893
31894  # Start game.
31895  score=0 level=-1 lives=3 is_quit=0
31896  do
31897
31898    # Build new level if necessary.
31899    if $level<0
31900      _rlevel=33 _glevel=33 _blevel=255
31901      _pacman_map_level{((-$level-1)%6)+1} mw={w} mh={h} mw2={int(w/2)} mh2={int(h/2)}
31902      if $level<-6 replace. 3,2 fi
31903      . nm[-2,-1] map0,map
31904
31905      # Precompute valid directions on each map point, and shortest path to the ghost's home.
31906      +shift[map] -1,0 +shift[map] 0,-1 +shift[map] 1,0 +shift[map] 0,1 a[-4--1] z !=. 1 nm. can_go
31907      +==[map] 1 100%,100% =. 1,$mw2,$mh2 distance. 1,..,3 channels. 100%
31908      f. 'if(i==2,0,if(i==8,1,if(i==1,2,if(i==4,3,i))))' nm. path
31909      +==[map] 2 pacdots={is} rm.
31910      level={-$level}
31911    fi
31912
31913    # Render board gfx.
31914    f[map] 'if(i>=4,0,i)' +==[map] 1 expand_xy. 1,0 r. 1600%,1600% erode. 9 b. 2
31915    g. xy abs[-2,-1] +[-2,-1] >=. 80% b. 2 n. 0,1 shrink_xy. 16
31916    +*. $_glevel +*.. $_blevel *... $_rlevel a[-3--1] c
31917    16,16,1,1,'x' +-[map] 1 max. 0 *. 16 r. 1600%,1600%
31918    16,16,1,1,'y' ri[-3,-1] ..,0,2 +[-2,-1] a[-2,-1] c
31919    16,16,1,3 _pacman_pacdots_gfx _pacman_pacgum_gfx a[-3--1] y
31920    warp. ..,0,0 rm.. -|[-2,-1] r. 100%,{h+24},1,3,0,0,0,1
31921    t. "Lives :",10,0,24,1,255 t. "Score :",{w-140},0,24,1,255
31922    if $lives +r[pacman_2] 12,12,1,4,2 r. {100*$lives}%,100%,1,4,0,2 j.. .,90,7 rm. fi
31923    nm. visu
31924    w[visu] {visu,f=h<0.5*{*,v}?1.5:1;[w,h]*=f},0,"[G"{`39`}"MIC] Pacman" cursor[0] 0
31925    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
31926    nm. level_N nm.. levelm_N
31927
31928    repeat 4 xg$>={16*$mw2} yg$>={16*$mh2+4*$>} dg$>=3 mg$>=0 done
31929    xp={16*10} yp={16*21} dp=-1 pacgum_timer=-1 fruit_timer=$| dying_pacman=0 is_get_ready=1
31930    xscore=0 yscore=0 nscore=0 oscore=0
31931
31932    # Start game interaction.
31933    do
31934
31935      # Display board graphics.
31936      t={int(6*$|)%4} left={if($pacgum_timer>=0,10-$|+$pacgum_timer,-1)}
31937      [visu]
31938      repeat 4
31939        mg=${mg$>} xg=${xg$>} yg={${yg$>}+24}
31940        if $mg==0 j. [ghost$>_$t],$xg,$yg,0,0,1,[ghostm_$t]
31941        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]
31942        elif $mg==2 j. [ghostd_$t],$xg,$yg,0,0,0.8,[ghostdm]
31943        else j. [ghost$>_$t],$xg,$yg,0,0,{$mg-2},[ghostm_$t] j. [ghostd_$t],$xg,$yg,0,0,1,[ghostdm]
31944        fi
31945      done
31946      if $dying_pacman
31947        _pacman_pacman_gfx {$dying_pacman/2} rotate[-2,-1] {90*(abs($dp)-1)} j... ..,$xp,{24+$yp},0,0,1,.,255 rm[-2,-1]
31948        dying_pacman+=1
31949        if $dying_pacman>64
31950          if $lives!=1 rm. break fi
31951            j. [game_over],{(w-{game_over,w})/2},{12+(h-{game_over,h})/2},0,0,{min(1,($dying_pacman-64)/50)},\
31952               [game_overm],255
31953          rectangle. 90,7,101,18,1,0
31954        fi
31955      else
31956        +rotate[pacman_$t,pacmanm_$t] {90*(abs($dp)-1)} j... ..,$xp,{24+$yp},0,0,1,.,255 rm[-2,-1]
31957        if $left>=0" && "($left>=5" || "$t<=2) j. [time{round($left)}],{(w-{time0,w})/2-10},1 fi
31958      fi
31959      t. $score,{w-60},3,20,1,255
31960      if $is_get_ready
31961        j. [level_N],{(w-{level_N,w})/2},{12+(h-1.5*{level_N,h})/2},0,0,1,[levelm_N]
31962        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
31963      fi
31964      if $oscore>0 j. [score$nscore],$xscore,$yscore,0,0,$oscore,[scorem$nscore],255 oscore-=0.04 yscore-=1 fi
31965      j. [gate],158,223,0,0,0.6
31966      w.
31967      if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h}
31968      elif {*,CTRLLEFT}" && "{*,C} w[] {f=h<0.5*{*,v}?1.5:1;[w,h]*=f}
31969      fi
31970      rm.
31971
31972      # Manage ghosts displacements and collisions.
31973      repeat 4
31974        xg=${xg$>} yg=${yg$>} dg=${dg$>} mg=${mg$>}
31975
31976        if max(abs($xg-$xp),abs($yg-$yp))<=8              # Test collision between ghost and pacman.
31977          if $mg==0" && "!$dying_pacman dying_pacman=1    # Was in normal mode -> dying pacman.
31978            xscore=$xp yscore={$yp+12} oscore=1 nscore=4
31979          elif $mg==1 mg=2 mg$>=$mg score+=100            # Was in invicibility mode -> dying ghost.
31980            xscore=$xp yscore={$yp+12} oscore=1 nscore=1
31981          fi
31982        fi
31983        if $mg>=2" && "($xg>>4)==$mw2" && "($yg>>4)==$mh2 # Check if dying ghost has returned to home.
31984          mg+=0.01
31985          if $mg>=3 mg=0 xg&=-2 yg&=-2 fi
31986          mg$>=$mg
31987        fi
31988
31989        if !($xg&15)" && "!($yg&15) # Check if ghost can take a new direction.
31990          ({u},{u},{u},{u};0,1,2,3)
31991          if $mg<2 # Try to chase or escape pacman
31992            =. {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)),\
31993                                     dX1=$xp-$xg;dY1=$yp-$yg;if(abs(dX1)<abs(dY1),if(dX1>0,2,0),if(dY1>0,3,1)))}
31994            =. 0,{($dg+2)%4}
31995            if $is_get_ready =. 0.8,{path,i({$xg>>4},{$yg>>4})} fi
31996          else =. 1,{path,i({$xg>>4},{$yg>>4})} # If dying ghost, follow the best path to home.
31997          fi
31998          sort. -,x
31999          repeat 4 d={i($>,1)} # Try directions until it matches.
32000            if {can_go,i({$xg>>4},{$yg>>4},$d)} dg=$d break fi
32001          done rm.
32002          dg$>=$d
32003        fi
32004        u={D=${dg$>};(D==0)-(D==2)}
32005        v={D=${dg$>};(D==1)-(D==3)}
32006        xg$>={($xg+$u*(1+($mg==0)))%(16*$mw)}
32007        yg$>={($yg+$v*(1+($mg==0)))%(16*$mh)}
32008      done
32009
32010      wait 22
32011
32012      # Manage pacman displacement.
32013      if !$dying_pacman
32014        d={if({*,ARROWRIGHT},1,if({*,ARROWDOWN},2,if({*,ARROWLEFT},3,if({*,ARROWUP},4,$dp))))}
32015        if !($xp&15)" && "!($yp&15)
32016          i={map,i({$xp>>4},{$yp>>4})}
32017          if $i==2 score+=10 pacdots-=1 # Pacdot eaten.
32018          elif $i==3 pacgum_timer=$| repeat 4 if !${mg$>} mg$>=1 dg$>={(${dg$>}+2)%4} fi done  # Pacgum eaten.
32019          elif $i>=4 score+={${score{$i-4}}} xscore=$xp yscore={$yp+12} oscore=1 nscore={$i-4} # Fruit eaten.
32020          fi
32021          =[map] 0,{$xp>>4},{$yp>>4}
32022          16,16,1,3 j[visu] .,$xp,{24+$yp} rm.
32023          d={if({can_go,i({$xp>>4},{$yp>>4},{abs($d)-1})},$d,$dp)}
32024          d={if({can_go,i({$xp>>4},{$yp>>4},{abs($d)-1})},$d,-abs($dp))}
32025          dp=$d
32026        else dp={if(abs($d-$dp)==2,$d,$dp)}  # Allow to turn back on non-integer locations.
32027        fi
32028        is_get_ready={if($dp>0,0,$is_get_ready)}
32029        u={($dp==1)-($dp==3)}
32030        v={($dp==2)-($dp==4)}
32031        xp={($xp+2*$u)%(16*$mw)}
32032        yp={($yp+2*$v)%(16*$mh)}
32033
32034        if $pacgum_timer>=0" && "$|>$pacgum_timer+10 # Check if pacgum still has some effect.
32035          repeat 4 xg$>&=-2 yg$>&=-2 mg$>={if(${mg$>}==1,0,${mg$>})} done
32036          pacgum_timer=-1
32037        fi
32038
32039        if !$is_get_ready" && "($|-$fruit_timer)>=10
32040          x={round(u(0,{map0,w}))}
32041          y={round(u(0,{map0,h}))}
32042          if !{map,i($x,$y)}" && "{map0,i($x,$y)}==2
32043            n={min(3,int(abs(g*1.7)))} =[map] {4+$n},$x,$y j[visu] [fruit$n],{16*$x},{16*$y+24} fruit_timer=$|
32044          fi
32045        fi
32046
32047      fi
32048      if !{*}" || "{*,Q}" || "{*,ESC} is_quit=1 fi
32049
32050    while !$is_quit" && "$pacdots
32051
32052    if $is_quit break         # Player asked to quit
32053    elif $pacdots             # Player lost a life
32054      lives-=1
32055    else                      # Player achieved level
32056      level={-$level-1} wait[0] -1
32057      rm[map0,map,can_go,path]
32058    fi
32059    rm[visu,level_N,levelm_N]
32060  while $lives
32061  rm w 0 endl
32062
32063# The functions below create the various sprite gfx.
32064_pacman_ghost_standard_gfx :
32065  _pacman_ghost_base_gfx $4 (0,$1^0,$2^0,$3) map.. . rm.
32066  ellipse. 10,11,3,4,0,1,255 ellipse. 20,11,3,4,0,1,255
32067  r. 16,16,1,3,2 point. 5,{7-($4>=2)},0,1,1 point. 10,{7-($4>=2)},0,1,1
32068
32069_pacman_ghost_afraid_gfx :
32070  _pacman_ghost_base_gfx $1
32071  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
32072  map.. . rm. r. 16,16,1,3,2
32073  line. 4,4,6,6,1,$col,255 line. 4,6,6,4,1,$col,255
32074  line. 9,4,11,6,1,$col,255 line. 9,6,11,4,1,$col,255
32075  f. 'if(y>=9&&y<=10&&x>=2&&x<=13&&((int((x+1)/2)+y)%2),arg(c+1,$col),i)'
32076
32077_pacman_pacman_gfx :
32078  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'
32079  (0,255^0,255^0,0^0,255) map.. . rm. r. 16,16,1,4,2 s. c,-3
32080
32081_pacman_pacdots_gfx :
32082  (255^184^151) r. 4,4,1,3 r. 16,16,1,3,0,0,0.5,0.5
32083
32084_pacman_pacgum_gfx :
32085  64,64,1,3 circle. 31,31,31,1,255,128,64 r2dx. 16
32086
32087_pacman_cherry_gfx :
32088  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KNzYgMSAxIDEgIzU5CnicFYpBCgAxDALVXvv/p5ZtmsgmIMoM7k0Cx/ySYYIXrE5qOgTmE1KGl"\
32089               "oUW1pp1qVUqmkt3Hj9Whx3SMSAyMCAxIDEgIzI2Cnicc/eNYkjPzUyONwACveTM3PQqBgA+VQX2"
32090  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
32091
32092_pacman_strawberry_gfx :
32093  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KNzIgMSAxIDEgIzYwCnicJYlJDoAwEMOyHOH/H+UANJ0RBaRIlp1tJ4HAd9HFaoUtTNWC1yIPW"\
32094               "T5ew5em+lT994guKHgAoIoa8zEgMjAgMSAxICMyNgp4nHP3jWJIz81MjjcAAr3kzNz0KgYAPlUF9g=="
32095  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
32096
32097_pacman_orange_gfx :
32098  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KNDQgMSAxIDEgIzQ2CnicBcHBDQAgDAOxXPjC/gsxU4VKi7DnAukKPU6SYjTVptxhbSv8wpVuf"\
32099               "UwDEZ4xIDIwIDEgMSAjMjYKeJxz941iSM/NTI43AAK95Mzc9CoGAD5VBfY="
32100  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
32101
32102_pacman_banana_gfx :
32103  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KNzAgMSAxIDEgIzQ4CnicNcqxDQAgDMTANx0S++8aIC9ShObceC6QQoSJ5Ai5vWW2WTI6xr+bv"\
32104               "LU/BnUW3jEgMjAgMSAxICMyNgp4nHP3jWJIz81MjjcAAr3kzNz0KgYAPlUF9g=="
32105  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
32106
32107_pacman_map_level1 :
32108  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjAxIDEgMSAxICM5Nwp4nGWOUQ6AMAhDKX56Be9/QnWJArEM9MdsGa9QYOsGiOy4FaZYHAo1a"\
32109               "MezIJ+QJnszLsq2aRaf9Q9GiaBnGmEZhSe8wLOdVqO+My+cFdJj9OpVQ0vzRm8l/L954AHE9jnsMSAyMCAxIDEgIzI2Cnicc/eNYk"\
32110               "jPzUyONwACveTM3PQqBgA+VQX2"
32111  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
32112  _rlevel=33 _glevel=33 _blevel=255
32113
32114_pacman_map_level2 :
32115  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjA4IDEgMSAxICMxMDAKeJxljkEOAzEIAxn32C/0/y+MdlUloAKJemgvjAOG+PkCs8ElHsjRQ"\
32116               "k0iycwS+/2jcuJlnhhhf3SxPKWw9DaJmYsbmLOqmyaXn92UsVV9Y+sweOvb7YB1vQPPDnV4a/ABHS45BDEgMjAgMSAxICMyNgp4nH"\
32117               "P3jWJIz81MjjcAAr3kzNz0KgYAPlUF9g=="
32118  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
32119  _rlevel=200 _glevel=33 _blevel=33
32120
32121_pacman_map_level3 :
32122  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjA5IDEgMSAxICMxMDEKeJxVTlsOwDAIAve5K+z+N+zStGomeyTdjwIKYT9IoHGAbpOgdQ3n1"\
32123               "i1ZinHdnO+20FuKg3nf4WIO3YolP8QEQ75CEkoKaUIDT3K95w8OZbp4lDcMj1PNUjXyDg+u4LTGC5LiOwAxIDIwIDEgMSAjMjYKeJ"\
32124               "xz941iSM/NTI43AAK95Mzc9CoGAD5VBfY="
32125  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
32126  _rlevel=33 _glevel=200 _blevel=255
32127
32128_pacman_map_level4 :
32129  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjEwIDEgMSAxICM5MAp4nHVPQQ6AMAhr8egX/P8TN+cGGSy6TBMTQgqlUPaDBDINLBJZbMQHq"\
32130               "mwq7e40eahgy8i/jDKB1QhlRWBzcPH09Rnr9ALTrHSOuN5N+IFF9LIY9uOPDitcQvExIDIwIDEgMSAjMjYKeJxz941iSM/NTI43AA"\
32131               "K95Mzc9CoGAD5VBfY="
32132  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
32133  _rlevel=200 _glevel=200 _blevel=33
32134
32135_pacman_map_level5 :
32136  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMjA3IDEgMSAxICMxMDAKeJxdjlEKxCAMRPOmn3uF3v+ELXRrDE2qwrIgzPB8Mn52MDv4ihBbQ"\
32137               "yjQyuRBz96RF8NmSYAb98s65j95Wd0bFqxG1MNV/s1ealMujRHdI5wtf9X0WnWmFWO/7JmJXScPVxI1CjEgMjAgMSAxICMyNgp4nH"\
32138               "P3jWJIz81MjjcAAr3kzNz0KgYAPlUF9g=="
32139  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
32140  _rlevel=200 _glevel=255 _blevel=33
32141
32142_pacman_map_level6 :
32143  base642img[] "MiBjaGFyIGxpdHRsZV9lbmRpYW4KMTgzIDEgMSAxICM5Mgp4nGVO0QqAQAhz67Ff6P+/sK66TNqJUBCozDHn5gUwW9EIBnhi2nmBP"\
32144               "QmHCeMwDbuzBNCH1oZeDBEiPSWiAoXU8Yfhn4PyiNc1X3i99NwUJPMoVyVs3PAASqEr4jEgMjAgMSAxICMyNgp4nHP3jWJIz81Mjj"\
32145               "cAAr3kzNz0KgYAPlUF9g=="
32146  decompress_rle. +mirror. x z. 1,100% a[-2,-1] x
32147  _rlevel=255 _glevel=130 _blevel=233
32148
32149#@cli x_paint
32150#@cli : Launch the interactive painter.
32151x_paint : check_display $0
32152  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32153  e[] "\n
32154------ "${g}"Interactive painter"$n" -----------------------\n
32155----\n
32156---- Use "${c}"mouse"$n" to select color and brush.\n
32157---- "${c}"Left button"$n" draws a colored stroke.\n
32158---- "${c}"Right button"$n" fills a colored region.\n
32159---- "${c}"Arrow keys"$n" or '"${c}"SPACE"$n"' and '"${c}"BACKSPACE"$n"' to swap\n
32160---- between available images.\n
32161---- Key '"${c}"S"$n"' to save snapshot of the current view.\n
32162---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
32163----\n
32164--------------------------------------------------"
32165  to_rgb
32166  if !$! i[0] 512,512,1,3,255 nm[0] "[New image]" else k[0] fi
32167  1 # Brush image [-1]
32168  parallel "_x_paint[]","w[] 400,320,0,Palette x_select_color[] __color,0,0,0" k[0]
32169
32170_x_paint :
32171
32172  # Init variables.
32173  pass[-2,-1] 1 ('{-2,n}') discard. {'_c1'} nm... {t} rm.
32174  __color={0,if(ia<128,vector3(255),vector3(0))}
32175  brushsize=1
32176  brushopacity=0
32177  brushangle=90
32178  brushthickness=1
32179  image=0
32180  refresh_image=1
32181  refresh_brush=1
32182  ox1=-1
32183  oy1=-1
32184
32185  # Start user event loop.
32186  do
32187
32188    # Open/refresh brush window.
32189    if $refresh_brush
32190      rm. (32,64;64,32) r. 16,16,1,3,1 r. {8*48},{4*48},1,3,0,2
32191      repeat 4,y repeat 8
32192        ellipse. {48*$>+24},{48*$y+24},{2*$>+1},{(2*$>+1)*$brushthickness},$brushangle,{1-$y/4},255
32193      done done
32194      rectangle. {$brushsize*48},{$brushopacity*48},\
32195                  {$brushsize*48+47},{$brushopacity*48+47},\
32196                  1,0xFFFFFFFF,255,128,128
32197      {w},16,1,3 line. 0,50%,100%,50%,1,0x55555555,128,64,128
32198      bx={$brushangle*w/180}
32199      rectangle. {$bx-16},20%,{$bx+16},80%,1,128
32200      line. {$bx-16},20%,{$bx+16},20%,1,255 line. {$bx+16},20%,{$bx+16},80%,1,255
32201      line. {$bx-16},80%,{$bx+16},80%,1,64 line. {$bx-16},20%,{$bx-16},80%,1,64
32202      a[-2,-1] y
32203      16,{h-16},1,3 line. 50%,0,50%,100%,1,0x55555555,128,64,128
32204      by={$brushthickness*(h-16)}
32205      rectangle. 20%,{$by-16},80%,{$by+16},1,128
32206      line. 20%,{$by-16},80%,{$by-16},1,255 line. 80%,{$by-16},80%,{$by+16},1,255
32207      line. 20%,{$by-16},20%,{$by+16},1,64 line. 20%,{$by+16},80%,{$by+16},1,64
32208      a[-2,-1] x
32209      w3. {w},{h},0,"Brush"
32210      refresh_brush=0
32211    fi
32212
32213    # Open/refresh image window.
32214    if $refresh_image
32215      w1[$image] {$image,w},{$image,h},0,"Image "\#$image" : "{$image,b}.{$image,x}
32216      refresh_image=0
32217    fi
32218
32219    # Manage user events.
32220    x1={*1,x} y1={*1,y}
32221    x2={*2,x} y2={*2,y}
32222    x3={*3,x} y3={*3,y}
32223
32224    if $x1>=0                       # Event in the image window.
32225      if {*1,b}&1                   # Left button -> draw brush stroke.
32226        ox1={if($ox1<0,$x1,$ox1)}
32227        oy1={if($oy1<0,$y1,$oy1)}
32228        delta={max(abs($x1-$ox1),abs($y1-$oy1))}
32229        r1={2*$brushsize+1}
32230        r2={$r1*$brushthickness}
32231        dx={2*($x1-$ox1)/max(1,$delta)}
32232        dy={2*($y1-$oy1)/max(1,$delta)}
32233        o={1-($brushopacity/4)^0.04}
32234        repeat max(1,($delta+1)/2)
32235          ellipse[$image] {$ox1+$>*$dx},{$oy1+$>*$dy},$r1,$r2,$brushangle,$o,$__color
32236        done
32237        ox1=$x1 oy1=$y1
32238        refresh_image=1
32239      else
32240        ox1=-1 oy1=-1
32241        if {*1,b}&2               # Right button -> fill region.
32242          flood[$image] $x1,$y1,0,10,0,1,$__color
32243          refresh_image=1
32244        fi
32245      fi
32246    fi
32247
32248    if {*1,ARROWRIGHT}" || "{*2,ARROWRIGHT}" || "{*3,ARROWRIGHT}" || "\  # Manage image selection.
32249       {*1,ARROWUP}" || "{*2,ARROWUP}" || "{*3,ARROWUP}" || "\
32250       {*1,SPACE}" || "{*2,SPACE}" || "{*3,SPACE}
32251      image={($image+1)%($!-2)} refresh_image=1
32252    elif {*1,ARROWLEFT}" || "{*2,ARROWLEFT}" || "{*3,ARROWLEFT}" || "\
32253         {*1,ARROWDOWN}" || "{*2,ARROWDOWN}" || "{*3,ARROWDOWN}" || "\
32254         {*1,BACKSPACE}" || "{*2,BACKSPACE}" || "{*3,BACKSPACE}
32255      image={($image-1)%($!-2)} refresh_image=1
32256    fi
32257
32258    if {*1,S} o[$image] gmic_paint.png fi  # Save snapshot if requested.
32259
32260    if {*3,b}" && "$x3>=0  # Manage brush selection.
32261      if $x3<384" && "$y3>=192 brushangle={$x3*180/(w-16)}   # Bottom slider -> select brush angle.
32262      elif $x3>=384" && "$y3<192 brushthickness={$y3/(h-16)} # Right slider -> select brush thickness.
32263      elif $x3<384" && "$y3<192 brushsize={int($x3*8/(w-16))} brushopacity={int($y3*4/(h-16))}
32264      fi
32265      refresh_brush=1
32266    fi
32267    wait
32268    while {*1}" && "!{*1,Q}" && "!{*1,ESC}
32269
32270  # Exit properly.
32271  __color=-1 w1[] 0 w2[] 0 w3[] 0 rm[-2,-1]
32272
32273#@cli x_plasma
32274#@cli : Launch the plasma effect demo.
32275x_plasma : check_display $0
32276  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32277  e[] "\n
32278------ "${g}"Plasma effect"$n" ----------------------\n
32279----\n
32280---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
32281---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
32282---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
32283----\n
32284-------------------------------------------"
32285  l[]
32286
32287  # Init plasma backgrounds.
32288  N=8
32289  repeat $N
32290    320,200,1,3 rand. 0,255 plasma. 1,0,7 n. 0,255
32291    amp={u(-40,40)} freq={round(u(2,6))} dir$>={if(u<0.5,-1,1)*round(u(1,2))}
32292    100%,100%,1,1,'$amp*cos(y*2*pi*$freq/h)'
32293  done
32294
32295  {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
32296  0 t. "** Welcome to G\47MIC, a powerful image processing framework **",0,0,50,1,255
32297  b. 0.5 n. 0,255
32298  M={w}
32299
32300  # Start animation loop.
32301  w[] {0,f=1.5*h<0.5*{*,v}?3:1.5;[w,h]*=f},0,"[G"{`39`}"MIC] Plasma Effect"
32302  t=0 tt={-1.5*{0,w}}
32303
32304  do
32305    tic=$|
32306
32307    # Render interpolated background between two successive plasmas.
32308    a={int($t)} a2={2*$a} a21={$a2+1}
32309    b={($a+1)%$N} b2={2*$b} b21={$b2+1}
32310    +warp[$a2] [$a21],1,0,2
32311    +warp[$b2] [$b21],1,0,2
32312    j.. .,0,0,0,0,{$t-$a} rm.
32313
32314    shift[$a21] 0,${dir$a},0,0,2  # Animate plasma background.
32315    shift[$b21] 0,${dir$b},0,0,2
32316    if int($t+0.005)>int($t) dir$a={if(u<0.5,-1,1)*round(u(1,3))} fi
32317    t={($t+max(0.005,($|-$tic)))%$N}
32318
32319    # Render text scrolling.
32320    +z.. $tt,{$tt+w-1+2}
32321    warp. [-4],0,0,0
32322    r. 100%,100%,1,3
32323    +*. -1 +. 255
32324    j... .,0,0,0,0,1,..,255 rm.
32325    j.. .,-2,-2,0,0,1,.,255 rm.
32326
32327    tt+={max(2,($|-$tic)*250)} # Animate scrolling.
32328    if $tt>=$M tt={-1.5*{0,w}} fi
32329
32330    # Display rendered frame.
32331    fps=${-fps} if $fps>0 to. $fps" fps",5,5,16,1,0.2 fi
32332    w.
32333    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
32334    rm. wait 20
32335  while {*}" && "!{*,ESC}" && "!{*,Q}
32336  rm[{-2*$N-2}--1] w[] 0 endl
32337
32338#@cli x_quantize_rgb : _nbcolors>=2
32339#@cli : Launch the RGB color quantization demo.
32340x_quantize_rgb : check "isint(${1=16}) && $1>1" check_display $0
32341  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32342  e[] "\n
32343------ "${g}"RGB Quantization"$n" --------------------------------------\n
32344----\n
32345---- This demo shows how RGB colors can be quantified using\n
32346---- the "${c}"k-means algorithm"$n".\n
32347----\n
32348---- "${c}"Left mouse button"$n" on 3D view rotates the color cube.\n
32349---- "${c}"Right mouse button"$n" on 3D view toggles colors/clusters mode.\n
32350---- "${c}"Left mouse button"$n" on image toggles dithering mode,\n
32351---- "${c}"Left mouse button"$n" on colormap adds a random color.\n
32352---- "${c}"Right mouse button"$n" on colormap removes a color.\n
32353---- Key '"${c}"R"$n"' init colormap with random values.\n
32354---- Key '"${c}"U"$n"' init colormap with uniform sampling.\n
32355---- Key '"${c}"M"$n"' init colormap with median-cut algorithm.\n
32356---- Key '"${c}"SPACE"$n"' does a single iteration of k-means and pauses.\n
32357---- Key '"${c}"ENTER"$n"' runs k-means algorithm.\n
32358---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
32359---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
32360---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
32361----\n
32362--------------------------------------------------------------"
32363
32364  if !$! sp ? fi
32365  k[0] to_rgb if h>300 r2dy 300 round 1 fi nm. img        # Resize input image if necessary.
32366  +r {w*h},1,1,3,-1 r. {min(w,8192)},1,1,3 nm. colors     # Get reduced set of image colors.
32367  $1,1,1,3 rand. 0,255 round. 1 nm. centroids             # Initialize random centroids.
32368  _x_quantize_rgb_3d (1,0,0,0;0,1,0,0;0,0,1,0) nm. pose3d # Init 3D object.
32369  _x_quantize_rgb_text "Colors",clustering0
32370  _x_quantize_rgb_text "Clusters",clustering1
32371  _x_quantize_rgb_text "Dithering: off",dithering0
32372  _x_quantize_rgb_text "Dithering: on",dithering1
32373  if {img,h<300} +r2dy[img] 300,1 else [img] fi           # Generate visualization canvas.
32374  {w+315},365,1,3,255 rm..
32375  rectangle. 4,4,305,305,1,0xFFFFFFFF,0
32376  rectangle. 309,4,{w-5},305,1,0xFFFFFFFF,0
32377  rectangle. 4,309,{w-5},360,1,0xFFFFFFFF,0
32378  .,.
32379  rectangle. 310,5,{w-6},305,1,1
32380  rectangle. 5,310,{w-6},360,1,2
32381  300,300,1,1,'(y<<11)+(x<<2)+3' j.. .,5,5 rm.
32382  a[-2,-1] c nm. visu
32383
32384  # Start k-means iterations.
32385  dithering=0 clustering=0 pause=1 s0=off s1=on
32386  do
32387
32388    # Create and display visualization.
32389    if !narg($visu_3d) # Update 3D vizualization.
32390      +-[centroids] 2 ++[centroids] 2 a[-2,-1] x permute. cxyz y. -. 128
32391      j[obj3d] .,0,8 rm.  # Update centroids position in 3D object.
32392      [obj3d]
32393      if $clustering
32394        if {colors,iM}<256 # Estimate nearest centroids for all colors.
32395          +index[colors] [centroids] *. 256 +[colors,-1]
32396        fi
32397        +channels[colors] 0 >>. 8 map. 2 permute. cxyz y. j.. .,0,{{-2,h}-$_N-h} rm.
32398      fi
32399
32400      pose3d. {pose3d,^} 300,300,1,3 j3d. ..,50%,50%,100,1,2,0,0,300 rm..
32401      j. [clustering$clustering],2,0,0,0,1,[mclustering$clustering],255
32402      nm. visu_3d j[visu] [visu_3d],5,5
32403    fi
32404
32405    if !narg($visu_img) # Update indexed image.
32406      +index[img] [centroids],{0.7*$dithering},1 if h<300 r2dy. 300,1 fi
32407      j. [dithering$dithering],2,0,0,0,1,[mdithering$dithering],255
32408      nm. visu_img j[visu] [visu_img],310,5
32409    fi
32410
32411    if !narg($visu_centroids) # Update colormap.
32412      +luminance[centroids] a. [centroids],y sort. +,x rows. 1 r. {visu,w-10},50,1,3
32413      0 t. "Colors: "{centroids,w},2,0,16,1,255,255,255 +dilate. 3 j... ..,2,2,0,0,1,.,255
32414      rm[-2,-1] nm. visu_centroids j[visu] [visu_centroids],5,310
32415    fi
32416    l[visu]
32417    w -1,-1,0,"[G"{`39`}"MIC] RGB Quantization"
32418    if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
32419    endl
32420
32421    # Check for user's interactions.
32422    x={int({*,x}*{visu,w}/{*,w})}
32423    y={int({*,y}*{visu,h}/{*,h})}
32424    b={*,b}
32425    i={visu,i($x,$y,0,3)}
32426    if $b&1" && "$i==1 # Toggle dithering.
32427      dithering={!$dithering} rm[visu_img] wait -1
32428    elif $b&1" && "$i==2 # Add new color.
32429      (${-rgb}) y. c a[centroids,-1] x  _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
32430      pause=1 wait 100
32431    elif $b&2" && "$i==2" && "{centroids,w}>2 # Remove color.
32432      r[centroids] {centroids,w-1} _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
32433      pause=1 wait 100
32434    elif $b&2" && "$i>=3 # Toggle clusters/colors mode.
32435      clustering={!$clustering} rm[visu_3d] wait -1
32436    elif {*,M} # Init colormap with median-cut.
32437      +&[colors] 255 colormap. {centroids,w},0,0 rm[centroids] nm. centroids
32438      _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
32439      pause=1 wait -1
32440    elif {*,R} # Init colormap with random values.
32441      rand[centroids] 0,255 round[centroids] 1
32442      _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
32443      pause=1 wait -1
32444    elif {*,U} # Init colormap with uniform sampling.
32445      uniform_distribution {centroids,w},3 *. 255 rm[centroids] nm. centroids
32446      _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255
32447      pause=1 wait -1
32448    elif {*,ENTER} # Start k-means iterations.
32449      pause=0
32450    elif $b&1" && "$i>=3 # Manage 3D view rotation.
32451      coords={visu,i($x,$y,0,3)-3} u1={(($coords>>2)&511)-150} v1={($coords>>11)-150}
32452      if !narg($u0) u0=$u1 v0=$v1 fi
32453      if $u0!=$u1" || "$v0!=$v1
32454        n0={sqrt(($u0)^2+($v0)^2)}
32455        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))}
32456        n1={sqrt(($u1)^2+($v1)^2)}
32457        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))}
32458        u={$nv0*$nw1-$nw0*$nv1} v={$nw0*$nu1-$nu0*$nw1} w={$nv0*$nu1-$nu0*$nv1} n={sqrt(($u)^2+($v)^2+($w)^2)}
32459        rotation3d[] $u,$v,$w,{-asin($n/18225)*180/pi} mv[pose3d] $! m*[-2,-1] nm. pose3d
32460        u0=$u1 v0=$v1 rm[visu_3d]
32461      fi
32462    elif !($b&1) u0=
32463    fi
32464
32465    if !$pause" || "{*,SPACE}  # Do one iteration of k-means.
32466      pause={*,-SPACE}
32467
32468      # Estimate new centroids positions.
32469      &[colors] 255 +index[colors] [centroids] *. 256 +[colors,-1]      # Estimate nearest centroids for all colors.
32470      repeat s#$colors                                                  # Recompute centroid positions.
32471        sh[colors] $> +histogram. {centroids,w*256},0,{centroids,w*256-1} rm..
32472        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]
32473      done a[-{colors,s}--1] c
32474      rm[centroids] nm. centroids
32475
32476      # Reassign unused centroids.
32477      +>>[colors] 8 channels. 0 histogram. {centroids,w},0,{centroids,w-1}
32478      cmax={xM}
32479      repeat w if !i($>) point[centroids] $>,0,0,1,{centroids,I($cmax)} point[centroids] $>,0,0,-0.001,${-rgb} fi done
32480      rm. c[centroids] 0,255
32481
32482      if $visu_3d rm[visu_3d] fi
32483      if $visu_img rm[visu_img] fi
32484      if $visu_centroids rm[visu_centroids] fi
32485      wait 20
32486
32487    else if $visu_img wait fi
32488    fi
32489
32490  while {*}" && "!{*,Q}" && "!{*,ESC}
32491  rm w 0
32492
32493 _x_quantize_rgb_3d :
32494  if $obj3d rm[obj3d] fi
32495  +distribution3d[centroids] circles3d. 5 col3d. 255      # Pre-compute 3D object.
32496  colorcube3d p3d. 1
32497  +&[colors] 255 distribution3d. circles3d. 3 o3d. 0.25 +3d[-3--1]
32498  -3d. 128,128,128 nm. obj3d _N={i[7]}
32499
32500_x_quantize_rgb_text :
32501  0 t. "$1",0,0,16,1,255 r. {w+2},15,1,1,0,0,0.5,0.5 +dilate. 3 to_rgb..
32502  nm.. $2 nm. m$2
32503
32504#@cli x_reflection3d
32505#@cli : Launch the 3D reflection demo.
32506x_reflection3d : check_display $0
32507  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32508  e[] "\n
32509------ "${g}"3D reflection"$n" ----------------------\n
32510----\n
32511---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
32512---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
32513---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
32514----\n
32515-------------------------------------------"
32516
32517  # Render background.
32518  128,256,1,3 rand. 0,255 plasma. 1,100 blur_xy. 30,2
32519  sh. 0 n. 0,90 rm. sh. 1 n. 0,60 rm. sh. 2 n. 0,180 rm.
32520  +mirror. x [-2,-1] a[-4--1] x
32521  +luminance. mirror. x b. 2 n. 0,255
32522
32523  # Create 3D objects.
32524  torus3d 30,10 col3d. 255,200,0
32525  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
32526  spherical3d 47,34,"100*abs(1+0.6*cos(3*phi)*sin(4*theta))"
32527  r3d[-2,-1] 0,1,0,90 db3d 0
32528
32529  # Start animation loop.
32530  xb,xl,anim=0
32531  w[] 400,400,0,"[G"{`39`}"MIC] 3D Reflection"
32532  do
32533
32534    tic=$|
32535    # Recreate 3D interpolated background object.
32536    +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..
32537
32538    # Render 3D background object (with flat colors).
32539    +z[-6] $xb,0,{$xb+255},255 j3d. ..,75%,50%,0,1,3,0,0
32540
32541    # Render light reflection map.
32542    +z[-6] $xl,0,{$xl+255},255
32543    xf={min(30,$anim-70)+20*cos(1.8*$|)}
32544    yf={50+20*sin(2.7*$|)}
32545    j3d. [-6],{20+$xf}%,$yf%,0,1,4,0,0
32546
32547    # Add light reflection to 3D background object.
32548    l3d . rm. +j3d. ..,75%,50%,0,1,5,0,0 j.. .,0,0,0,0,0.6 rm[-3,-1]
32549
32550    # Add 3D foreground object.
32551    j3d. [-4],$xf%,$yf%,0,1,4,0,0
32552
32553    # Display frame and update animation variables.
32554    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-19},13,1,0.2 fi
32555    w. rm.
32556
32557    if {*,CTRLLEFT}" && "{*,D} w[] 800,800 elif {*,CTRLLEFT}" && "{*,C} w[] 400,400 fi
32558    xb={($xb+6)&255}
32559    xl={($xl-6)&255}
32560    anim+=1
32561    r3d[-2,-1] {sin(0.5*$|)},{cos($|)},1,{max(0.005,$|-$tic)*33}
32562    r3d... -1,0.3,0.8,{max(0.005,$|-$tic)*100}
32563    wait 20
32564  while {*}" && "!{*,ESC}" && "!{*,Q}
32565  rm[-5--1] w[] 0
32566
32567#@cli x_rubber3d
32568#@cli : Launch the 3D rubber object demo.
32569x_rubber3d : check_display $0
32570  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32571  e[] "\n
32572------ "${g}"3D rubber object"$n" -------------------\n
32573----\n
32574---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
32575---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
32576---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
32577----\n
32578-------------------------------------------"
32579  rm
32580  sphere3d 150,0 torus3d 70,15 cylinder3d 20,40
32581  col3d... 200,200,200,0.3 col3d.. 128,200,76 col3d. 200,128,76
32582  c3d[-3--1] r3d. 1,0,0,70 +3d[-3--1] +3d. 10,-8,20 *3d. 1.5
32583  400,400,64,3
32584  {w},{h},1,3,'if(c==0,x,if(c==1,y,y*{1,d}/h))'
32585  {w},{h},1,3
32586  w[] {w},{h},0,"[G"{`39`}"MIC] 3D Rubber Object"
32587  frame=0
32588  do
32589    fps=${-fps}
32590    {w},{h},1,3 fc. 16,32,32 j3d. [0],50%,50%,0,1,3,0,0 j[1] .,0,0,$frame rm.
32591    r3d[0] 0.1,1,0.6,{3*cos($|*1.25)} r3d[0] 1,0.2,0.6,-1
32592    +warp[1] [2],0,0 *[3] 0.8 *. 0.2 +[3] . rm.
32593    if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi
32594    w.
32595    if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi
32596    wait[0] 20
32597    sh[2] 2 -. 1 &. {{1,d}-1} rm.
32598    frame={($frame-1)%{1,d}}
32599  while {*}" && "!{*,ESC}" && "!{*,Q}
32600  rm w 0
32601
32602#@cli x_segment : _max_resolution={ 0 | >=128 }
32603#@cli : Segment foreground from background in selected opaque RGB images, interactively.
32604#@cli : Return RGBA images with binary alpha-channels.
32605#@cli : Default value: 'max_resolution=1024'.
32606x_segment : check "${1=1024}==0 || $1>=128" check_display $0
32607  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
32608  e[^-1] "Extract foreground from background in image$? interactively, with maximum resolution $1."
32609  e[] "\n
32610----------------------------------------------------------------------------------------------------\n
32611----\n
32612---- "${c}"Left mouse button"$n" or key '"${c}"F"$n"' create a new foreground control point
32613 (or move an existing one).\n
32614---- "${c}"Right mouse button"$n" or key '"${c}"B"$n"' create a new background control point
32615 (or move an existing one).\n
32616---- "${c}"Mouse wheel"$n", or keys '"${c}"CTRL+arrows UP/DOWN"$n"' zoom view in/out.\n
32617---- '"${c}"CTRL+mouse wheel"$n"', '"${c}"SHIFT+mouse wheel"$n"' or "${c}"arrow keys"$n" move image in zoomed view.\n
32618---- Key '"${c}"SPACE"$n"' updates the extraction mask.\n
32619---- Key '"${c}"TAB"$n"' toggles background view modes.\n
32620---- Key '"${c}"M"$n"' toggles marker view modes.\n
32621---- Key '"${c}"BACKSPACE"$n"' deletes the last control point added.\n
32622---- Key '"${c}"PAGE UP"$n"' increases background opacity.\n
32623---- Key '"${c}"PAGE DOWN"$n"' decreases background opacity.\n
32624---- Keys '"${c}"CTRL+D"$n"' increase window size.\n
32625---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n
32626---- Keys '"${c}"CTRL+R"$n"' reset window size.\n
32627---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n
32628----\n
32629----------------------------------------------------------------------------------------------------"
32630  repeat $! l[$>]
32631
32632    # Init variables and images.
32633    name={0,n} title={0,b} if narg({0,x}) title=$title.{0,x} fi
32634    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}
32635    selection=-1 marker_mode=2 xpan=-1 ypan=-1 bg_mode=0 opacity=64
32636    to_rgb nm img
32637
32638    if narg($_gui_control_points)>=4 # Import list of control points from plug-in GUI.
32639      ($_gui_control_points) r. {w/4},4,1,1,-1
32640    else 0 # Empty list of control points.
32641    fi
32642    nm. points
32643
32644    # Compute potential map.
32645    if $1>0 if $w>$h +r2dx[img] {min($1,$w)},2 else +r2dy[img] {min($1,$h)},2 fi else [img] fi
32646    _x_segment.
32647    pw={potential,w} ph={potential,h}
32648
32649    # Start event loop.
32650    do
32651
32652      # Handle user events for zoom/navigation/resizing.
32653      wait
32654      x={*,x} y={*,y} b={*,b} o={*,-o}
32655      is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
32656      is_shift={{*,SHIFTLEFT}" || "{*,SHIFTRIGHT}}
32657      is_mouseout={$x<0" || "$y<0}
32658      x={$x0+$x*($x1-$x0+1)/$ww} y={$y0+$y*($y1-$y0+1)/$wh}
32659      oww=$ww owh=$wh ox0=$x0 oy0=$y0 ox1=$x1 oy1=$y1
32660
32661      if {*,r} # When window resized.
32662        nww={*,d} nwh={*,e} m={min($nww,$nwh)}
32663        cx={($x0+$x1)/2} cy={($y0+$y1)/2} dx={$nww*($x1-$x0+1)/$ww} dy={$nwh*($y1-$y0+1)/$wh}
32664        x0={$cx-$dx/2} x1={$cx+$dx/2}
32665        y0={$cy-$dy/2} y1={$cy+$dy/2}
32666        ww=$nww wh=$nwh
32667      elif $is_ctrl" && "{*,-D} # Increase window size.
32668        nww={min({*,u},$ww*1.25)} nwh={min({*,v},$wh*1.25)} m={min($nww,$nwh)}
32669        if $m==$nww ww=$m wh={$h*$m/$w} else ww={$w*$m/$h} wh=$m fi
32670      elif $is_ctrl" && "{*,-C} # Decrease window size.
32671        nww={$ww/1.25} nwh={$wh/1.25}
32672        if min($nww,$nwh)>=64 ww=$nww wh=$nwh fi
32673      elif $is_ctrl" && "{*,-R} # Reset window size.
32674        fdim=${fitscreen[]\ $w,$h} ww={arg(1,$fdim)} wh={arg(2,$fdim)}
32675        x0=0 y0=0 x1={$w-1} y1={$h-1}
32676      elif ($is_shift" && "$o<0)" || "{*,ARROWLEFT} # Go left.
32677        dx={($x1-$x0)/6} x0-=$dx x1-=$dx
32678      elif ($is_shift" && "$o>0)" || "{*,ARROWRIGHT} # Go right.
32679        dx={($x1-$x0)/6} x0+=$dx x1+=$dx
32680      elif ($is_ctrl" && "$o>0)" || "({*,ARROWUP}" && "!$is_ctrl) # Go up.
32681        dy={($y1-$y0)/6} y0-=$dy y1-=$dy
32682      elif ($is_ctrl" && "$o<0)" || "({*,ARROWDOWN}" && "!$is_ctrl) # Go down.
32683        dy={($y1-$y0)/6} y0+=$dy y1+=$dy
32684      elif $o>0" || "($is_ctrl" && "{*,ARROWUP}) # Zoom in.
32685        if $x1-$x0>16" && "$y1-$y0>16
32686          cx={if($x>=0" && "!{*,ARROWUP},$x,($x0+$x1)/2)}
32687          cy={if($y>=0" && "!{*,ARROWUP},$y,($y0+$y1)/2)}
32688          x0={$cx+($x0-$cx)*0.75} y0={$cy+($y0-$cy)*0.75}
32689          x1={$cx+($x1-$cx)*0.75} y1={$cy+($y1-$cy)*0.75}
32690        fi
32691      elif $o<0" || "($is_ctrl" && "{*,ARROWDOWN}) # Zoom out.
32692        zfactor={max(($x1-$x0+1)/$w,($y1-$y0+1)/$h)}
32693        if $zfactor<1.3
32694          cx={if($x>=0" && "!{*,ARROWDOWN},$x,($x0+$x1)/2)}
32695          cy={if($y>=0" && "!{*,ARROWDOWN},$y,($y0+$y1)/2)}
32696          x0={$cx+($x0-$cx)/0.75} y0={$cy+($y0-$cy)/0.75}
32697          x1={$cx+($x1-$cx)/0.75} y1={$cy+($y1-$cy)/0.75}
32698          dx={$zfactor^2*($w-$x0-$x1)/2} dy={$zfactor^2*($h-$y0-$y1)/2}
32699          x0+=$dx x1+=$dx y0+=$dy y1+=$dy
32700        else
32701          dx={($w-$x0-$x1)/2} dy={($h-$y0-$y1)/2}
32702          x0+=$dx x1+=$dx y0+=$dy y1+=$dy
32703        fi
32704      elif $b&4" && "!$is_mouseout # Pan.
32705        if $panx<0" && "$pany<0 panx=$x pany=$y
32706        else dx={round($panx-$x)} dy={round($pany-$y)} x0+=$dx y0+=$dy x1+=$dx y1+=$dy
32707        fi
32708      else panx=-1 pany=-1
32709      fi
32710      if $ww!=$oww" || "$wh!=$owh" || "$ox0!=$x0" || "$oy0!=$y0" || "$ox1!=$x1" || "$oy1!=$y1 rm[baseview] fi
32711
32712      # Handle events related to control points management.
32713      N={points,w}
32714      is_left_button={$b&1" || "{*,F}} is_right_button={$b&2" || "{*,B}}
32715      is_button={$is_left_button" || "$is_right_button}
32716      if narg($baseview)" && "$is_button" && "$x>=0" && "$y>=0" && "$x<$w" && "$y<$h
32717        if $selection==-1" && "$N # Check for selection of an existing point.
32718          ($x;$y) r. $N,2 -. [points] *. {max($ww,$wh)/max($x1-$x0,$y1-$y0)} sqr. s. y +[-2,-1]
32719          dmin={im} selection={if($dmin>25,-1,xm)} rm.
32720        fi
32721        if $selection>=0
32722          if $marker_mode # Move existing point.
32723            +columns[points] $selection ox={i[0]} oy={i[1]}
32724            =. $x =. $y,0,1 =. {1+$is_left_button},0,3
32725            j[points] .,$selection rm. rm[view]
32726          fi
32727        else # Add new foreground or background point.
32728          ($x;$y;0;{1+$is_left_button}) a[points,-1] x selection=$N if !$marker_mode marker_mode=2 fi rm[view]
32729        fi
32730      else selection=-1
32731        if {*,SPACE}" && "narg($labels) rm[labels] # Update labels.
32732        elif {*,TAB}" && "narg($baseview) # Toggle background view modes
32733          bg_mode={($bg_mode+1)%6} rm[baseview] wait -1
32734        elif {*,M}" && "narg($view) # Toggle markers view modes
32735          marker_mode={($marker_mode-1)%3} rm[view] wait -1
32736        elif {*,PAGEDOWN}" && "narg($baseview) # Decrease background opacity
32737          opacity={max(0,$opacity-32)} rm[baseview] wait -1
32738        elif {*,PAGEUP}" && "narg($baseview) # Increase background opacity
32739          opacity={min(255,$opacity+32)} rm[baseview] wait -1
32740        elif {*,BACKSPACE}" && "$N # Remove last point.
32741          if $N>1 z[points] 0,{$N-2}
32742          else i=$points rm[points] i[$i] 0 nm[$i] points
32743          fi rm[view] wait -1
32744        fi
32745      fi
32746
32747      # Manage zoomed view bounds.
32748      w2={round(($x1-$x0)/2)} h2={round(($y1-$y0)/2)}
32749      if $x0<-$w2 x1-={$x0+$w2} x0=-$w2 fi
32750      if $y0<-$h2 y1-={$y0+$h2} y0=-$h2 fi
32751      if $x1>=$w+$w2 x0+={$w-1+$w2-$x1} x1={$w-1+$w2} fi
32752      if $y1>=$h+$h2 y0+={$h-1+$h2-$y1} y1={$h-1+$h2} fi
32753
32754      # Render labels.
32755      if !narg($labels)
32756        N={points,w}
32757        if narg($view) to[view] "Processing...",5,5,20,2 w[view] fi
32758        if $N
32759          [points]
32760          sh. 0,0,0,0 *. {$pw/$w} rm.
32761          sh. 1,1,0,0 *. {$ph/$h} rm.
32762          pointcloud. -1,$pw,$ph dilate. 3
32763          watershed. [potential] -. 1
32764        else [potential],[potential],1,1,1
32765        fi
32766        nm. labels
32767        if narg($baseview) rm[baseview] fi
32768      fi
32769
32770      # Render base image.
32771      if !narg($baseview)
32772        nx0={$x0*$pw/$w} ny0={$y0*$ph/$h}
32773        nx1={$x1*$pw/$w} ny1={$y1*$ph/$h}
32774        +z[img] $x0,$y0,$x1,$y1
32775        r. $ww,$wh,1,100%,{if($ww<w" && "$wh<h,2,1)}
32776        +z[labels] $nx0,$ny0,$nx1,$ny1
32777        r. $ww,$wh,1,100%,{if($ww<w" && "$wh<h,2,3)}
32778        if $bg_mode>=3 *. -1 +. 1 fi
32779        *. {255-$opacity} +. $opacity a[-2,-1] c
32780        if $bg_mode%3>=1 i.. 100%,100%,1,3,{(($bg_mode-1)%3)*255} blend[-2,-1] alpha
32781        else drgba.
32782        fi
32783        nm. baseview
32784        if narg($view) rm[view] fi
32785      fi
32786
32787      # Render view.
32788      if !narg($view)
32789        [baseview] r. 100%,100%,1,3
32790        if $marker_mode
32791          if $marker_mode==2 rad1=5 rad2=3 opa=1 else rad1=3 rad2=2 opa=0.8 fi
32792          col0=255,0,0 col1=0,255,0
32793          repeat w#$points
32794            +columns[points] $> x={(i[0]-$x0)*$ww/(1+$x1-$x0)} y={(i[1]-$y0)*$wh/(1+$y1-$y0)} l={i[3]-1} rm.
32795            circle. $x,$y,$rad1,1,0 circle. $x,$y,$rad2,$opa,${col$l}
32796          done
32797        fi
32798
32799        nm. view
32800        w[view] $ww,$wh,0,$title
32801      fi
32802
32803    while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER}
32804
32805    # Recompute labels at full resolution.
32806    if narg($view) to[view] "Processing fullres...",5,5,20,2 w[view] fi
32807    k[img,points]
32808    N={points,w} status=
32809    if $N
32810      status={points,^}
32811      [img] _x_segment. pointcloud[points] -1,$w,$h
32812      zfact={{img,max(w,h)}/{potential,max(w,h)}} dilate[points] {int(3*$zfact)}
32813      watershed[points] [potential] -[points] 1 k[img,points]
32814      *. 255
32815    else k[img] [img],[img],1,1,255
32816    fi
32817    a c nm $name
32818
32819  endl done
32820  u $status   # Return control points of last image.
32821  w 0
32822
32823# Compute potential function.
32824_x_segment :
32825  b. 0.2% gradient_norm. f. '1/(1+i^2)'
32826  nm. potential
32827
32828#@cli x_select_color : _variable_name
32829#@cli : Display a RGB or RGBA color selector.
32830#@cli : Argument 'variable_name' specifies the variable that contains the selected color values (as R,G,B,[A])
32831#@cli : at any time.
32832#@cli : Its value specifies the initial selected color. Assigning '-1' to it forces the interactive window to close.
32833#@cli : Default value: 'variable_name=xsc_variable'.
32834x_select_color : skip ${1=xsc_variable} check_display $0
32835  rm
32836  n={narg($$1)} if !$n $1=0,0,0 fi
32837  rgba_mode={$n>=4} R={arg(1,$$1)} G={arg(2,$$1)} B={arg(3,$$1)} A={if($rgba_mode,arg(4,$$1),255)}
32838  e[^-1] "Open "${arg\ 1+$rgba_mode,RGB,RGBA}" color selector widget, with variable '$1' and starting color "\
32839             ($$1)"."
32840  if !{*} w[] {400+24*$rgba_mode},400,0,"Select a color" fi
32841  update_view=1 is_sv=0 is_h=0 is_a=0 colordb=0 is_thread_variable={arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_'}
32842
32843  # Manage color presets.
32844  m "add_preset : if !narg($_xsc_preset$""1) _xsc_preset$""1=$""2,$""3,$""4 fi"
32845  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
32846  add_preset 4,0,0,255 add_preset 5,255,255,0 add_preset 6,255,0,255 add_preset 7,0,255,255
32847  add_preset 8,50,50,50 add_preset 9,100,100,100 add_preset 10,150,150,150 add_preset 11,200,200,200
32848  um add_preset
32849  if !narg($_xsc_preset) _xsc_preset=11 fi
32850  ($R^$G^$B) c. 0,255 rgb2hsv. H={i[0]} S={i[1]} V={i[2]} rm.
32851
32852  # Start event loop.
32853  do
32854    w={*,d} h={*,e} x={*,x} y={*,y} b={*,b}
32855
32856    # Update base image.
32857    if !$!
32858      $w,$h,1,3,200
32859      if $rgba_mode x1={w-89} y1={h-57} x2={w-80} else x1={w-49} y1={h-57} x2={w-40} fi
32860      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}
32861      rectangle {$x0-1},{$y0-1},{$x1+1},{$y1+1},1,0xFFFFFFFF,232
32862      line {$x0-1},{$y0-1},{$x1+1},{$y0-1},1,128
32863      line {$x0-1},{$y0-1},{$x0-1},{$y1+1},1,128
32864      (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.
32865      rectangle {$x2-1},{$y0-1},{$x3+1},{$y1+1},1,0xFFFFFFFF,232
32866      line {$x2-1},{$y0-1},{$x3+1},{$y0-1},1,128
32867      line {$x2-1},{$y0-1},{$x2-1},{$y1+1},1,128
32868      (359;0^1;1^1;1) r. {$x3-$x2+1},{$y1-$y0+1},1,3,3 hsv2rgb. j.. .,$x2,$y0 rm.
32869      if $rgba_mode
32870        rectangle {$x4-1},{$y0-1},{$x5+1},{$y1+1},1,0xFFFFFFFF,232
32871        line {$x4-1},{$y0-1},{$x5+1},{$y0-1},1,128
32872        line {$x4-1},{$y0-1},{$x4-1},{$y1+1},1,128
32873        (1;0) r. {$x5-$x4+1},{$y1-$y0+1},1,4,3 *. 255 drgba. j.. .,$x4,$y0 rm.
32874      fi
32875      t. "Current",$x0,{$y1+12},14,1,0
32876      if narg($_xsc_old)
32877        t. "Old",$x0,{$y1+34},14,1,0
32878        ($_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.
32879      fi
32880      repeat 12
32881        (${_xsc_preset$>}) -. 255 r. 4,1,1,1,0 +. 255
32882        y. c r. 18,18 drgba. frame. 1,1,{255*($>==$_xsc_preset)}
32883        j.. .,{$x6+($>%6)*25},{$y6+($>>=6)*25} rm.
32884      done
32885      update_view=1
32886    fi
32887
32888    # Update view.
32889    if $update_view
32890      .
32891      cx={$x0+$V*($x1-$x0)} cy={$y0+(1-$S)*($y1-$y0)}
32892      if $cx>$x0 line. {$cx-1},$y0,{$cx-1},$y1,1,200 fi
32893      if $cx<$x1 line. {$cx+1},$y0,{$cx+1},$y1,1,200 fi
32894      if $cy>$y0 line. $x0,{$cy-1},$x1,{$cy-1},1,200 fi
32895      if $cy<$y1 line. $x0,{$cy+1},$x1,{$cy+1},1,200 fi
32896      line. $x0,$cy,$x1,$cy,1,0 line. $cx,$y0,$cx,$y1,1,0
32897      cy={$y0+(359-$H)*($y1-$y0)/359}
32898      if $cy>$y0 line. $x2,{$cy-1},$x3,{$cy-1},1,200 fi
32899      if $cy<$y1 line. $x2,{$cy+1},$x3,{$cy+1},1,200 fi
32900      line. $x2,$cy,$x3,$cy,1,0
32901      if $rgba_mode
32902        cy={$y0+(255-$A)*($y1-$y0)/255}
32903        if $cy>$y0 line. $x4,{$cy-1},$x5,{$cy-1},1,200 fi
32904        if $cy<$y1 line. $x4,{$cy+1},$x5,{$cy+1},1,200 fi
32905        line. $x4,$cy,$x5,$cy,1,0
32906      fi
32907      ($H^$S^$V^$A) sh. 0,2 hsv2rgb. rm. round. R={i[0]} G={i[1]} B={i[2]}
32908      r. 48,16 drgba. r. {w+2},{h+2},1,3,0,0,0.5,0.5 j.. .,{$x0+55},{$y1+10} rm.
32909      t. "HSV ("{round($H)}","{round($S*255)}","{round($V*255)}")",{$x0+115},{$y1+24},14,1,0
32910      if $rgba_mode t. "RGBA ("$R","$G","$B","{round($A)}")",{$x0+115},{$y1+8},14,1,0
32911      else t. "RGB ("$R","$G","$B")",{$x0+115},{$y1+8},14,1,0
32912      fi
32913      ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'}
32914      f. if(i>=_'a'" && "i<=_'z',i+_'A'-_'a',i)
32915      t.. "html ""#"{t},{$x0+115},{$y1+40},14,1,0 rm.
32916      w. 100%,100%,0 rm.
32917      if $rgba_mode $1=$R,$G,$B,$A else $1=$R,$G,$B fi
32918      update_view=0
32919    fi
32920    if $is_thread_variable wait 50 else wait fi
32921
32922    # Manage window size.
32923    ww={*,w} wh={*,h}
32924    is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
32925    if {*,r} ww={*,d} wh={*,e}
32926    elif $is_ctrl" && "{*,-D} ww={1.25*$ww} wh={1.25*$wh}
32927    elif $is_ctrl" && "{*,-C} ww={0.8*$ww} wh={0.8*$wh}
32928    elif $is_ctrl" && "{*,R} ww={400+24*$rgba_mode} wh=400
32929    fi
32930    ww={max(200,$ww)} wh={max(200,$wh)}
32931    if $ww!={*,w}" || "$wh!={*,h} w[] $ww,$wh rm fi
32932
32933    # Manage user events.
32934    if $b&1" && "$x>=0" && "$y>=0
32935      if !$is_h" && "!$is_a" && "($is_sv" || "($x>=$x0" && "$x<=$x1" && "$y>=$y0" && "$y<=$y1)) # SV selection
32936        S={max(0,min(1,1-($y-$y0)/($y1-$y0)))} V={max(0,min(1,($x-$x0)/($x1-$x0)))}
32937        update_view=1 colordb=0 is_sv=1 k[0]
32938      elif !$is_sv" && "!$is_a" && "($is_h" || "($x>=$x2" && "$x<=$x3" && "$y>=$y0" && "$y<=$y1)) # H selection
32939        H={max(0,min(359,359-($y-$y0)*359/($y1-$y0)))}
32940        colordb=0 is_h=1 rm
32941      elif !$is_sv" && "!$is_h" && "($is_a" || "($x>=$x4" && "$x<=$x5" && "$y>=$y0" && "$y<=$y1)) # A selection
32942        A={round(max(0,min(255,255-($y-$y0)*255/($y1-$y0))))}
32943        colordb=0 is_a=1 update_view=1 k[0]
32944      elif !$is_sv" && "!$is_h" && "!$is_a" && "{narg($_xsc_old)}" && "$x>=$x0+55" && "$x<=$x0+102" && "\
32945           $y>=$y1+32" && "$y<=$y1+47 # Old color
32946        ($_xsc_old) y. c sh. 0,2 rgb2hsv. rm. H={i[0]} S={i[1]} V={i[2]} A={i[3]}
32947        colordb=0 rm
32948      elif !$is_sv" && "!$is_h" && "!$is_a" && "$x>=$x6" && "$x<=$x5" && "$y>=$y6" && "$y<=$y6+50" && "\
32949           ($x-$x6)%25<=20" && "($y-$y6)%25<=20 # Preset.
32950        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.
32951        H={i[0]} S={i[1]} V={i[2]} A={i[3]}
32952        colordb=0 rm
32953      elif !$is_sv" && "!$is_h" && "!$is_a" && "$x>=$x0+55" && "$x<=$x0+102" && "$y>=$y1+10" && "$y<=$y1+27
32954         # Add current as old and/or preset.
32955        _xsc_old=$R,$G,$B,$A colordb={($colordb+1)%2}
32956        if !$colordb # Double-click to add to preset.
32957          _xsc_preset$_xsc_preset=$R,$G,$B,$A _xsc_preset={($_xsc_preset-1)%12}
32958        fi
32959        rm wait -1
32960      else colordb=0
32961      fi
32962    elif !$b is_sv=0 is_h=0 is_a=0
32963    fi
32964    if {*,ARROWUP} colordb=0 S={min(1,$S+1/256)} update_view=1 k[0] wait -1
32965    elif {*,ARROWDOWN} colordb=0 S={max(0,$S-1/256)} update_view=1 k[0] wait -1
32966    elif {*,ARROWRIGHT} colordb=0 V={min(1,$V+1/256)} update_view=1 k[0] wait -1
32967    elif {*,ARROWLEFT} colordb=0 V={max(0,$V-1/256)} update_view=1 k[0] wait -1
32968    elif {*,PAGEUP} colordb=0 H={min(359,$H+1)} rm wait -1
32969    elif {*,PAGEDOWN} colordb=0 H={max(0,$H-1)} rm wait -1
32970    fi
32971
32972    # Check RGB variable modification from another thread.
32973    if ['$$1']=='-1' break fi # Close request
32974    if (($rgba_mode" && "['$$1']!='$R,$G,$B,$A')" || "(!$rgba_mode" && "['$$1']!='$R,$G,$B'))" && "\
32975       $x<0" && "$y<0" && "!$is_sv" && "!$is_h" && "!$is_a
32976      ($$1) y. c -. 255 r. 1,1,1,4,0 +. 255 sh. 0,2 rgb2hsv. rm.
32977      H={i[0]} S={i[1]} V={i[2]} A={i[3]} rm
32978    fi
32979
32980  while {*}" && "!{*,ESC}" && "!{*,Q}
32981  rm w 0
32982  if $rgba_mode u $R,$G,$B,$A else u $R,$G,$B fi
32983  _xsc_old=${}
32984
32985#@cli x_select_function1d : _variable_name,_background_curve_R,_background_curve_G,_background_curve_B
32986#@cli : Open an interactive window, where the user can defined its own 1D function.
32987#@cli : If an image is selected, it is used to display additional information :
32988#@cli :   - The first row defines the values of a background curve displayed on the window (e.g. an histogram).
32989#@cli :   - The 2nd, 3rd and 4th rows define the R,G,B color components displayed beside the X and Y axes.
32990#@cli : Argument 'variable_name' specifies the variable that contains the selected function keypoints at any time.
32991#@cli : Assigning '-1' to it forces the interactive window to close.
32992#@cli : Default values: 'variable_name=xsf_variable', 'background_curve_R=220', \
32993# 'background_curve_G=background_curve_B=background_curve_T'.
32994x_select_function1d : skip ${1=xsf_variable},${2=220},${3=$2},${4=$2} check_display $0
32995  e[^-1] "Open 1D function widget, with variable name '$1'."
32996  if $! k[0] fi
32997  is_additional_data=$!
32998  if !{*} w[] 400,400,0,"Create a 1D function" fi
32999  reset_w={*,w} reset_h={*,h}
33000  if !narg($$1) $1=0,0,100,100 fi
33001  ($$1) nm. points y. x r. 2,{w/2},1,1,-1
33002  is_thread_variable={arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_'} selected=-1 X=-1 Y=-1
33003  do
33004
33005    # Update base view.
33006    if !narg($baseview)
33007      {{*,d}-48},{{*,e}-48},1,3,255
33008      if $is_additional_data  # Render background graph.
33009        100%,100% +rows[0] 0 graph.. .,3,0,0,0,1,1 rm. c. 0,1
33010        +fc.. ${2-4} j... .,0,0,0,0,1,.. rm[-2,-1]
33011      fi
33012      grid. {(w-1)/8},{(h-1)/8},0,0,0.2,0xCCCCCCCC,0
33013      line. 0,100%,100%,0,0.2,0
33014       frame. 24,24,200
33015      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
33016      if $is_additional_data" && "{0,h}>1 # Render colored X-axis guide.
33017        if {0,h}>2 +rows[0] 1,3 else +rows[0] 1 r. 100%,3 fi
33018        r. {-2,w-48},3,1,1,3 permute. xzcy r. 100%,8 frame. 1,1,0
33019        j.. .,23,{-2,h-19} rotate. -90 j.. .,{-2,w-19},23 rm.
33020      fi
33021      nm. baseview
33022      l rm[view] onfail endl
33023    fi
33024
33025    # Update view.
33026    if !narg($view)
33027      +z[baseview] 24,24,{baseview,w-25},{baseview,h-25} r. 200%,200%
33028
33029      # Draw curve.
33030      function1d[] 1,{points,^}
33031      l. c 0,100 transpose
33032        i[0] ({'CImg3d'},{h},{h-1})
33033        i.. 1,100%,1,1,y 1,100% a[-3--1] x
33034        1,{h-1},1,1,2 +f. y ++. 1 a[-3--1] x
33035        4,100%,1,1,1 y a y col3d 0
33036      endl
33037      *3d. {-2,(w-1)/100},{-2,(1-h)/100}
33038      j3d.. .,0,100%,0,1,1,0,0 rm.
33039
33040      # Draw control points.
33041      repeat h#$points
33042        x={points,i(0,$>)} y={100-{points,i(1,$>)}}
33043        circle. $x%,$y%,6,1,0xFFFFFFFF,0
33044      done
33045      if $selected>=0
33046        x={points,i(0,$selected)} y={100-{points,i(1,$selected)}}
33047        circle. $x%,$y%,3,1,0
33048      fi
33049
33050      r. 50%,50%,1,3,2
33051      +j[baseview] .,24,24 rm..
33052
33053      # Draw current coordinates.
33054      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
33055      nm. view
33056      w[view]
33057    fi
33058
33059    if $is_thread_variable wait 50 else wait fi
33060
33061    # Manage user events.
33062    x={*,x} y={*,y} b={*,b} is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
33063    X={($x-24)*100/({*,w}-49)} Y={100-($y-24)*100/({*,h}-49)}
33064    oww={*,w} owh={*,h} ww=$oww wh=$owh
33065    if {*,r} ww={*,d} wh={*,e} # Resize window.
33066    elif $is_ctrl" && "{*,-D} ww={view,w*125%} wh={view,h*125%} # Increase window size.
33067    elif $is_ctrl" && "{*,-C} ww={view,w*75%} wh={view,h*75%} # Decrease window size.
33068    elif $is_ctrl" && "{*,R} ww=$reset_w wh=$reset_h # Reset window size.
33069    elif !$is_ctrl" && "{*,R} rm[points] (0,0;100,100) nm. points $1={points,^} rm[view] # Reset keypoints.
33070    elif $b&3 # Add/move/delete point.
33071      is_inside={$X>=0" && "$Y>=0" && "$X<=100" && "$Y<=100}
33072
33073      # Check for a point selection.
33074      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
33075
33076      if $x>=0" && "$b&1" && "$selected>=0 # Move an existing point.
33077        if {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} X={points,i(0,$selected)} fi
33078        if {*,CTRLLEFT}" || "{*,CTRLRIGHT} Y={points,i(1,$selected)} fi
33079        if {points,$selected>0" && "$selected<h-1}
33080          =[points] {points,max(min($X,i(0,$selected+1)-0.5),i(0,$selected-1)+0.5)},0,$selected
33081        fi
33082        =[points] {min(100,max(0,$Y))},1,$selected $1={points,^} rm[view]
33083
33084      elif $b&1" && "$is_inside # Create new point.
33085        ($X,$Y) a[points,-1] y sort[points] +,y
33086        +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]
33087      elif $b&2" && "$selected>0" && "$selected<{points,h-1}" && "$is_inside # Delete an existing point.
33088        l[points] s y rm[$selected] a y endl wait -1 selected=-1 $1={points,^} rm[view]
33089      fi
33090    elif !($b&1) selected=-1
33091    fi
33092
33093    # Manage window size.
33094    ww={min(90%*{*,u},max(200,$ww))}
33095    wh={min(90%*{*,v},max(200,$wh))}
33096    if $oww!=$ww" || "$owh!=$wh w[] $ww,$wh rm[baseview,view] fi
33097
33098    # Check points variable modification from another thread.
33099    if ['$$1']=='-1' break fi # Close request
33100    if ['$$1']!=['{points,^}'] # Keypoints changed
33101      rm[points] ($$1) nm. points y. x r. 2,{w/2},1,1,-1 l rm[view] onfail endl
33102    fi
33103
33104  while {*}" && "!{*,ESC}" && "!{*,Q}
33105  w[] 0 u {points,^}
33106  if $is_additional_data rm[^0] else rm fi
33107
33108#@cli x_select_palette : _variable_name,_number_of_columns={ 0=auto | >0 }
33109#@cli : Open a RGB or RGBA color selector widget from a palette.
33110#@cli : The palette is given as a selected image.
33111#@cli : Argument 'variable_name' specifies the variable that contains the selected color values (as R,G,B,[A])
33112#@cli : at any time.
33113#@cli : Assigning '-1' to it forces the interactive window to close.
33114#@cli : Default values: 'variable_name=xsp_variable' and 'number_of_columns=2'.
33115x_select_palette : skip ${1=xsp_variable},${2=0} check_display $0
33116  if !$! error[0--3] "Command '$0': Missing specified palette image." fi
33117  k[0] +r {w*h*d},1,1,{s},-1 to_color. rgba_mode={s==4} to_rgba. nm. palette
33118  e[^-1] "Open "${arg\ 1+$rgba_mode,RGB,RGBA}" color selector widget for palette$?, with variable name '$1'."
33119
33120  if w>1024 error[0--3] "Command '$0': Too much colors ("{w}") in selected palette." fi
33121  if !{*} w[] 400,400,0,0,-1,-1,"Palette: "{0,b} fi
33122
33123  selected=-1 oselected=-1
33124  do
33125    ww={*,w} wh={*,h}
33126    R={palette,round(i($selected,0,0,0))} G={palette,round(i($selected,0,0,1))}
33127    B={palette,round(i($selected,0,0,2))} A={palette,round(i($selected,0,0,3))}
33128
33129    # Update color in specified variable.
33130    if $selected>=0" && "$oselected!=$selected
33131      if $rgba_mode $1=$R,$G,$B,$A else $1=$R,$G,$B fi
33132    fi
33133
33134    # Check close request from external thread.
33135    if ['$$1']=='-1' break fi
33136
33137    # Create base view.
33138    if !narg($baseview) l[palette]
33139      {w},1,1,1,x +. 1
33140      s. x append_tiles[^0] $2
33141      M={w} N={h} 100%,100%,1,1,1
33142      +r. {$ww-17},100%,1,1,4
33143      r.. 100%,{$wh-57},1,1,4
33144      r[-2,-1] .,.. -|[-2,-1]
33145      line. 100%,0,100%,100%,1,1
33146      line. 0,100%,100%,100%,1,1
33147      -. 1 *. -1
33148      r.. .,.,1,1,1 -.. 1
33149      +map.. [0],0 drgba.
33150      rv[-2,-1] *[-2,-1]
33151      +!=.. -1 dilate. 3
33152      mv... $! +. 1 a[-3--1] c
33153      nm. baseview
33154    endl
33155    if narg($view) rm[view] fi
33156    fi
33157
33158    # Create and display view.
33159    if !narg($view)
33160      $ww,$wh,1,3,200
33161      if $selected<0 sh[baseview] 0,2
33162      else
33163        +channels[baseview] 0,2 +channels[baseview] 4,4
33164        !=. {$selected+1} rectangle. 0,0,100%,100%,1,0xFFFFFFFF,1
33165        +dilate. 5 -[-2,-1] *. -1 +dilate. 5 *.. 255
33166        r.. 100%,100%,1,3 j... ..,0,0,0,0,1,. rm[-2,-1]
33167        if $rgba_mode t.. "RGBA ("$R","$G","$B","$A")",8,{$wh-45},14,1,0
33168        else t.. "RGB ("$R","$G","$B")",8,{$wh-45},14,1,0
33169        fi
33170        ($R^$G^$B) rgb2hsv. H={round(i[0])} S={round(i[1]*255)} V={round(i[2]*255)} rm.
33171        t.. "HSV ("$H","$S","$V")",8,{$wh-31},14,1,0
33172        ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'}
33173        f. if(i>=_'a'" && "i<=_'z',i+_'A'-_'a',i)
33174        t... "html ""#"{t},8,{$wh-17},14,1,0 rm.
33175      fi
33176      sh[baseview] 3 j... ..,8,8,0,0,1,. rm[-2,-1]
33177      nm. view w[view]
33178    fi
33179
33180    if arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_' wait 50 else wait fi
33181
33182    # Manage window size.
33183    is_ctrl={{*,CTRLLEFT}" || "{*,CTRLRIGHT}}
33184    if {*,r} ww={*,d} wh={*,e}
33185    elif $is_ctrl" && "{*,-D} ww={1.25*$ww} wh={1.25*$wh}
33186    elif $is_ctrl" && "{*,-C} ww={0.8*$ww} wh={0.8*$wh}
33187    elif $is_ctrl" && "{*,R} ww=400 wh=400
33188    fi
33189    ww={max(200,$ww)} wh={max(200,$wh)}
33190    if ($ww!={*,w}" || "$wh!={*,h})" && "narg($baseview) w[] $ww,$wh rm[baseview] fi
33191
33192    # Handle user events.
33193    oselected=$selected
33194    if narg($baseview)
33195      x={*,x} y={*,y} b={*,b}
33196      if $b&1" && "$x>=0" && "$y>=0  # Select color.
33197        if {baseview,i($x-8,$y-8,0,4)} selected={baseview,i($x-8,$y-8,0,4)-1} else selected=-1 fi
33198        rm[view] wait -1
33199      elif {*,ARROWUP}" && "$selected>=$M selected-=$M rm[view] wait -1
33200      elif {*,ARROWDOWN}" && "$selected<{0,w-$M} selected+=$M rm[view] wait -1
33201      elif {*,ARROWRIGHT}" && "$selected<{0,w-1} selected+=1 rm[view] wait -1
33202      elif {*,ARROWLEFT}" && "$selected>0 selected-=1 rm[view] wait -1
33203      fi
33204    fi
33205
33206  while {*}" && "!{*,ESC}" && "!{*,Q}
33207  w 0 k[0]
33208  if $selected>=0 if $rgba_mode u $R,$G,$B,$A else u $R,$G,$B fi else u -1 fi
33209
33210#@cli x_shadebobs
33211#@cli : Launch the shade bobs demo.
33212x_shadebobs : check_display $0
33213  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
33214  e[] "\n
33215------ "${g}"Shade bobs"$n" -------------------------------\n
33216----\n
33217---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
33218---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
33219---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
33220----\n
33221-------------------------------------------------"
33222  rm t=100 w 512,512,0,"[G"{`39`}"MIC] Shade Bobs"
33223
33224  # Start animation loop.
33225  do
33226    t+=0.015
33227    if $t>4*pi" || "{*,b} # Reset motions variables if necessary.
33228      rx={u(-1,1)} ry={u(-1,1)} rz={u(-1,1)} rt={u(-1,1)} rcx={u(-0.6*0.6)} t=0
33229      N={20+round(u(80))} R={(2+round(u(40)))*min({*,w},{*,h})/300}
33230      if $obj3d rm[colormap,img,obj3d] fi
33231      {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
33232      (67.5;73.5;109.5;103.5;51.5;100.5;{2*$N};$N) 3,{2*$N},1,1,0
33233      1,$N,1,1,5 2,$N,1,1,'y+x*$N' a[-2--1] x z. 0,5
33234      4,$N,1,1,1 y[-3--1] a[-4--1] y nm. obj3d
33235      {*,w},{*,h} nm. img
33236      wait -1
33237    fi
33238
33239    # Compute bobs coordinates.
33240    r={$ry+$rx*cos(6*$rz*$t)+(1-$rx)*sin(6*$rt*$t)}
33241    (0;{30*$ry*($N-1)}) ($t;{2*pi*($N-1)/$N+$t}) r[-2,-1] 1,$N,1,1,3
33242    +.. {360*sin($rz*$t)} *.. {pi/180}
33243    +sin[-2,-1] cos[-4,-3] *[-4,-2] $r *[-3,-1] $rcx +[-4,-3] +[-2,-1]
33244    *.. {{*,w}/2} *. {{*,h}/2} a[-2,-1] x
33245    ++. $R -.. $R a[-2,-1] y z. 0,2 y. j[obj3d] .,0,8 rm.
33246
33247    # Draw bobs, map colors and display.
33248    j3d[img] [obj3d],50%,50%,0,-1,2,0,0
33249    &[img] 255 +map[img] [colormap] w. rm. wait 20
33250    if {*,CTRLLEFT}" && "{*,D} w[] 1024,1024 elif {*,CTRLLEFT}" && "{*,C} w[] 512,512 fi
33251  while {*}" && "!{*,ESC}" && "!{*,Q}
33252  rm w 0
33253
33254#@cli x_spline
33255#@cli : Launch spline curve editor.
33256x_spline : check_display $0
33257  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
33258  e[] "\n
33259------ "${g}"Spline curve editor"$n" --------------------------\n
33260----\n
33261---- "${c}"Mouse"$n" to insert/move/delete points.\n
33262---- Key '"${c}"R"$n"' to reset the curve.\n
33263---- Key '"${c}"SPACE"$n"' to shows/hide spline curve.\n
33264---- Key '"${c}"P"$n"' to shows/hide control points.\n
33265---- Key '"${c}"ENTER"$n"' to shows/hide control polygon.\n
33266---- Key '"${c}"T"$n"' to shows/hide point tangents.\n
33267---- Key '"${c}"I"$n"' to shows/hide point indices.\n
33268---- Key '"${c}"C"$n"' to shows/hide point coordinates.\n
33269---- Keys '"${c}"+"$n"' and '"${c}"-"$n"' to increase/decrease roundness.\n
33270---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
33271----\n
33272-----------------------------------------------------"
33273
33274  # Init display and variables.
33275  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
33276  w[0] {0,w},{0,h},0,0,{n} r[0] {*,w},{*,h},1,3,1
33277  i[1] 1         # Point coordinates
33278  roundness=0.5  # Curve roundness
33279  visuflags=23   # Visualisation flags
33280  nearest=-1     # Nearest point
33281  active=-1      # Active point
33282
33283  # Start event loop.
33284  do
33285
33286    # Init coordinates [1] if necessary.
33287    if {1,whds}==1
33288      rm[1] roundness=0.5 nearest=-1 active=-1
33289      i[1] ({0.2*w},{0.2*h};\
33290            {0.2*w},{0.8*h};\
33291            {0.8*w},{0.8*h};\
33292            {0.8*w},{0.2*h})
33293    fi
33294
33295    # Estimate screen-normalized coordinates [2], curve tangents [3] and tangent orientations [4].
33296    [1] ({{*,w}/{0,w}},{{*,h}/{0,h}}) *[-2,-1]                       # Normalized coordinates.
33297    +shift[2] 0,-1,0,0,2 +shift[2] 0,1,0,0,2 -[-2,-1] *. $roundness  # Curve tangents.
33298    +s. x sqr[-2,-1] +[-2,-1] sqrt. r. 2 +/[-2,-1] rm..              # Tangent orientations.
33299
33300    # Display curve, control points, polygon and tangents.
33301    +r[0] {*,w},{*,h},1,3
33302    if $visuflags&4 polygon. {2,h},{2,^},0.3,128,200,255 fi
33303    repeat h#1
33304      line. {2,@0-3},0.3,255,255,0
33305      if $visuflags&1 spline. {2,@0-1},{3,@0-1},{2,@2-3},{3,@2-3},1,255 fi
33306      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
33307      if $visuflags&16 t. $>,{{2,@0}-3},{{2,@1}-18},13,1,255,255,0 fi
33308      if $visuflags&32 t. "("{round({1,@0})}","{round({1,@1})}")",{{2,@0}-16},{{2,@1}+10},13,1,100,200,255 fi
33309      shift[1-4] 0,-1,0,0,2
33310    done
33311    if $visuflags&2 repeat h#1
33312      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
33313    done fi
33314    w. rm[3,4,-1] wait
33315
33316    # Handle key events.
33317    if {*,SPACE} visuflags+={if($visuflags&1,-1,1)} wait -1 fi  # Show/hide spline
33318    if {*,P} visuflags+={if($visuflags&2,-2,2)} wait -1 fi      # Show/hide points
33319    if {*,ENTER} visuflags+={if($visuflags&4,-4,4)} wait -1 fi  # Show/hide polygon
33320    if {*,T} visuflags+={if($visuflags&8,-8,8)} wait -1 fi      # Show/hide tangents
33321    if {*,I} visuflags+={if($visuflags&16,-16,16)} wait -1 fi   # Show/hide indices
33322    if {*,C}" && "!{*,CTRLLEFT}" && "!{*,CTRLRIGHT} # Show/hide coordinates
33323      visuflags+={if($visuflags&32,-32,32)} wait -1 fi
33324    if {*,PADADD}" && "$roundness<1 roundness*=1.1 wait -1 fi    # Increase roundness
33325    if {*,PADSUB}" && "$roundness>0.1 roundness*=0.9 wait -1 fi  # Decrease roundness
33326    if {*,R}" && "!{*,CTRLLEFT}" && "!{*,CTRLRIGHT} rm. i[1] 1 wait -1 fi  # Reset curve
33327    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} fi # Increase window size
33328    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} fi # Decrease window size
33329    if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} fi             # Reset window size
33330    if {*,r} w[] fi # Resize window if necessary.
33331
33332    # Set/unset active point.
33333    if {*,b}==0 active=-1                            # Unset active point if mouse button is released
33334    elif {*,x}>=0" && "{*,b}" && "$active==-1        # Find new active point
33335      [2] ({*,x},{*,y}) -[-2,-1] sqr. s. x +[-2,-1]  # Compute distance vector to points
33336      nearest={ym}                                   # Set nearest point
33337      if im<64 active=$nearest fi                    # Set it as active point, if near enough
33338      rm.
33339    fi
33340    rm[2]
33341
33342    # Move active point.
33343    if {*,b}&1" && "{*,x}>=0" && "$active!=-1
33344      =[1] {{*,x}*{0,w}/{*,w}},0,$active
33345      =[1] {{*,y}*{0,h}/{*,h}},1,$active
33346
33347    # Delete nearest point.
33348    elif {*,b}&2" && "{*,x}>=0" && "{1,h}>3
33349      l[1] s y rm[$nearest] a y endl wait -1
33350
33351    # Insert new active point.
33352    elif {*,b}&1" && "{*,x}>=0
33353      xy=({{*,x}*{0,w}/{*,w}},{{*,y}*{0,h}/{*,h}})  # Point coordinates in the image basis
33354      +shift[1] 0,-1,0,0,2 +. [1] /. 2              # Compute center of segments
33355      $xy -[-2,-1] sqr. s. x +[-2,-1]               # Compute distance vector to segments
33356      ns={ym} rm.                                   # Get nearest segment
33357      l[1] s y i[{$ns+1}] $xy a y endl              # Insert new point at right position
33358      active={$ns+1}                                # Set new active point as newly inserted
33359    fi
33360
33361  while {*}" && "!{*,ESC}" && "!{*,Q}
33362
33363  # Render spline as a tertiary mask for output.
33364  +shift[1] 0,-1,0,0,2 +shift[1] 0,1,0,0,2 -[-2,-1] *. $roundness
33365  [0],[0],1,1,2 rm[0]
33366  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
33367  flood. 0,0,0,0,0,1,0
33368
33369  # Exit properly.
33370  rm[0,1] w 0
33371
33372#@cli x_starfield3d
33373#@cli : Launch the 3D starfield demo.
33374x_starfield3d : check_display $0
33375  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
33376  e[] "\n
33377------ "${g}"3D starfield"$n" ---------------------------------------\n
33378----\n
33379---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
33380----\n
33381-----------------------------------------------------------"
33382  l[]
33383    ('G\47MIC') s x
33384    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]
33385    expand_xy 6,0 dilate_circ 5 b 0.5 expand_z 1,0 isosurface3d 10% *3d 1,1,5 rv3d
33386    repeat $N col3d[$>] ${-rgb} done
33387    0 t. "Version "${-strver},0,0,48,1,1 r2dy. 18 +f. 255 to_rgb.
33388    random3d 2500 col3d. 255 *3d. 320,200,1000 -3d. 160,100
33389    l3d 0,0,-600
33390    w[] 640,400,0,"[G"{`39`}"MIC] 3D Starfield"
33391    t0=0 t=0
33392
33393    do
33394      320,200,1,3
33395
33396      # Starfield.
33397      l.. s3d
33398        r[2] 3,{2,h/3},1,1,-1 s[2] x %[4] 1000
33399        +/[4] 1000 *. -1 n. 0,2 c. 0,1 sqr. j.. . rm.
33400        a[2-4] x
33401        y a y
33402      endl
33403      j3d. ..,50%,50%,-600,1,0,0,0,240 -3d.. 0,0,{min(12,$t0/10-4)}
33404
33405      # Torus.
33406      torus3d 100,30 col3d. 255,64,255
33407      +col3d. 64,64,255 r3d. 1,0,0,-90 +3d. 65,0,0
33408      +3d[-2,-1] c3d.
33409      r3d. 1,1,0,{-6*$t} r3d. 0,0,1,{2*$t}
33410      j3d.. .,{($t-200)*2}%,50%,0,0.25,3,0,0 rm.
33411
33412      # Letters.
33413      repeat $N
33414        +r3d[$>] 1,{$>%4},1,{-${z$>}/2}
33415        j3d.. .,{90+${x$>}},{60+${y$>}},${z$>},1,4,0,0 rm.
33416        z$>={tl=280+6*$<;if($t<tl,min(0,${z$>}+20),-20*($t-tl))}
33417      done
33418
33419      # Presents.
33420      if $t<280 op={max(0,min(1,($t-200)/20))}
33421      else op={max(0,1-($t-280)/20)}
33422      fi
33423      j. ...,{(w-{-3,w})/2},120,0,0,$op,[-4]
33424
33425      w. wait 30 rm.
33426      t0+=1 t={$t0%350}
33427      if !$t x=0 repeat 5 z$>={-3200-150*$>} done fi
33428
33429    while {*}" && "!{*,ESC}" && "!{*,Q}
33430    w[] 0 rm endl
33431
33432#@cli x_tetris
33433#@cli : Launch tetris game.
33434x_tetris : check_display $0
33435  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
33436  e[] "\n
33437------ "${g}"Tetris"$n" --------------------------------------------\n
33438----\n
33439---- This is a G\47MIC implementation of the "${g}"Tetris"$n" game.\n
33440----\n
33441---- "${c}"Arrow keys"$n" to move/rotate the triominos.\n
33442---- Key '"${c}"SPACE"$n"' to make the current triomino falling.\n
33443---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
33444----\n
33445----------------------------------------------------------"
33446  rm
33447
33448  # Init board and triominos shapes.
33449  4,1,1,1,1,1,1,1
33450  3,2,1,1,1,0,0,1,1,1
33451  3,2,1,1,0,0,1,1,1,1
33452  2,2,1,1,1,1,1,1
33453  3,2,1,1,0,1,1,1,1,0
33454  3,2,1,1,0,1,0,1,1,1
33455  3,2,1,1,1,1,0,0,1,1
33456  nm[-7--1] m0,m4,m8,m12,m16,m20,m24
33457
33458  if u<0.25  # Enable extended set.
33459    i 2,1,1,1,1,1
33460    i 2,2,1,1,1,1,0,1
33461    i 3,1,1,1,1,1,1
33462    i 1,1,1,1,1
33463    i 3,2,1,1,1,1,1,1,0,1
33464    i 3,3,1,1,1,1,1,1,0,1,1,1,1
33465    nm[-6--1] m28,m32,m36,m40,m44,m48
33466  fi
33467  repeat $! i={4*$>} l[m$i] repeat 3 +rotate[0] {90*($>+1)} nm. m{$i+$>+1} done endl done
33468  N=$!
33469
33470  # Render triomino colored sprites.
33471  3,$N,1,1,'u(16,224)' r. 3,400% nm. colors
33472  (0,-1,0;1,0,-1;0,1,0) *. 120 nm. mask
33473  repeat $N
33474    +r[m$>] 500%,500%,1,3 +correlate. [mask],0 r. 200%,200%,1,1,3 ri.. . *[-2,-1] c. 30%,100%
33475    +r[m$>] .,.,1,3 +replace_color. 0,0,1,1,1,{colors,@{3*$>}-{3*$>+2}} rv[-3,-1] +[-3,-1] c.. 0,255
33476    channels. 0 *. 255 a[-2,-1] c nm. s$>
33477  done
33478  rm[colors,mask]
33479  fact={{s0,w}/{m0,w}}
33480
33481  # Generate board and background.
33482  W=12 H=20
33483  $W,$H . nm[-2,-1] board,curr_board
33484  {$fact*$W},{$fact*$H},1,3 . nm[-2,-1] render,curr_render +channels. 0 nm. curr_render_mask
33485  +rows[render] 0,50% plasma. 1,2 noise. 20 blur_y. 40%,1 +mirror. y a[-2,-1] y ri. [render]
33486  n. 0,64 blur_x. 1 100%,100% noise. 0.5,2 ==. 1 b. 1 *. 300 +[-2,-1] c. 0,255 nm. background
33487
33488  # Start game.
33489  time=$| score=0 fall_mode=0 gameover=0 n=-1 nn={round(u(0,$N-1))}
33490  do
33491    wait {if($fall_mode,-1,-20)}
33492
33493    # In case of game over.
33494    if $gameover
33495      +j[background] [curr_render],0,0,0,0,0.7,[curr_render_mask],255
33496      to. "Game\nOver!",22,30%,32,2,1,255 w. rm.
33497      continue
33498    fi
33499
33500    # Check for completed lines and select new random triomino.
33501    if $n<0
33502      l[board] s y i=-1 repeat $! if {$<,im} i=$<,$i fi done
33503      0 rm[$i] a y score+={2^(narg($i)-1)-1} r $W,$H,1,1,0,0,0,1 nm board endl
33504      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
33505      n=$nn nn={round(u(0,$N-1))} x={$W/2} y=0 do_render=1 fall_mode=0
33506    fi
33507
33508    # Render board at current time.
33509    if $do_render
33510      rm[curr_board,curr_render,curr_render_mask]
33511      [board] nm. curr_board j[curr_board] [m$n],{$x-int({m$n,w}/2)},$y,0,0,1,[m$n]
33512      [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.
33513      +*[curr_board] 255 r. [curr_render],[curr_render] nm. curr_render_mask
33514      0 t. "Score : "$score"    Next :",4,0,25,1,200 r. 50%,50%,1,3,2 +!=. 0 *. 255
33515      j[curr_render] ..,0,0,0,0,1,.,255 j[curr_render_mask] .,0,0,0,0,1,.,255 rm[-2,-1]
33516      +*[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.
33517      do_render=0
33518    fi
33519
33520    +shift[background] 0,{round(-13*$|*1.04^$score)},0,0,2
33521    j. [curr_render],0,0,0,0,1,[curr_render_mask],255
33522    w. {2.25*w},{2.25*h},0,"[G"{`39`}"MIC] Tetris" rm. cursor[0] 0
33523
33524    # Manage user interactions.
33525    if {*,SPACE} fall_mode=1 fi
33526    if {*,ARROWUP}" || "{*,ARROWLEFT}" || "{*,ARROWRIGHT}
33527      an={if({*,ARROWUP},n=$n+1;if(n%4,n,n-4),$n)}
33528      nx={w2=int({m$an,w}/2);max(w2,min($x-{*,ARROWLEFT}+{*,ARROWRIGHT},$W-({m$an,w}%2)-w2))}
33529      +j[board] [m$an],{$nx-int({m$an,w}/2)},$y,0,0,-1,[m$an]
33530      if iM==1 x=$nx n=$an fi
33531      rm.
33532      do_render=1
33533    fi
33534
33535    if {*,ARROWDOWN}" || "$|-$time>0.9^int($score/2)" || "$fall_mode # Piece goes down.
33536      y+=1
33537      +j[board] [m$n],{$x-int({m$n,w}/2)},$y,0,0,-1,[m$n]
33538      if iM>1" || "$y+{m$n,h}>$H
33539        if $y<=1 gameover=1 fi  # Game over!
33540        j[board] [curr_board] j[render] [curr_render] n=-1
33541      fi
33542      rm.
33543      time=$| do_render=1
33544    fi
33545
33546  while {*}" && "!{*,ESC}" && "!{*,Q}
33547  rm w 0
33548
33549#@cli x_threshold
33550#@cli : Threshold selected images interactively.
33551x_threshold :
33552  e[^-1] "Threshold image"$_gmic_s" interactively."
33553  repeat $! l[$>]
33554    wsiz0=${"fitscreen ."}
33555    value=-1
33556    w[] $wsiz0,0,"[G'MIC] "{n}" - Interactive threshold"
33557    0
33558    for {*}" && "!{*,ESC}
33559
33560      if [w#1,h#1]!=[{*,w,h}] # Generate image view
33561       rm[1] +slices[0] 50% r. {*,w,h},1,100%,1 w.
33562      fi
33563
33564      mx,my,mb={*,x,y,b}
33565      if $mb" && "$mx>=0" && "$my>=0
33566        value={"w>h?( dw1 = "{*,w}" - 1; val = (dw1 - "$mx")*100/dw1; ):
33567                    ( dh1 = "{*,h}" - 1; val = (dh1 - "$my")*100/dh1; )"}
33568        update_view=1
33569      fi
33570
33571      if $update_view
33572        if $value>=0
33573          +ge[1] $value% *. 255
33574          to. "Threshold: "{_round($value,0.1)}%,1%,1%,{max(13,3.5*h%)}
33575          w. rm.
33576        else w.
33577        fi
33578        update_view=0
33579      fi
33580
33581      wait
33582      if {*,r} update_view=1 fi
33583      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D}
33584        w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 update_view=1
33585      fi
33586      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C}
33587        w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 update_view=1
33588      fi
33589      if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R}
33590        w[] $wsiz0 wait -1 update_view=1
33591      fi
33592    done
33593    w[] 0 rm. u $value% ge ${}
33594 endl done
33595
33596#@cli x_tictactoe
33597#@cli : Launch tic-tac-toe game.
33598x_tictactoe : check_display $0
33599  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
33600  e[] "\n
33601------ "${g}"Tic-Tac-Toe game"$n" -----------------\n
33602----\n
33603---- Use "${c}"mouse"$n" to select positions of the\n
33604---- symbols. Close window to exit game.\n
33605----\n
33606-----------------------------------------"
33607
33608  # Allocate variables.
33609  message=0                    # [-7] : State message.
33610  counter=0                    # [-6] : Turn counter (0 to 8).
33611  player=0                     # [-5] : Current player (0 or 1).
33612  state=0                      # [-4] : Board state.
33613  tmp3=0                       # [-3] : Temporary variable 3.
33614  tmp2=0                       # [-2] : Temporary variable 2.
33615  tmp1=0                       # [-1] : Temporary variable 1.
33616  _x_tictactoe2                # Generate board.
33617  w. -1,-1,0," "               # Init display window.
33618
33619  # Start main loop.
33620  do
33621
33622    # Set state message depending on the current player.
33623    if $player message="Tic-Tac-Toe (O to play)"
33624    else message="Tic-Tac-Toe (X to play)"
33625    fi
33626
33627    # Select position by the user.
33628    do # Enter event loop.
33629      w[] {w},{h},0,"[G"{`39`}"MIC] "$message wait # Wait for events and force window size if necessary.
33630      if !{*}" || "{*,ESC}" || "{*,Q} w[] 0 rm return fi # Quit properly if window has been closed.
33631      if {*,b}&1" && "{*,x}>20" && "{*,y}>20" && "{*,x}<400" && "{*,y}<400 # If mouse button pressed on the board area.
33632        tmp3={int(({*,x}-15)/130)}                                           # X of the selected position (0,1 or 2).
33633        tmp2={int(({*,y}-15)/130)}                                           # Y of the selected position (0,1 or 2).
33634        tmp1={4^($tmp2*3+$tmp3)}                                             # Get state code of the selected position.
33635        if int($state/$tmp1)%4 tmp1=-1 fi                                    # Check availability of position.
33636      else tmp1=-1 fi                                                        # If no mouse button, do nothing but loop.
33637    while $tmp1<0                                                            # Go on until a valid position selected.
33638
33639    # Draw symbol on selected position and update board state.
33640    _x_tictactoe{$player%2}                                                  # Generate the symbol sprite and his mask.
33641    j... ..,{"130*"$tmp3" + 15+u(-5,5)"},\                                   # Draw symbol (with some fuzzyness).
33642               {"130*"$tmp2" + 15+u(-5,5)"},0,0,1,.
33643    rm[-2--1]                                                                # Delete sprite and mask.
33644    w.                                                                       # Update display window.
33645    state+={(1+$player)*$tmp1}                                               # Update the board state.
33646
33647    # Check for a winning configuration.
33648    (21,1344,86016,4161,16644,66576,65793,4368;\                             # The list of winning configurations.
33649     0,0,0,0,1,2,0,0;\                                                       # Corresponding X coords for the stroke.
33650     0,1,2,0,0,0,0,0;\                                                       # Corresponding Y coords for the stroke.
33651     3,3,3,4,4,4,5,6)                                                        # Corresponding index of the stroke sprite.
33652    repeat w                                                                 # Start to check configurations.
33653      tmp1={@$>}                                                             # Save the current configuration code.
33654      if ($state&$tmp1)==$tmp1||($state&(2*$tmp1))==2*$tmp1                  # If a winner has been found.
33655        _x_tictactoe{i($>,3)}                                                # Generate the stroke symbol and his mask.
33656        j[-4] ..,{130*{-3,i($>,1)}+u(-5,5)},\                                # And display it on the board.
33657                        {130*{-3,i($>,2)}+u(-5,5)},0,0,1,. rm[-2--1]
33658        if ($state&$tmp1)==$tmp1 w.. -1,-1,0,"Tic-Tac-Toe (X won!)"
33659        else w.. -1,-1,0,"Tic-Tac-Toe (O won!)"                             # Update display window.
33660        fi
33661        do wait
33662          if {*} w[] {*,w},{*,h} fi
33663        while {*}" && "!{*,ESC}" && "!{*,Q}                                # Wait for the window to be closed.
33664        rm w[] 0 return                                               # And return properly.
33665      fi
33666    done                                                                     # Go on until all configurations checked.
33667    rm.                                                                      # Delete winning configuration data.
33668
33669    player={($player+1)%2}                                                  # Select next player.
33670    counter+=1                                                              # Increment turn counter.
33671  while $counter<9                                                       # Loop to next move until all positions filled.
33672
33673  # Here, the game has been ended without winners.
33674  w[] -1,-1,0,0,"Tic-Tac-Toe (Tied game!)"                                 # Change window title.
33675  do wait
33676    if {*} w[] {*,w},{*,h} fi
33677  while {*}" && "!{*,ESC}" && "!{*,Q}                                    # Wait for the window to be closed.
33678  w[] 0 rm                                                           # Return properly.
33679
33680# Generate Tic-Tac-Toe graphics.
33681_x_tictactoe : # Apply a hand-drawing effect.
33682  spread. 4 b. 6,1,0 sharpen. 0.8 n. 0,1
33683
33684__x_tictactoe : # Apply color to last image and generate corresponding opacity mask.
33685  +f. 1-i +n.. $2,255 +n... $3,255 n[-4] $1,255 a[-4,-2,-1] c
33686
33687_x_tictactoe0 : # Generate a 'X' and his mask.
33688  128,128,1,1,1 line. 15%,15%,85%,85%,1,0 line. 15%,85%,85%,15%,1,0 erode. 12
33689  _x_tictactoe deform. 4
33690  __x_tictactoe 40,40,160
33691
33692_x_tictactoe1 : # Generate a 'O' and his mask.
33693  128,128,1,1,1 ellipse. 50%,50%,22%,22%,0,1,0 ellipse. 50%,50%,15%,15%,0,1,1
33694  _x_tictactoe deform. 4
33695  __x_tictactoe 160,40,160
33696
33697_x_tictactoe2 : # Generate the board.
33698  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
33699  100%,100% noise. 10 b. 8,0 sharpen. 1.5 n. 220,255 *[-2,-1] to_rgb.
33700
33701_x_tictactoe3 : # Generate an horizontal stroke and his mask.
33702  421,130,1,1,1 line. 10%,60%,90%,60%,1,0 erode. 6
33703  _x_tictactoe rotate. {u(-6,6)},1,1,50%,50%
33704  __x_tictactoe 180,10,10
33705
33706_x_tictactoe4 : # Generate a vertical stroke and his mask.
33707  _x_tictactoe3 transpose[-2--1]
33708
33709_x_tictactoe5 : # Generate a ++ diagonal stroke and his mask.
33710  421,421,1,1,1 line. 10%,10%,90%,90%,1,0 erode. 6
33711  _x_tictactoe
33712  __x_tictactoe 180,10,10
33713
33714_x_tictactoe6 : # Generate a +- diagonal stroke and his mask.
33715  421,421,1,1,1 line. 10%,90%,90%,10%,1,0 erode. 6 _x_tictactoe __x_tictactoe 180,10,10
33716
33717#@cli x_warp : _nb_keypoints_xgrid>=2,_nb_keypoints_ygrid>=2,_nb_keypoints_contours>=0,\
33718# _preview_fidelity={ 0=coarsest | 1=coarse | 2=normal | 3=fine | 4=finest },\
33719# _[background_image],0<=_background_opacity<=1
33720#@cli : Launch the interactive image warper.
33721#@cli : Default values: 'nb_keypoints_xgrid=nb_keypoints_ygrid=2', 'nb_keypoints_contours=0' and 'preview_fidelity=1'.
33722x_warp : check "isint(${1=2}) && $1>=2 && isint(${2=$1}) && $2>=2 && isint(${3=0}) && $3>=0 &&
33723                isint(${4=1}) && $4>=0 && $4<=4 && ${6=0.5}>=0 && $6<=1" skip "${5=}" check_display $0
33724  if !$! error[0--3] "Command '$0': Requires at least one input image!" return fi
33725  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
33726  e[] "\n
33727------ "${g}"Interactive image warper"$n" -----------------------------\n
33728----\n
33729---- "${c}"Left mouse button"$n": Add and move keypoint.\n
33730---- "${c}"Right mouse button"$n": Delete keypoint.\n
33731---- Key '"${c}"SPACE"$n"' or "${c}"middle mouse button"$n": Show/hide keypoints.\n
33732---- Key '"${c}"TAB"$n"': Change keypoint radius.\n
33733---- Key '"${c}"SHIFT"$n"': Toggle to original image.\n
33734---- Key '"${c}"R"$n"': Reset keypoints.\n
33735---- Keys '"${c}"ESC"$n"', '"${c}"ENTER"$n"' or '"${c}"Q"$n"': Process fullres and exit.\n
33736----\n
33737-------------------------------------------------------------"
33738
33739  if ${"is_image_arg $5"} pass$5 store. background fi
33740  radius_keypoints=4
33741
33742  repeat $! l[$>] nm={n}
33743    nm img
33744    if narg($background) $background rr2d. {-2,[w,h]},2,3 store. rbackground fi
33745
33746    # Start interactive window.
33747    selected_keypoint=-1
33748    view_keypoints=1
33749    do
33750
33751      # Generate base image.
33752      if !narg($imgb)
33753        if {*} wdims=${"fitscreen "{*,d},{img,{*,d}*h/w}}
33754        else wdims=${"fitscreen "{img,[w,h,1]},128,60%}
33755        fi
33756        w[] $wdims,0,"[G'MIC] Interactive Warp"
33757        +to_color[img] r. $wdims,1,100%,2 n. 0,255 nm. imgb
33758        rmn warp,imgw,imgr
33759      fi
33760
33761      # Generate set of keypoints.
33762      if !narg($keypoints)
33763        if narg($__x_warp_keypoints) ($__x_warp_keypoints) r. 1,{w/4},1,4,-1
33764        else
33765
33766          # Keypoints located on regular grid.
33767          nbp,nbq=$1,$2
33768          1,{$nbp*$nbq},1,4,"const nbp = "$nbp"; const nbq = "$nbq";
33769            p = y%nbp;
33770            q = int(y/nbp);
33771            x = p*100/(nbp - 1);
33772            y = q*100/(nbq - 1);
33773            [ x,y,x,y ]"
33774
33775          # Keypoints located on contours.
33776          nbc=$3
33777          if $nbc>0
33778            +b[imgb] 0.5 gradient_norm. sqrt. {round([w,h]/4)} gaussian. 20%
33779            1,$nbc,1,4,">
33780              begin(ref(crop(#-1),gauss));
33781              st = stats(#-2);
33782              iM = st[1];
33783              xM = st[8];
33784              yM = st[9];
33785              img = vector(#w#-1*h#-1,-iM);
33786              draw(#-2,img,xM - w#-1/2,yM - h#-1/2,0,0,w#-1,h#-1,1,1,-1,gauss);
33787              nxyM = [ xM,yM ]*100/([w#-2,h#-2]-1);
33788              [ nxyM,nxyM ]"
33789            rm[-3,-2]
33790            a[-2,-1] y
33791          fi
33792        fi
33793        N0={h} nm. keypoints
33794        rmn warp,imgw,imgr
33795      fi
33796
33797      # Generate warp field.
33798      if !narg($warp)
33799        subsamp={arg(1+$4,8,6,4,2,1)}
33800        +_x_warp_rbf[keypoints] {imgb,round([w,h]/$subsamp)} *. $subsamp
33801        r. {imgb,[w,h]},1,100%,3 +. '[x,y]'
33802        nm. warp
33803        rmn imgw,imgr
33804      fi
33805
33806      # Generate warped image.
33807      if !narg($imgw)
33808        +warp[imgb] [warp],0,1,3 nm. imgw
33809        rmn imgr
33810      fi
33811
33812      # Render visualization.
33813      if !narg($imgr)
33814        [imgw]
33815        if narg($rbackground) $rbackground ri. .. j.. .,0,0,0,0,$6 rm. fi
33816        nm. imgr
33817
33818        if s==4 drgba. fi
33819        if $view_keypoints
33820          eval[keypoints] "*
33821            begin(
33822              col1 = [ 64,200,255 ];
33823              col2 = [ 255,255,255 ];
33824              fact = ([ w#"$imgw",h#"$imgw" ] - 1)/100;
33825              const radius1 = "$radius_keypoints";
33826              const radius2 = radius1 + 2;
33827              const opacity = min(1,3/"($radius_keypoints-1)");
33828            );
33829            X = round((I)[0,2]*fact);
33830            ellipse(#-1,X,radius2,radius2,0,opacity,0);
33831            ellipse(#-1,X,radius1,radius1,0,opacity,y<"$N0"?col1:col2); I"
33832        fi
33833        w.
33834      fi
33835
33836      # Manage user interaction
33837      wait
33838      mb={*,b} mxy={[{*,x,y}]*100/([{*,w,h}]-1)} mouse_over={{*,x}>=0}
33839
33840      if $mouse_over" && "$mb" && "$selected_keypoint<0" && "h#$keypoints>0 # Determine selected keypoint
33841        selected_keypoint={keypoints,"dmin = inf; kmin = -1; fact = ([w#"$imgr",h#"$imgr"]-1)%;
33842          repeat (h,k,
33843            dist = norm(((I[k])[0,2] - ["$mxy"])*fact);
33844            dist<dmin?(dmin = dist; kmin = k)
33845          );
33846          kmin>=0 && dmin<"max(8,1.5*$radius_keypoints)"?kmin:-1"}
33847      fi
33848
33849      if {*,-SPACE}" || "$mb==4 view_keypoints={!$view_keypoints} rmn imgr fi # Show/hide keypoints
33850
33851      if {*,-R} rmn keypoints __x_warp_keypoints= fi # Reset keypoints
33852
33853      if {*,-TAB} # Change keypoint radius
33854        radius_keypoints={max(2,($radius_keypoints+2)%16)} view_keypoints=1 rmn imgr
33855      fi
33856
33857      if {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} # View original
33858        if {imgb,s==4} +drgba[imgb] w. rm. else w[imgb] fi
33859        do wait while {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} rmn imgr
33860      fi
33861
33862      if {*,r} rmn imgb fi # Window resize
33863
33864      if $mouse_over" && "$mb==1" && "$selected_keypoint<0 # Add keypoint
33865        wxy={warp,I([$mxy]*([w,h]-1)/100,1)*100/([w,h]-1)}
33866        ({"[ "$mxy,$wxy" ]"}) permute. zycx a[keypoints,-1] y
33867        selected_keypoint={keypoints,h-1}
33868        rmn warp
33869
33870      elif $mouse_over" && "$mb==1" && "$selected_keypoint>=0 # Move keypoint
33871        ({"[ "$mxy" ]"}) permute. zycx j[keypoints] .,0,$selected_keypoint rm.
33872        rmn warp
33873
33874      elif $mouse_over" && "$mb==2" && "$selected_keypoint>=0" && "h#$keypoints>4 # Remove keypoint
33875        1,1,1,4,-1 j[keypoints] .,0,$selected_keypoint rm. discard[keypoints] -1 r[keypoints] 1,{keypoints,h/4},1,4,-1
33876        N0-={$selected_keypoint<$N0?1:0} selected_keypoint=-1
33877        rmn warp
33878
33879      elif !$mb selected_keypoint=-1
33880      fi
33881    while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER}
33882    if !$< __x_warp_keypoints={keypoints,^} fi
33883
33884    +drgba[imgw] to. "Processing fullres...",5,5,20,2 w. rm.
33885    k[img,keypoints]
33886    _x_warp_rbf[keypoints] {img,[w,h]} +. '[x,y]'
33887    warp[img] .,0,2,3 rm. nm $nm
33888  endl done c 0,255 w 0
33889
33890# Generate 2d warping field from set of keypoints, using RBF reconstruction.
33891# $1,$2 = width,height
33892_x_warp_rbf :
33893  if !h $1,$2,1,2,[x,y] return fi
33894  $1,$2,1,2,"*
33895    begin(
33896      fact = ([w,h] - 1)/100;
33897      xy(p) = (I[#0,p])[0,2]*fact;
33898      const N = h#0;
33899      ref(vector(#N*N),A);
33900      ref(vector(#N*2),B);
33901      repeat (N,p,
33902        repeat (N,q,  A[q + N*p] = A[p + N*q] = norm(xy(p) - xy(q)));
33903        copy(B[2*p],(I[#0,p])[2,2]*fact - xy(p),2);
33904      );
33905      W = solve(A,B,2);
33906    );
33907    res = [ 0,0 ];
33908    repeat (N,p,res += W[2*p,2]*(norm([x,y] - xy(p))));"
33909  k.
33910
33911#@cli x_waves
33912#@cli : Launch the image waves demo.
33913x_waves : check_display $0
33914  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
33915  e[] "\n
33916------ "${g}"Image waves"$n" --------------------------\n
33917----\n
33918---- "${c}"Left mouse button"$n" to drop balls.\n
33919---- "${c}"Right mouse button"$n" to rotate view.\n
33920---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
33921---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
33922---- Keys '"${c}"CTRL+F"$n"' to switch fullscreen mode.\n
33923---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
33924----\n
33925---------------------------------------------"
33926
33927  if !$! l[] # Generate fractal image
33928    200,200 x={-1.06-u*0.1} y={-0.26-u*0.1}
33929    mandelbrot $x,$y,{$x+0.1},{$y+0.1},256
33930    16,1,1,3,u r. 256,1,1,3,3 shift. 1
33931    map[0] . rm. r2dx 100
33932    +mirror y +mirror x + n 0,128
33933    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
33934  endl else k[0] r[0] 100,100,1,3,2 fi
33935  i[0] (20;80;0^20;80;0^20;80;0) r[0] 400,300,1,3,3 water[0] 100,2
33936  w[0] {0,1.25*w},{0,1.25*h},0,"[G"{`39`}"MIC] Image Waves"
33937  w={w} elevation3d. 0 rv3d.
33938  sh. 8,{7+3*i[6]},0,0 r. 3,{h/3},1,1,-1
33939  (0,1,0;1,0,1;0,1,0) /. 2
33940  ball[] 20,200,255,128,1,0.7,3.5
33941
33942  0 $w,$w .
33943  l3d {$w/2},-200,-1000 sl3d 0.4 ss3d 0.8 f3d 500 time0=$|
33944  do
33945    +convolve. [3],1 -. ... rm... b. 0.8 -. {ia} # Update height map.
33946    r. 1,{$w*$w},1,1,-1 j[2] .,2,0 r. $w,$w,1,1,-1 # Set 3D object coordinates.
33947    [1]
33948    if {5,h} +l[5] rows 0,2
33949      nb={w}
33950      i[0] ('CImg3d') i[1] ($nb,$nb) transpose[2]
33951      (1,0;1,{$nb-1}) r. 2,$nb,1,1,3 round.
33952      1,{4*$nb},1,1,1 y a y
33953    endl [4] sprites3d.. .,1 rm. +3d[-2,-1] fi
33954    -3d. {$w/2},{$w/2} *3d. {0,0.9*max(w,h)/$w} # Center and scale 3D object.
33955    r3d. 0,0,1,{if({*,b}&2,{*,x}*360/{*,w},$|*30)} r3d. 1,0,0,120 # Get rotated 3D object.
33956    +j3d[0] .,50%,65%,30,1,3,0,0
33957    fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,2,0.2 fi
33958    w.
33959    if {*,CTRLLEFT}" && "{*,D} w[] {2.25*w},{2.25*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.25*w},{1.25*h}
33960    elif {*,CTRLLEFT}" && "{*,F}
33961      if !narg($is_fs) is_fs={*,w},{*,h} fw={min({*,u}*h/w,{*,v}*w/h)} w[] $fw,{$fw*h/w},0,1
33962      else w[] $is_fs,0,0 is_fs=""
33963      fi
33964    fi
33965
33966    rm[-2,-1] wait 20
33967    if {*,b}&1||($|-$time0)>1 ({u*$w};{u*$w};70;0) a[5,-1] x time0={$|-u} fi # Insert new ball.
33968    if {5,h} l[5,-1] # Manage ball motion and collision.
33969      sh[0] 2,2,0,0 sh[0] 3,3,0,0 -.. . +. 0.2 rm[-2,-1]
33970      s[0] x repeat $!-1 coords={$<,@0-1} if {$<,@2}<i($coords) =. {80+{i($coords)}},$coords rm[$<] fi done
33971      if $!==1 i[0] 0 else a[0--2] x fi
33972      endl
33973    fi
33974  while {*}" && "!{*,ESC}" && "!{*,Q}
33975  rm w 0
33976
33977#@cli x_whirl : _opacity>=0
33978#@cli : Launch the fractal whirls demo.
33979#@cli : Default values: 'opacity=0.2'.
33980x_whirl : check "${1=0.2}>=0" check_display $0
33981  use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r
33982  e[] "\n
33983------ "${g}"Fractal whirls"$n" ----------------------------\n
33984----\n
33985---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n
33986---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n
33987---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n
33988----\n
33989--------------------------------------------------"
33990
33991  5,5,1,3 256,256,1,3 [-1] w. 384,384,0,"[G"{`39`}"MIC] Fractal Whirls"
33992  tangle=0 tzoom=0 xc={(w-{-3,w})/2} yc={(h-{-3,h})/2}
33993  do
33994    rand... 0,255 j.. [-3],$xc,$yc,0,0
33995    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)"
33996    tangle+=0.001
33997    tzoom+=0.02
33998    j. [-2],0,0,0,0,$1 w.
33999    if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi
34000    wait 20
34001  while {*}" && "!{*,ESC}" && "!{*,Q}
34002  rm[-3,-2] w[] 0
34003
34004#-------------------------------------
34005#
34006# Define menu entries for
34007# the G'MIC plug-in
34008#
34009#-------------------------------------
34010
34011# Function that returns the list of external sources to be included in the plug-in.
34012# This command can be superseded on the user '.gmic' file to add sources for the plug-in.
34013# $1 = try network update
34014# $2 = try 'gui_filter_sources' defined in local update file.
34015gui_filter_sources : skip ${1=0},${2=1}
34016
34017  # Try to update the command 'gui_filter_sources' itself.
34018  if $2
34019    local=${_path_rc}update$_version.gmic
34020    if isfile(['{/$local}']) m $local fi
34021    if isfile(['{/$_path_user}']) m $_path_user fi
34022    gui_filter_sources $1,0
34023  fi
34024
34025  # Try to clean older update files to save disk space.
34026  if u>0.95 l[]
34027    files=${"files "$_path_rc"/update*.gmic"}
34028    repeat narg($files)
34029      file=${arg\ 1+$>,$files}
34030      ('$file') z. {[w-8,w-6]} ver={t} rm.
34031      if isnum($ver)" && "$ver<$_version delete $file rm. fi
34032    done
34033  onfail
34034  endl fi
34035
34036  # Build list of filter sources.
34037  ({'https://gmic.eu/update$_version.gmic'},1) # Include stdlib update (last 1 -> Override default filters).
34038  l[] _gui_filter_sources $1 onfail rm endl # Invoke custom command to include other filter sources.
34039  l[] i cimgz:${_path_rc}gui_filter_sources onfail endl # Include other user-defined filters sources.
34040  ('{/$_path_user}') # Include local '.gmic' (or 'user.gmic') file.
34041
34042# Function that always returns a valid preview size.
34043gui_preview_wh :
34044  u {0$_preview_width?[0$_preview_width,0$_preview_height]:[400,400]}
34045
34046# Function used for filters based on parallelization with spatial splitting.
34047gui_parallel_overlap :
34048  apo "$1",$3,{if($2,2^($2-1),0)}
34049
34050# Function that renders a single preview image from multiple preview images.
34051# Pre-cond : Number of image is $!>1.
34052gui_preview :
34053  frame 1,1,0,0,0,255 montage B
34054
34055# Return name of a layer.
34056gui_layer_name :
34057  u ${"_gui_merge_layers[0] name,[unnamed]"}
34058
34059# Return blending mode of a layer.
34060gui_layer_mode :
34061  u ${"_gui_merge_layers[0] mode,alpha"}
34062
34063# Return opacity of a layer.
34064gui_layer_opacity :
34065  u ${"_gui_merge_layers[0] opacity,100"}
34066
34067# Return (x,y) position of a layer.
34068gui_layer_pos :
34069  u ${"_gui_merge_layers[0] pos,0,0"}
34070
34071# Set name of a layer.
34072gui_set_layer_name :
34073  repeat $! l[$>]
34074    opacity=${-gui_layer_opacity} mode=${-gui_layer_mode} pos=${-gui_layer_pos}
34075    nm "name($1),mode("$mode"),opacity("$opacity"),pos("$pos")"
34076  endl done
34077
34078# Set blending mode of a layer.
34079gui_set_layer_mode :
34080  repeat $! l[$>]
34081    name=${-gui_layer_name} opacity=${-gui_layer_opacity} pos=${-gui_layer_pos}
34082    nm "name("$name"),mode($1),opacity("$opacity"),pos("$pos")"
34083  endl done
34084
34085# Set opacity of a layer.
34086gui_set_layer_opacity :
34087  repeat $! l[$>]
34088    name=${-gui_layer_name} mode=${-gui_layer_mode} pos=${-gui_layer_pos}
34089    nm "name("$name"),mode("$mode"),opacity($1),pos("$pos")"
34090  endl done
34091
34092# Set position of a layer.
34093gui_set_layer_pos :
34094  repeat $! l[$>]
34095    name=${-gui_layer_name} mode=${-gui_layer_mode} opacity=${-gui_layer_opacity}
34096    nm "name("$name"),mode("$mode"),opacity("$opacity"),pos("{round("$1")},{round("$2")}")"
34097  endl done
34098
34099# Flatten list of input layers according to their blending modes, opacities and positions,
34100# set in each layer name by the G'MIC plug-in.
34101gui_merge_layers :
34102  if !$! return fi
34103  mode0=${"_gui_merge_layers. mode,alpha"}
34104  opacity0=${"_gui_merge_layers. opacity,100"}
34105  pos0=${"_gui_merge_layers. pos,0,0"}
34106  if $opacity0<100" || "['$pos0']!='0,0' 100%,100%,1,4 fi
34107  wh0={w},{h}
34108  wh=${-max_wh} r. $wh,1,100%,0
34109  repeat $!-1 l[-2,-1] rv
34110    mode=${"_gui_merge_layers[1] mode,alpha"}
34111    opacity=${"_gui_merge_layers[1] opacity,100"}
34112    pos=${"_gui_merge_layers[1] pos,0,0"}
34113    to_a[1] r[1] $wh,1,100%,0
34114    shift[1] ${u\ $pos},0,0
34115    to_colormode[0,1] 0
34116    blend $mode,{max(0,min(1,$opacity/100))}
34117  endl done
34118  r $wh0,1,100%,0
34119
34120_gui_merge_layers :
34121  u {`"
34122    str = ["{'{n}'}"]; const sstr = size(str);
34123    def = ['${2--1}'];
34124    ker = ['$1(']; const sker = size(ker);
34125    const N = max(size(str),size(def));
34126    ref(vectorN(0),res);
34127    p = q = find(str,ker);
34128    p>=0?(
34129      q+=sker;
34130      r = find(str,'),',q);
34131      q = r>=0?r:(str[sstr-1]==_')'?sstr-1:-1);
34132    );
34133    q>=0?copy(res,str[p + sker],q - p - sker):(def = ['${2--1}']; copy(res,def,size(def)));
34134    res"`}
34135
34136# gui_split_preview : "command",_split_type,_split_posx,_split_posy
34137# 'split_type' can be { 0=no split | 1=forw. horiz. | 2=forw. vert. | 3=back. horiz. | 4=back. vert. |
34138# 5=dupl. left | 6=dupl. top | 7=dupl. bottom | 8=dupl. right | 9=dupl. horiz.l | 10=dupl. vert. |
34139# 11=checkered | 12=checkered inv. }
34140# if 'posx' or 'posy' are equal to 'nan', splitting is done at the middle, and moving is not possible.
34141# 'compute_entire_image' can be { 0=no | 1=yes }.
34142gui_split_preview : check "isint(${2=0}) && $2>=0 && $2<=12 && ${5=0}>=0" skip "${3=nan},${4=nan}"
34143  __split_preview="$1" m "_split_preview : run $__split_preview k[0]"
34144  is_movable={!isnan($3)" && "!isnan($4)}
34145  posx,posy={$is_movable?cut([$3,$4],0,100):[50,50]}
34146  pw,ph=${-gui_preview_wh}
34147  repeat $! l[$>]
34148    is_failed=0
34149    +l
34150      apply_timeout _split_preview,0$_preview_timeout
34151    onfail
34152      if 0$_is_timeout gui_timeout_preview
34153      else gui_error_preview ${}
34154      fi
34155      is_failed=1
34156    endl
34157
34158    if $is_failed" || "!$2 k.
34159    else
34160      drgba rr2d $pw,$ph,0,2
34161      r {[max(w#0,w#1),max(h#0,h#1)]},1,100%,0,0,0.5,0.5
34162      posx,posy={round([$posx,$posy]*([w,h]-1)%)}
34163
34164      if $2==1" || "$2==3 # Forward/backward horizontal
34165        1,[0],1,1,'y>=$posy?($2==1):($2==3)' r. [0],[0],1,1 j[0] [1],0,0,0,0,1,.
34166      elif $2==2" || "$2==4 # Forward/backward vertical
34167        [0],1,1,1,'x>=$posx?($2==2):($2==4)' r. [0],[0],1,1 j[0] [1],0,0,0,0,1,.
34168      elif $2==5 # Duplicate top
34169        j[0] [1],0,$posy
34170      elif $2==6 # Duplicate left
34171        j[0] [1],$posx
34172      elif $2==7 # Duplicate bottom
34173        j[0] [1],0,{$posy-h}
34174      elif $2==8 # Duplicate right
34175        j[0] [1],{$posx-w}
34176      elif $2==9 # Duplicate horizontal
34177        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
34178      elif $2==10 # Duplicate vertical
34179        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
34180      elif $2==11 # Checkered
34181        1,[0],1,1,'y>=$posy' [0],1,1,1,'x>=$posx' r[-2,-1] [0],[0],1,1 xor[-2,-1]
34182        j[0] [1],0,0,0,0,1,.
34183      elif $2==12 # Checkered reverse
34184        1,[0],1,1,'y<=$posy' [0],1,1,1,'x>=$posx' r[-2,-1] [0],[0],1,1 xor[-2,-1]
34185        j[0] [1],0,0,0,0,1,.
34186      fi
34187      k[0]
34188
34189      dir=0
34190      if isin($2,1,3,5,7,9,11,12)
34191        dir+=1
34192        line 0,$posy,100%,$posy,0.75,0xF0F0F0F0,255 line 0,$posy,100%,$posy,0.75,0x0F0F0F0F,0
34193        if $is_movable
34194          coords={p=[$posx,$posy];[p+[-10,-1],p+[10,-1],p+[0,-11]]}
34195          polygon 3,$coords,0.7,255
34196          polygon 3,$coords,0.7,0xFFFFFFFF,0
34197          coords={p=[$posx,$posy];[p+[-10,1],p+[10,1],p+[0,11]]}
34198          polygon 3,$coords,0.7,255
34199          polygon 3,$coords,0.7,0xFFFFFFFF,0
34200        fi
34201      fi
34202      if isin($2,2,4,6,8,10,11,12)
34203        dir+=2
34204        line $posx,0,$posx,100%,0.75,0xF0F0F0F0,255 line $posx,0,$posx,100%,0.75,0x0F0F0F0F,0
34205        if $2!=9" && "$is_movable
34206          coords={p=[$posx,$posy];[p+[-1,-10],p+[-1,10],p+[-11,0]]}
34207          polygon 3,$coords,0.7,255
34208          polygon 3,$coords,0.7,0xFFFFFFFF,0
34209          coords={p=[$posx,$posy];[p+[1,-10],p+[1,10],p+[11,0]]}
34210          polygon 3,$coords,0.7,255
34211          polygon 3,$coords,0.7,0xFFFFFFFF,0
34212        fi
34213      fi
34214      l[]
34215        0 +t. "After",0,0,20,1,255 t.. "Before",0,0,20,1,255
34216        autocrop 0 z[0] {0,[-1,-1,w,h]} z[1] {1,[-1,-1,w,h]}
34217        if isin($2,3,4,7,8,12) rv fi
34218        +dilate[-2,-1] 3 /[-2,-1] 255 r[-4,-3] 100%,100%,1,3
34219      endl
34220
34221      if {-4,"const c1 = "$posx">w+2; const c2 = "$posy">h+2; arg("$dir",c2,c1,c1&&c2)"}
34222        j[0] [-4],2,2,0,0,1,..
34223      fi
34224      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)"}
34225        j[0] [-3],{[w#0-3-w,isin($2,11,12)?2:h#0-3-h]},0,0,1,.
34226      fi
34227      k[0]
34228    fi
34229  endl done
34230  um _split_preview
34231
34232# Command to display text on a the G'MIC plug-in preview.
34233# $1 : header message
34234# $2 : header font size.
34235# $3 : main message
34236# $4 : main font size.
34237gui_print_preview : skip "${1=},${3=}" check "${2=32}>=0 && ${4=20}>=0"
34238
34239  # Resize image to output resolution.
34240  if $! k[0] fi
34241  drgba
34242  siz={0$_preview_width?[0$_preview_width,0$_preview_height]:[${fitscreen\ {[max(w,1),max(h,1),1]},400}]}
34243  sizw={arg(1,$siz)-8}
34244  if $! rr2d $siz,2,3 drgba else $siz,1,3,128 fi
34245  (1;0.5^1;0.5^0;1)
34246  (0,0.5,0;0.5,1,0.5;0,0.5,0) *. 0.65
34247  ri[-2,-1] ...,3 * c 0,255
34248
34249  # Render title.
34250  l[]
34251    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)}
34252    r. 100%,140%,1,100%,0,0
34253  onfail rm
34254  endl
34255
34256  # Render message.
34257  l[]
34258    0 t. "$3",0,0,$4,1,255 i.. 100%,100%,1,3,255 a c
34259    ('"$3"') is_err={"crop(0,4)=='*** '"} rm.
34260
34261    if $is_err
34262      x={"ref(crop(0,0,0,3,32,h,1,1),T);
34263          for (c = 32, c<w, ++c, crop(c,0,0,3,32,h,1,1)==T?break());
34264          c<w?for (0, c<w, ++c, max(crop(#-1,c-2,0,0,3,5,h,1,1))<=0?break());
34265          c<w?c:0"}
34266      if $x +z. {$x+1},100% z.. 0,$x sh.. 0,2 fc. 0,255,0 rm. i.. 1,10 fi
34267    fi
34268
34269    repeat $! l[$<]
34270      for w>$sizw
34271        x={"const c0 = "$sizw"-1;
34272            const c02 = c0/2;
34273            for (c = c0, c>=c02, --c, max(crop(#-1,c-2,0,0,3,5,h,1,1))<=0?break());
34274            c<c02?for (c = c0, c>=c02, --c, max(crop(#-1,c,0,0,3,1,h,1,1))<=0?break());
34275            c<c02?c0:c"}
34276       +z. {$x+1},100% z.. 0,$x
34277      done
34278    endl done
34279    a y,0
34280  onfail rm
34281  endl
34282
34283  a[^0] y,0.5
34284  r. [0],[0],1,100%,0,0,0.5,0.5
34285  blend alpha
34286
34287gui_no_preview : skip "$*"
34288  gui_print_preview "",0,"No preview available",32
34289
34290gui_timeout_preview :
34291  gui_print_preview "",0,"Preview timeout",32
34292
34293gui_warning_preview :
34294  gui_print_preview "Preview warning:",32,"$*",22
34295
34296gui_error_preview :
34297  gui_print_preview "Preview error:",32,"$*",20
34298
34299gui_check_version :
34300  if $_version<$1
34301    gui_error_preview "This filter requires at least version *"${"strver $1"}"* of the G'MIC framework.\n\n"\
34302                      "https://gmic.eu/download.html"
34303    u 0
34304  else u 1
34305  fi
34306
34307# Function to auto-crop layers and set their positions.
34308gui_autocrop_layers :
34309  repeat $! l[$>]
34310    nm=${-gui_layer_name}
34311    mode=${-gui_layer_mode}
34312    opacity=${-gui_layer_opacity}
34313    coords=${autocrop_coords\ auto}
34314    z $coords
34315    nm mode($mode),opacity($opacity),pos({arg(1,$coords)},{arg(2,$coords)}),name($nm)
34316  endl done
34317
34318
34319#@gui ____<i>About</i>
34320#---------------------
34321
34322#@gui ♥ Support Us ! ♥ : _none_,fx_support_us
34323#@gui : note = note{"<center><a href="https://gmic.eu"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAA\
34324# BXCAMAAACJMYjxAAAAXVBMVEX+/v4cO0s7OU4wPnEiJSju6+qiqrCMlZu3u8BufoHd3+FDY2JRLzljS2bLz9Lp9vrteR34lyZqHE3puI3S7/hbX6GUW4z\
34325# wza97QiOaYUa6mqTdfz25i22+VxzqoGYqrht5AAAWRklEQVR42uyabZfiKBCFFYUCDDg7TqIh0f//M7duBSEx2p09Z18+7NzTPUYaE/J4qyjI7H7rt/5m\
34326# nXe/9VtfS+vdb21U7Me/hNbblPzu/6czEs/Y39LmT/jU36C0+7/p/OvXeaf7S3+7bkQ19uN4uVzGG3/i31X0/3GyEFjXnu+991sCMN36dIGu479tLR9Cl\
34327# +J/ll3PLMBKbJXbipbW+nq9Lgan2U/cl8V/4Tz3r8r+hEKMu/9CDCrDStdLz7RKXDGly+MxTBqZjM4hyJ3GR4Y1/suwOoHVuf8EFmgxK6YFWMyBI0yaGd\
34328# Two+qPH8PwuDAvLX36fiyw/oFKRcdEHYuS9UtXB4GVSP/9HLw15JwjMtGfzx/jELwyLCjp3fUx/AEBUzlgYI8rh6oItBCGl+/TjH975Rj9p5QI+4gaVug\
34329# YWPnQxOrvN5an0yFrv9+rE9m3oz7zD5xlxyviEBpHRiV+KqQgHAzoIeLAxAe+nw2tifq88pKn5P3bUTccZqzC69g0nc0f7f4ZY3l3OLQioQUdTsb/eu0W\
34330# /RSKu8iwsrXuHHWvKsju2Vlgde3774cd2dxRe0s2xjI4psEhtl5ZpvAzdIVVNVgQXDo8jaWJQ9RGCdLtgeZjtAi26BftBqgyrCrg0suqxdvcoEdm1fdgx\
34331# Uiqo1YOuxdjbasc6DmO1j5haeqgkLxe5e9uxQpid/EdpoYPA5FGxwYi+55WtCzDIlaMku6EgFL7gzJ2Hvbu0BKrsKq07KKfib/K150uOQoZzicVWr0E4a\
34332# 3XG2AZMhgGw/LzcollzeLzuiu2Wotj0YcmG8sz6aYBMPM+fSl1UECzVyxjcEnFDS2i7HCYw9KnfQukS1Lod2ILzg1oTfTPW/DjBOtebPVKaUZrFFa3DYl\
34333# WOzLmgKGbikaH0DIr8i+Z/QXVglqiCD6pMxwFAqthWDTjXRWVaqkFLdx1hnUgk3OStTNWh/YV1WmC5cwsN6K+MtZH87QW0wKrAmjOavq3RGJ/vT5uN7sp\
34334# dz7tbWNtVbCWNFSljmml9POtGjaUt10TkN2TIBVrBeN3azlmxbBYFRY72+QmYxcxiAEKo6JWPjY3FiZBS9YXJ/N67zGxenwOQ2hAJG5k1eLKIuNrs7E2d\
34335# fB0VQwpMau3MZgzlY+J/bVLPRJeppXsGzerYLquPYSDaILVSjZol7DoUPP66XQCMMkYMBbRnBVEZA35PLVzgr8Lq8vlBRfauLHQYlQbqgYGUFmpEP0MVp\
34336# QEUBU7NhZ+wwoU+CWHzjryffq+76q1yOxWsgod2swqCCxdqVRYtrDKtFrhJZnNOL+Edf4VnTPGABZ07X9kLqBVA5KboFrV3/uL3jBZW6SJIoql3NJ4lbm\
34337# wZrEEUBlV6JKoe2b3TpjggxptcBYnLT56F4cyfTzDsM2wGN0rLH+qrNDTiMidOK85fAsra52MLcGphwGskL3FRiVjXbIeBdYGVlKmE3+1eTjB+o/rHNsh\
34338# BGEWqEsO8wIlFrCwscqH/E9+3zMxwFrFYc2IXeCJMKgShqDxhGLnQVjKKoOLEpEAY2OtYJ396eScezrrDmMB1eUKWktjQUOGdU+bHmboGC2PrQWq0JqPh\
34339# HUnsEJmRS7q536ssYnZ2YoCjPpeWCEO3eqsRmAFwOLL5jD0cqzmzvJBWO0zK0dRT9eMfFGy690ZnPp0Utlyo0ShFPLjDNYPbuJlRz+D9dFaZ1zK4BuyXg\
34340# LOU2gxYMvKCN4ZC4KHEHNUawrhZRPpJSx0E3EcpvgahSqwlKgmeD5+gUXzIKQCPQPT7zaUz+HIbtWzlDU2Y2qa8TJz1tiH4/EYZrDub/O7NxzvEM/IJlq\
34341# pryPKQH4X/eddziCsWAp371531crIK6wGKvNhsfKEBcZ6AwuqsHQ7M5Yj0lv2SO2xCYrw9oLCgac6e22OxwbOyrQefeIW1fRDhTW+OR0dGBOEuGcjTTmK\
34342# z26snnWrL1m2EVaN8MqFc+2Onh9gsRWbzujF+QhRWKz1ARab3s6NhRT1LSzQcrBWRBRKlXUftQ4Mq6+whhEtoPWjwOr96mwtk5LrE5lqBm0dWHnnQDDGC\
34343# IMtAThJMpAKKDu/2vjygMRgWRlbiuhUunHiE1TfwNJUWSEOdltgcZzg4o4v2Aus4bo7O0ZzGzIrNHmGtVfuNoO1SvEGrKRWIDsnqWUNaLFIZFkogViRlr\
34344# oo3yAS1lznNSyo0mpSwr3nE8rdFFisAmuvFrCe6X2/0ViSs1gEa1meC5+wrFNHNYel6XRUh1PqMyvWsIrDYixeKmj9mnK8td5r7JIYsqGbP3mwKvwEK9F\
34345# 6t+r8AuvYzMVLnuRn/nPcxD6tsMISViuw5KLFWOC5VVodQwhaYLEuu2idc/0cliEub91lmMHq9RtYeTxEbrXXiCmZnCDpQkewUt7Vo9Bx5DQKCmS/WUFV\
34346# TjXFV6Y6iLPU2lnsCGzRnDIsUihSJ4DOxbwP+oWtOGGJDJ8oULrdp9WOzNZpnIfh1aAeyLDu0GO13rHTOgHI2N5tG5zxHknKskzX5jsIiiFxW2JcTUD0c\
34347# NleYJHzfxEWth7qTRrZilZvYYFVmQ15NO0hwzKoeeVHz3iti9KcFNlaamRYU6Glr8yKYZUEz0V9onTlakJYyXpnlbR0ttbkbqQDHLYQv1VZoTOWlZg+Xm\
34348# yEGQIytmr4l0hvhFUVbAW8NlYNQ2EFeja/z3WDMjQRgfjlLatKKx4bsleeDbGl/ED9joUgU2IBliyBuHHIrADrNr6eMeLiUFs8VqWODArFFyhFryGplQF\
34349# LyQ0GFk9Lm2Dd5rSsnf9VWK1z1mQslWHF4/4QcnpHg6ACj/fOmmDlN2TxPLUfhMZDuNwHNhQzY1x3pgUN8NRkrsdlvafsY766WGlBCqBkp9eua9MISp3t\
34350# Al7IboA1Lo01W/KkEF5ZlZ1SNa/gLb/l99NwKWZWrC+sVdo1i601INCYDfYXJlbyoJXh8KF4amKFh/1xt6ZFe/7GXtUSMwIopK8YnERNlT2q4iyi+D2s/\
34351# trfFrCaso8YXmDBS7MELw0Cy4BjzllTnnzCOn/I7/gLDsCq0GIhhXPsZY0XpgUNhdXt8X4DXsdIbYXUGgk6cPI+GpkKwyotGYEl6Yxh+e9h3fopDsfQZC\
34352# Uz+SEWVqbQqrBYBRbhgOGV/H6usM7vH0aDE5gJLAnEsgU/cAAKKn6Qwz4rAkjeg+8/bJT6aCFjJ0X8IjWRUxDGR+YTLAG5ARaDAi2imuJ1jsJMy5njN7A\
34353# wOeYwRH6vzvpsrHOOxgLrxt7KsC5PVgKrosLj6C+egmmfgcXImKKhVsbUYnABG7avOZxU0SZYRc7WFB/zTk92lnErWDUuAcsdi7P2Aqsm+PPnyVAOsrPw\
34354# KJBpAcqAnA5cMjMWWBOqy4b/+BaJKK8mltPinsdaVBYQrLCCpckVAQfkb002FvupxCFZuWZZNNl3sMISFqZGHt9+goUYg/jwa1jnJ6x0Y2sJLZ4KJ00lA\
34355# 5rQeAeqccsGvJ0/tMRx0SusKWKCc2tY6lj0/IuvK8OQahwaL1GYa1tHQX3tLJKL7iGEIaQ/FfGCaOI00cxR2PR4ajGwYCxgkRfOX9CIRu6QNmwqWzCCch\
34356# lRZa3Wi55SW/OQD29gcdZvBJYrsIpCSuUYAHRN71tgIWWJDGUoAuxLWKwCK/AUAx4PcdTY31h9P/KxmExQjVHvNsiAi7B6kVnDkoG70wqW54bUHdewssi\
34357# EfCSzhi2wyLyDFWqCFztzBIqmi+pC6nMYZmkA6dnfNyZ1haRBlN9PpvK7bTLqhdXzKBirX4pSZLa9fOmO/CwWvMIzH0ahlmFYCRFeGixtTdy55mksZ9ew\
34358# JDPu1T4Xpcc/STe7JSdCIArLCDQ97OCFVZaxtnz/x7RPA2kGknEsj7pGFoT56NPLTyKdbgZLDXYLFhTEakiaHw/kJQix1d71h5ACKQTVTZFHMhhZ4Tdep\
34359# Cln7QJJB67r/NLt8E1h0Q/W5x5hCRMABKyi3/NAkvYeO0ehfZth1fDdXIO1g1I7AscMic0uYInqOsti67tsI/LHQ7K46PMTyATWdymvhvz8l0+osHdu9S\
34360# BglTQ1wATXBLJlzf5BWSkskeSsGRbiSURpP8DKq/MSFmp4ySUBlptguZbQNWcFvK6spKDU1eaF1rgLvz4O2tVvKknsYkqQgnCSfF8H4hyimdZkQ3BVXJv\
34361# IUQxQxbVL+DQABsvj9A9FgBWifAtcDk4kVTU+iUqosNwJVjt1UFjfssFyKDAat5GVoid0WMdUXHCfkoIeTLHs98JLYBmfZ4Q5PXoOq2Mx45DBUht6ZBqV\
34362# RZZHDuuVi7oQKnwIANCKHELF5s42bCldo5eciaTGLVZjkk+Uh5XNQ9VYiR/l3gfF2Evsd05fDZYh02hLU/OCvFu1HbFYaFnmGWBhCB0Wznd0TKKUcw2nL\
34363# M/eYTlL8K6haqvi5A3WelF7eQIIUoy+vOkDYSVSVhBgVW3HJa8e9i/k5HcqpyEALLrGL03UQVUja8ubW2E1gQrJv7UG91Umc6mw6hT0yAIm9A4bosANSu\
34364# WfYEVMS84SWabP7w0X1qi/DVaLr8sOkt9ew+ozawo7t8gCF05liCy4cIK1mWCfIiOWlmNC59BhbQYLL5VV6z+wM3EKtz5cUWkxYjgzyVeLLGwOdSPd3kE\
34365# zwNqgdLlyyPkNrK+Uzldde3FQfd4eWl/GyHIjLH+CFTKG02BDWUs396RlsKAO61u8H1q2iAeruLHg4lTGpHUAkpyatn2jhNaHsVJdTEc+udCNtPJ5HkMI\
34366# AsC5nmMyQgsVDJYbYdXeUZaVOnzYLdddqClT9Tz8m374KT5TnvNo2FcDthPAmGMUDxIx3OiruO5xoF1pPc6s/ArL0pDHuF7JidrIbEeW9HFt4KEvSl2H5\
34367# aacpZ5VWMVXyL15REXg7wUtZ3VWHVZgb7A8lzAMKWFC12zVLjSqDTNYVeHdDsdD94Vd338aKQ/F91GbBlYrrjgHPZ7NlKPSUlgZjzzDciOskFuyao0p2n\
34368# 84wvLKqsGCihvk8/OisxCcTeXFogGs4GC1/ihXE9cvoVRhsT/puLI5e4WVF1D6hdIC17tRWI+LSAKrP4zB6qGFWu08rBuzubDD+noJCyk+D7g2pogT3ea\
34369# eF2nsuT8MkbPwaTnL9MGfDwGW6BhLN06X+RDDtgvqSS4ve8ydR1q+yWV7mBFWNx3FZ4ScXGg5C3QGWF8NFhpml+deTWbEFRYiTXhx9quwvxhB0d821LEm\
34370# 3RlUv632UzpF1nInSXNBknONDQNjNrSyb1nBmAtf2vDJzvkOK0Rnsn67Ynp7hQ9YUGEkrEWW2incOXLoUQ9ABsrpnaL3a4jv5CcnGirIs8GC3UZYZC3hw\
34371# hVWAKxngVHYyUpN/pnFymqZad21H6qtHxqdJN+If2cVEtbvdSJNHVx2jAx/acRVRH1vZFQpPvlV6bHXfVihcG26yox4+VnNwIdpm1jdg6WbELcIsNQoGL\
34372# 7pYuAmP8Lq9Sh9OfvQbxR12idYAbC8wbrq1GDd2AX9NyyoxNwAQQZL1wX1jGalRRe0PNsKXoXHabBCREGtxQWwQoWlf3yNLG+KaezU+TewMnG6AevHD4N\
34373# 1y4arSoq8GaSmzDFiO/PGvDE3XNgVTWmXDFZXjpbHqhxqIbJGpP2noTVLp07f+v7ChpY88NnJ/4SFOcNCibNrws4zTqjWJrSt0ZWdd0wU57tEijFNZZGG\
34374# EmplKZ6bUUxTp1NwecwqMZalN2CB1ltY9OWmQr/KF9W/UtnDdd/gi6A4jZuJmPqZUIhkKj08qMpqpakWCqzS0mluy6xOCqjCHQMd17D4Hiq7yy9JoI1vg\
34375# 7/GFYUGtvW5hgEpqWHkwbQWDj2b1mZnhd4pVDu8iP/l0xzM/BoWDpj+tHN2u23DMBSObfBXG32Tvf+r7hxaabu18RJsHXYxAi4tRTSlLyRttU3kc79oS4\
34376# pTf5GTcPxDc9in0xePTzi09V51n/WxLp8tu2Rm4WAU/DWBM7p8yuTrAevuY/xqv/N2quf5AHakvG2p5W3gp0vKU058nbDu7njiBH5FWIqbOrZRUGrlxJO\
34377# aqRF+jeEvEFw+xherypvWMuwnj6n56Jry0VLdUks8lTcBWBEBWPfkq5/Uu3WM8LFCe8YCHYal77HaWMYY9gorl6FCtf38mQoQz9cWjOrdmiwfXP2g6CO0\
34378# XBNohz/BqsBqwno+tBJs1LWwPgWVAClFKyq3wQBxVw1Xc2lYvWRtWKWK3KOqynSX7iEr3wZa2a/vVX1f9CUyq0yvr3ZXKlP3lN21ZuQFSLk7wri7Kt386\
34379# kajKp6zD5eH08B0kuaean5clM2L/Dqw7sP6cje0fA2HEswwdbFgTInY6rZg1kQj6YwwK8ICBgTOtkXagJQ4foKpDhWGRDhmGhimxYgtgaKHhhVbS/+Vyb\
34380# axvZpnQIM0JDuq9sseS3fFyhHrgBEuDG2JyHVkhGr/cWBE1cLLaBbVaVr6OmGhvj8fWgbnByzfcR6LeSXiDSx0GX4Dam7eaXhAWSwdhKCip3s7VXV+FnY\
34381# bhrw+1rGiN2+wQgHJdH1vDlOGYw+sozwMM3jjVYPnMIqFQ5U8d5gU2swJL0DTMcpWqsizW2GzOof1ZdV7YWkyYXF6sSwMBOFvh3va8832LCGsGIypI/yz\
34382# 5zv8ipcdPWAr0hfjV40wFXXV2qImg6QvrhnwfzIPrHZgRA9stdNT4YihbXSg1UxYHbBMjPNoWFHUNqzH32UV6yOw8GCadyIL11b47z1zsWYxhoq5cYO1O\
34383# xYyYcXwMQwTTR3s9aExRgHW621p375Vt9i7de+MLKxxM3GsPZXJTHOD2/nsf8BqtTOGZRCnXgBBJqx+5wb0hCVywMrWDMaTeq9kdVqyzp+1dDUhMRBYzY\
34384# HektEhDJMxXjdyI6phKcaFYXIsUAByjU5N3ybbvWGNyg0Bou9gXRrWBnOk4zbNScEKIh3GhwEjBE4OWOsLLKnlPiwxFMqTu649BIvyMSxG+gFLbXh2gSc\
34385# e1isbrEBEJ9kReKRAVxss3glLtzDPC2F9iwKrCWv/9g3H9jGstc3txVyUYZTSxjGKHnVRHDOylglr9jWXhqU/Rhbv5XW2h57lnVl4JncqvOgIG13PK47i\
34386# EVYXWUcx4q3LRJjh9IBly1BlZEV0Gm6DFdyPkm/hcsAivgDxn9Nw1qxCBNzM6YGTCG/UhfMIB5F2SljxAot91CyurGMc9zYNIWfPc6LY7JwG1vlTaSo9K\
34387# JdZYWywfO4WIFaBRpRoKxbIqMRRQTRcoBpUELTJVedUD2NlpqVYZLuhSejOgfnOPOkpfOY8vRqctNPDSIU+Y/QwtGkWyQ638MT5ruHK7hNakDTgWk8fHF\
34388# ROzOfnmRJwvAWnO9vo4EvHECi0S3ik144+x/gRWjas5jBYQcrl1pI+B0BvswtN5Y35gPkaKdUeWrKUlrMrcToNBSDQJ+CJEe4ihYHlKY6ghA5OhHXlTMR\
34389# jPdnrnG8d9sc3vHsfrWbjGsPiw80Jx/F4Z/1Wprkxd0926Tcd7ej+Fa3vhiw455Ia64dPDNyTfp4cGWE5574/bz5T9yFLDz+HYEe9eECk+h9aXzn1feqT\
34390# halb8huwnzGXXwyVdH9qySJZkPz/XfH/5b/88/IdcfPxH+J0LVIAAAAASUVORK5CYII="/></a></center>"}
34391#@gui : note = note{"<center>is proposed to you by</center>"}
34392#@gui : note = note("<center><a href="https://tschumperle.users.greyc.fr/"><img src="data:image/png;base64,iVBORw0KGgo\
34393# AAAANSUhEUgAAAEAAAABBCAMAAABW61JJAAADAFBMVEX27vz06viVWEaZZlbz5/Tv3urwxMeFXE2icmuBUT7w4u7QzeKaaFqRVUPs5vWKVEKBRyvr4vKR\
34394# WEfSj4d/aGWuaVjkqqaca1+ATjnAfHCVWkh7UD/z6Pbv1d6DVUSGUT2TY1ONWUji1ubv0NiHeoaLd3zEgHW8d2qQYFCOX0+LUDri2uvcnpeQen+md3GKY\
34395# laYYlGXXk3p3u3Z0uTFyN7nr62Aa2uoaFehYE93Tz/tvb7su7yYf4XIhHqgb2WRZVieY1KKXk/u1+Lvys/uwcPPi4KkZ1eiZladXk2ZW0nn2unXxNHpwM\
34396# XqtrXntLPfr7DhpqKtk5yHdXuEcXSoc2uSbWeeZlaIWUjj3u/uzdPTvsrdu8LHrrmjipKQfYZ2ZWalbF9zYF+TaV6gaVurZ1aCYFakYlF9WEyJW0uDWUm\
34397# PUz5yTDyETDTd1efSyNrRt8HOtsG8pK7go52IgpTMlJCbhY+rbF2QXEt7U0RiSTxXQjJ1PiLv5/bx4/Hw2+Xfztvmxs6WpMLlvMHNsbvCp7CalqqkjpuR\
34398# h5iefX+iene3eW23c2VhXGWpbmKLZl2wa1unZFN0U0hrT0U/OzlRPC5aPCsrKyrKzOHCw9nnzdffx9K3u9KvudKnsszAtca+r76tqL2UnLfcqqp6iqiGj\
34399# KWxmKK5mZ7VnZmbipbZmpLUlo68jY1rdY3JioC7f3eveXGfdG6wdmxpY2yIbGqxc2ZRTVNkUk9LQTt6STGFSjBHOS8qJR4gHhkWFxLd2ey5wNjGvM2Jmr\
34400# mvobGCka+mnK6Pk6zZpKNpgaJ+g5mtjJB4eo6UgYu4hYHBh3+ZeHl2anBsV1RwSDM9MytmPig7IxPSz+ShsM3lubvetbl4k7TOq7LFoqnSpqd1hKLHnZ9\
34401# paXmVcm6FaWRMU2F7XVZDRUpeRTZTMBxgMReaqsezrcC8n6mklaKfkaC/l5pbcY+nhoqvhYNMY4Cnfnx3b3xbY3eOc3W1cGFcWGBiVVZbTEliS0O4qbjS\
34402# oKBQaYhBSFRDMiYCeYKqAAAKU0lEQVRYw42WBVhaaxjH4XDOoUREUhBEBQRFBLu7u2N2TqdzttO56czN3Kauu7u77rq7u7fb3X2/g9db3OeBv/Dg83D+P\
34403# /7v+d7vPR8OhmE8Jhx4GSLsSuytFYyHceD9l/+fiPFrwD9/0/FjiHEM3tiYMXaZ9vt/+rVULNxfoHE39oH5MTcex8DhYB03EDCmL+z6+NTPVStWLj+xPA\
34404# oGtH+GGxNAjPPB57/lOPNS/74D+18m7d7d359/3btTCAj/QowVB0LouLGvnbd+8/2Bw7cOHz584Kf9+3+4keSdMAXRvQxE0Amvzb/ss1f7h5tv3Rr5/ff\
34405# Dzc0/zct/kZ2QMAHWJWgpugu17Mt9Tc0HRppHm0dGR9++bX7blP8y4aMOoc5CG4M/XRkv+/LNyEhzU/lUUVrARKKDQ/GNn35smnVxymKK7sX/V8Hdz74f\
34406# fjdKLi1tJBBS85KTe32uSH840OSQKTTXTWv8P7yH3+w/cFBOAApzcxvY2ysLYSqz9428zqQY1qrOX/7wI9FI6w8NbRjYa2MWwuTEf/5jQSZiEIBx/rf9x\
34407# SQMkOof6ubG7sMANS5xl1OqFxkEeO/X/te+JMyv9ncLZbP7zMyYTE6MV/vG9Y8M2m4Pn+c7+HIJav9UcAfC3NhsGzOmkhODCk90P55hgP/4+eusAl9QQV\
34408# hqqFtDQ2gD20bG5Lh4oojd2exoAxIseOq6hwQAqeAWlLiFjiXgeAajCLKkPhM24BY83UMikQYVRAGZrvEPCy3SAoJRAKD2PKLoByzbmkIicec2jb7//m0\
34409# yFD6nKLShoXfbhlg0EGntemwA4IPnJBWpeG75zZu3D5LpUAm7kJDWVB6wY3MQJfJUpwGAmbt3qmbtKSb67fDzOGgLsWXbjSx5Nz3SemMojmc+MgBwMufa\
34410# i8+/HSwOMJo41YOn+S7u2UTBQQuaenvcDMrZ9QYA1vTv/Mo75fU8oHKBwK8vfhaoR6D29fklg7JqPWxACf2k2df2TJw7r7z8puBguE2NN2tu01uLbXVxb\
34411# cKVj/QCjFf2sJJJvgUOxLKpHqIyD3ez2E7XufNul+/d4rIoavlivN4usJ+VsgPsJEVpY5qInkYrkcU8uvhyIj2sZvXqzJ+r2/QCloEm2EEqJKghuoUF3R\
34412# SaI4uPjd+yk7tXpryfuXij/k78QOWbDABhBD5EmwRB4SV9yuDVzO1m25gcNIOS5WmtL8LSr/J2qUh5oe4REA2CNCVFQ/FI9BYwEWqCUSEyIUGmD7Bw1sC\
34413# u2aS8VPdwQACvkqJN94VbGmxC4tEg6xkZCdtgfXvRPg8DhLlH8EEJ797R2HmjVwfY3/nUoNbW1tHrz+lLcFySl0wiFWIl2NraVlTQbOp2MZPDi8yUwRnW\
34414# 1pkbMvQ++2eqVMmkwtQICAIEMrlizjaO57PC8O+YMWhG0ON4/Z1ot+KiimTkD4EloNF5PAtan9Kz/aI/2ycWRdEn7doK9N7HnVwN1gOgFeg0/lBI7P3Hu\
34415# 9x8OGhQUHQgzgDdrVUZhdPJPBML3qFDkyLYPkrlFzsIZvEAYA0bkoCxlsRVQ4BQMc3E5BDE3hx/pUAcZlaDZgSO+/VM99OkQjUEKjA1nUQDvczxav80ZQ\
34416# CUkAGPGVsWdnbM+BuhC5v5woig4YM+VJta0N3BAnrGbfFhuqBjBeDxH9SqZLGANX5i021newJB3Qjx1RpL8sEKdkgs+uDJBiUHHdsHeOcLKYShDfBfBz1\
34417# dQPoF8FhpbOTz+TSybbgN08WrPY7DcUG1sc0ZJ7+Va4qYQlhrNsebm//PTQAPRjkxjUa3oEUMhShdUM/ouDgvdLr2YHX3aQqB7w4AY348rAvArykAE4Uo\
34418# IpN5FlCJjdIlGkWjY70QGPPjl1wqEPPdNyEw5ofxxuYtuhuq1k8sJhInepDJZAtoKORce3Bb8APwk1rAUkmBBhriAABsDsPGeGNYdxXsxfIAAJgqwAjuv\
34419# XWrH7Q/CMJjUUHiBbVG/PC+GCwB3MLAw/8F4BlLan3VmlIsgQfIwKMXXa17sjpa+OdpFLa7QIhwt8EA5uCkyzBnMIBpvL9aIiuXbH3lQNBAjUTR1LkeHg\
34420# IyT6BW1f0SgyIUBA8DAmy3JiVijpknBYbh6S3TGY7gsA1WogWhWFm1RmV19fT8Np8M8fmNpSKBQHCTTBaQ1bvqXLxiAx0roygYIP00FyrZ5oVMh7VuR0d\
34421# HnFNr1ASgqhWf5OS4SoeP3qZBfFNLOkYANYgK6+I80csJFKvjkaAKSvoabvicTVgiBoNh55iezsBNAerI6ly3MTExKTf36LE7h2iQKd80TUTmlfMEctBI\
34422# KPrxDSF1uqMdSEC9IIaKNnkhiB0jMjLdzsrODldVVZXVvTGhW+LqmpSbf+TYHZMKGoSFEIl4ZNO9Sk8UDTrzZrWTFeVuZIvxaVeyRYQsVohgXiuryZOtc\
34423# Fkd1R9vrM++LGW5uubemH/s2DSTCjCQTC1NIRotXAYGOprRtefqs+qsrI4JCy8Npvn3bj7XBqxO4AWE6zy1rt47J8dbwmK5sjDA+yYmk0ACrcAo8vIK/v\
34424# QayTc5WUXaqbpy5eoGl/uZ0dZWVKoV1cmJSnXCJdRftgfyTsQA+fNvHQUAWzpkSoNABnaIC4cDxqQvF6iggEtKlm2KRgIXzYCXfeh4nIoJJ5FIpVJ7b3v\
34425# pdUDInX/k6DsT8FyEsIliCRUx489tAac2LtihRlyuEbeQa8MxpyAIbunZVvMxQE99vSRJai+1T8rPZwHAkTsmh2xtaYBgSYfmMDfXzeYSjLTy8xODUeHu\
34426# EwgjM6Y7nZxCXagFdHd3f5KTKJV6S3YXu+4ePnLrzjQTW1ADEI3G9pEpr2lP3sCuKA5QyBXQ3hgKskgYebJj5konZ2olrn7FihVnahMT7bt357KuDx89C\
34427# gAmFSCCRgNBYQ1mLi8xO8FPEUAEe0xhaem/yTqwdfHi89UPOxnOzlSc9/qoyhMf1kq9O7bmsr59M3/fkWKyCbYQ7AECBJWEfDWSJldYitJ4PB65jEgUiS\
34428# CzGCRwwpTn1acDcZOdnXE52YuWUyvPSj+PWvM1K2ff4a3De1hTsYluI/NtpLtvfk0QK0rp4LwzelsgArtcpOj1QiitWd+cWgkz0gFgbXbb2irqPUm29Um\
34429# Jq/T7fScubEzCCJPMNs8qtXD3zzMCBWAjoqwMBAgQc7c/80QolMpLH0Y5Uo2PO+POZEedX9e6tKfe+t7axKTrw9Vbq2pZL/wqbIdqvnCg204jgFvvJ5dj\
34430# Q6ZULh64uiGuLRBBKJEnVlktWMrAOePWfVL19LJwydps6/fWSpKS3kz49Yvll1gklT+bOTuAZzJNQxAPOsjFAQqxQk4gpPadC0LwAECx6oq8t9IRPxnn3\
34431# fPRWUnWkq51Ue+tkiQmvqr+LP/TrueslO2y7XLgt9wpHhx0cCBiUmBrwAxGhEIEyGrBw+WRduk4+5yEVZL1lavOLHZeJZEmvlp3vn/2qa9zWbOvXCsDgC\
34432# dfOwz6OWgJZZallnS+j+eitqBABHTjvTWVTlTGH/5VIUZ4CClbAAAAAElFTkSuQmCC" />   David Tschumperlé</a>     and    
34433#@gui : <a href="https://foureys.users.greyc.fr/"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAM\
34434# AAACdt4HsAAAC/VBMVEW3xMQAAADUqJoDAwEICALFmozFqIwGBQG1jYA7O0EfICUdHiMPDAoODASth3svLzYFBQQoKS8LCQM1NTwaGx0SDg0bHSEHBgYJ\
34435# DQTRpZfCmIpuVk4XFxh5XlU3N0C+lIemgnaif3OPcGZ2XFM/P0clJSoXHAoUFQfAloi4kINrVE0QFAgJCAece3BZRj9WQz03LCYODw8UFRjNo5WXdmuEa\
34436# F6AY1plTkYpKysuJSEYGBwlHhsYFRELEAWotbWxin2ffXEyMzlPPjhMOzVHODMkJi4yJyILCQi0wcGTnp6CZlxPVVVDQ1VxWE9oUUopKzQsLTJDNC8iIy\
34437# UrIh9nbm6LbWRZWGOGaWBJU15eS0IxMT0gJCkgGhYSEhUUEg8XFguyv7+Ejo7Ep4u7koWphHibeW6ZeG2VdGqJa2JzWlJERE00O0NSQjouMzkhGxkfJgo\
34438# cFgkQEARBQUk3P0diU0ZBREQxNj4uLzoqMDZAMyoyKSY1PyEQERMcIQ8LCwwiGwgYEgYSDwSturqfqqrKoJOvlX2UfmpXW1tJSVFHR1BIT087O0hhTEVZ\
34439# S0UiHxsoIA8nMA6wvLyMlpZ+iIh9YVhPT1hFT1h7aVc6QUlHR0c3OEVdRz86PDwxMzMpJyQnIR4qLxoeGBMcHAocIwjHnZDCpYpubXpvdnZvcnJkZG9XV\
34440# myTc2leXWhWVWBOTk5LS0srKzA/Ni47LyovNRofIBQkKxEQGwq8oIVcYmJcXl5RUVtWV1dMS1VHPzgrKRSXoqJhY3iljHVbWnGbhG5naWVQW2WIc2BNTW\
34441# BTUl0+P0xFSkk4OTZ6hIR4gYFze3ungHFQUGVjYmGDb1xoXlVdWVA+SFBAQFA9QEE4MjIuLy80Lyo5LRmFhJG6k4d1dIC0mX9vbm5UU2eNd2NJSVtGSi0\
34442# 4RBYyPRQ6LQ+lsbGapaWAf4tsb4Z5eIVYY299aWlDSlN0Y1JvX09RSEBMQz5CPz4/SxinrKyVmZmNjIufeWF2Z12ObFYuJgeXgXY+PimhEuIiAAAMWklE\
34443# QVRYw7yTX2hSURzHPZ57UeIqcvE2/IdOiZz5Z+OqUxRnc/iXDUrHGJhOlpuDMIN8mA+p0xJGU2a9jDlXIRgEjUYPG6PtZU+tFVHQ/hD0EKxeeqgoiIqu+\
34444# 1Mrrce+cO49f+75nO/5/X6X9h808O/lMcvfVjKumjIZT3HsX4C3RRrN2eDYR7enNrjE1tbHucTG4rXJ0F995KouZ7XOxcraxw/laDlh8psD0Y1o28yZ23\
34445# 8DHPGUxkJ/enT52ogNP4SEFygJwDFjDliyvMk0BAjX45bxT79fovgxYOZgsw6oNI4qIXaTg2EQlnIrDQEGD3fhczF0eOrN3GmHIOJ1MxX6qLHrqk5hw0x\
34446# +AGYsjQHjuaXqo98cWB4qFWQky6PzeJJmhUqalbrJl60AxC2NAa9y48Xf9zsUZ3t5Ffq+Xko7SOrZa1YWnZb6hHnGF0vVMdehMPowQUXWjUoPALzs3ksV\
34447# zQ08ri+J0NTdBd9YaPUnemWzhxSPnqWTJP0PWVcbVdTjtuq4J+OJHxTKpIGI8BQROr1Zsntuyy9Atmu1AeDFyczk5NuFjPMAIGwdtSp0tQjubrpDQX5aW\
34448# GtQCty7S5MZn8+zvnIAGNX0yw8Zp8K3F4MKKdM8qg/i2sy19Zm7S4uPB/YBs9PRTt5hAG8vGJUIaRVw6zP5HBoMU9y5tYMYWPJGwka2qLL0OvE7JVt/An\
34449# y055e4r0qbc679n9DlafLrjFKyIiLrAD32ygMK4Mw497+lkl+irT4vx59Oeaq1SeeAL795U0u6+3gkX7Vr//BltApFIrRwO3Qst1CdHHAVPZeowqCVVg2\
34450# LL3IzPgpgcVXj5bI3q4tIVa1aZm1PM/uwhYr6jA84hADi5pnNo7OngXDFQ1szTJWE3IcLb9/QckueYlnozarYIjMw6/v6OujkvgPenb5aZkYuxQN5IlbY\
34451# 3i4AvMAAwOygGcvxMnZm3VdcsVwz5F5wgDc7JOoSCLqvtzq8CrK3km05Zzun9tpPySlAk8GfD2zfYuDpGJ5kJHGHktYUJHAEaVMKuaeFTW2Y0pEYmieIU\
34452# 10Jqa5HOMJDeb3XE8b3l7/NG9UUgPPgCwZYOzvpFJLGWTuxJItmBgDHY0kGUH6/ceOGn0jkmzp7ulVqAnb15LF+Se+dL1cmavpgktBHsGf3TIBRSMfArW\
34453# QhCWMpQLOD4wAGWRAoX7++HyBknd3XO1qmNRrO1jNp74h/3mS6N/EuqmwavWgy2bTGAHSkYoVYGsdBOgVvAkCj2p5uvl7ehq3t04RdEtaEZecmnmkiNuZ\
34454# gpJ1te/dudjpiPyuax6IExmDFGAA5j4DkDmRRAFATQvXMy4UglMkEmLq7Y1DhZrL1IzKvGB02Dl2fmNA163SivmbbVZGdEWQdh6kLt1iprxCnABACKh0Q\
34455# AYXtWAz49e6AWn1V6pZq+eI+tUksVykEeiYTbRmShZvl7m6RLQU5QQAhA0my0pACpBCIBzngAtgV8eRJoF9uVQgEonBYpjGKmV2Doqtn+WK2VNRuZfcLh\
34456# qNgXwiCfK1dgWrgxv0TjAPAtEOAarUoKrcyVWa7uOWcTDrUYu206SUSvs6mkAEIaoLwAkgjNQdBanSLFYMIYw9wyjSI8vkoynSjEuMwija3C6xsvlgsR9\
34457# lhk1qur3llMBgsiOMQT1OAPLXt/vJyAUGOU732jlOz4kE+WhM/3MMcVqHtw2zm7jgik/Uz7YASC+5eASJpqpB+sFQ2IUqEYRyfl/eVmUCHYSt8UQcdrRl\
34458# nRx0NP1piUFxxwVAwMxEqCb8SpMGFDrUNdSk6SAjZIaIvpSA2otbVW12KDtWlCIqIiKg9dIqgQ3TpHet3GIYXnv888/z/zwyacWCzVjtrQ9BKzwVkVZ4X\
34459# MJl00O22MGrdMof1tDKCFdA2MAfSEk0UKKQhSYREdcFKhO2F4tFK/FrU7Y67k8XlYpzpX+P9Fj/pJzbstDZaVrKKCIDeAqDRuYWRDVCTrW+DgaTBmlSDg\
34460# KzYzrzP39m3L1zOlxL5kvVEqcwKbJ6xMMEU0+onreTRBJpkyNbrIShRmqSL+tYWtiFAqEn2ht/h57vHPdalnSuZdNpZDW2UU4V4pxLzt6os6YDkEAEagg\
34461# Uk2UCPAhCLhoIh+I+d9fvqFn89tJLrqDmejzqj+bjcXWowwXTscBb2SCmBXMjUEXFBGejKE2KANUCOFmjI5YjhQsoZVePxYW69HE9Z8jud6rrF4k62AxD\
34462# UaKDR/6YooXM0oNBYMaSRSHMcGQ+EHLgZDVa8+X5TCPed/VKp0FrMqnJIIInwdlEPmOGh//tAxggoHSARYGIE0nVNH+NZoOBsMJmkW24W98VzsWFiI1Rg\
34463# TIE6exhIpl+kXsEaNCXMV9BmmjLDioY1DWJzkmHWwWQc3mjQE1oOrKSvNRx1byxJglDeCfB870ww/ndHYaRhPJvNtKORCJ4ZSFRE35BJy6lKxuNgvd6kz\
34464# DgYdZElM8gAhMF/FwDqQWhDCFEAcBo5VeDJy69cylQUp8al4w5vnlUTxfbGkA8XCkIydpptNBJ28A+MTZ3BVP9FIkTdIkwVZQIjr465ADD1Nh+32KjcZs\
34465# LZ5cCyM7GeYCu8pameDq9IY4I+GG8NpoNfv6a6CEVqjfB1bbImGq4XR0j9Gdeex4+dAsmCp+KRfesCry42u4ybf37h/JufNzYREgfit2+SJo7GYm+MKQN\
34466# CaEynhkE+ChC+2//g84Mdp085vUmhWiqwQaERdft9xTr/5cfShQ+rn+6u7lrdZud0BU0MGkqiQhlmCxOMUGTTRf57By+/DB3OJoqynBTasQRbDfssjlCx\
34467# 8+VOeOnp+107rnzfdv/jvUNXIpGjoydPJkQAcByHJ2uTva+fXd2z48Dlt7//3M5mni+m2zmmwjc8cpPxpLtf7twJP6puX3149+HFQ9/v7vr+8eO967vRa\
34468# C7wt6ayCW0aDsN4RCzm/xfTP4kpaA0eEhDxq0Fwxh2MGi8GEouWmFjBXoTUeGhr20npraVd1Yu0q1URD461tm47FWuFtqNrK6hzuokgzrGjil/gB17MBH\
34469# /393ne530Or8uVvPHg0cQlsRi31HNXn/8Z3nS6PnN0+v7mzee3bVy36/zhx7MBR/3xi4M1b7nJ8SaLr0KW3+WzZ+3XduP9i0cTLx5xghGlw+jck9/7nWv\
34470# uphztO8Ondq3fuP3QvgPDtn878msikc28/fguDzPVqm+8AbkpUP2IY7ceTg6h1wzo1xi6q8UvPdty89iZg18DgZnIhU0njl05Ox2Zvee4F7n9EOpUtWaa\
34471# RSZcZHNsbnm5NRhkxrHjbpgh+zhrIXf8U7AIT04c2ToyMhZwBJaW0mPz6XTKYZN6+fMNIng2iVpdujColJTeoqCaZCOEsURTzTA0Ho2rYAHQweT1oZGRv\
34472# XvvOQLz7bELewKOf/PTvyY5hrGPqHygmVxFknOyXFSrPuYjNi4KUBV8FKHQyHDdjtOj1717926YrQcCS/PtmVWBQGp68pIR7FUk2I9+HsC5FkBRXQZ4iD\
34473# HLWI0RcC8xjnhe6vqHRqkSGL3+fWRDZEfd8Z/Uq8kbKEfKHbgTfYrmvKUWSBpSh1Y1/F0ZIxmzLzTIMmuAIE0kaDgHnpz7dvd0atV76XSkbuf/8cA1V4J\
34474# zDFqrFTslmJPXUD2dgTWg9msYp2bjkunJQpcnGPYwxJSuJ0afnrw2vbpBfWa2PT8TGeL1SrArkoSzEPsAPSvyWkCQeeQjCZLF8q6MXQNu22tEMpGFMQU0\
34475# p+DDB7vbY6tEvqYj1NTKSicXRAS91hYQhF6MJgS/iji7liZGSXiIoqNRCLUoT3hdJGMgnFtz8UL6y90NZ76mD1/DG6GerNe8Ccbr0oTlxVIlyQJfk8jbv\
34476# fqqmCto1TjNWIiDoEYRIuFnOUjGPUn/cGQs/eXOxZ1ixvtZIX0myWQF65PSEphlvMHhoawPiawdwSTDMLxgRAsgXOAJXZY4WpQVxIeAy3V5WJEInGM/A1\
34477# +ZdJMiwAuxFX+PMxEtIicNRESSGBJBtBi3V0gYQYHhdcMUqVJUkaBVXLCCMUlnGI4uNTiax1XCFlAkmcapAZ53rnGzXreFAcPSDAXGw0OEpWYggfwxmZc\
34478# lq4AoIRyWlQrQKyLbcIfjRYvktDBoDZrqYg6nPHzflzU5DAF/AqeUboxPJCQ9V4LOWMW/KFmwKcbU7KKkzHWE/DjhmSpoCcLZ1fQO7uMId1VI+rM4zXr/\
34479# AnoH9FcRhS6kAAAAAElFTkSuQmCC" />   Sébastien Fourey</a></center>")
34480#@gui : url = link{"( IMAGE Team / GREYC Laboratory - CNRS UMR 6072 )","https://www.greyc.fr/?page_id=443&lang=en"}
34481#@gui : note = note{"\nIf you appreciate what we do on <b>G'MIC</b> and want to help us maintaining and \
34482# developing this piece of software, please consider <a href="https://libreart.info/en/projects/gmic"> \
34483# making a donation</a>!\n\n"}
34484#@gui : note = note{<center><a href="https://libreart.info/en/projects/gmic"><img src="data:image/png;base64,iVBORw0K\
34485# GgoAAAANSUhEUgAAALAAAAAwCAMAAACG2QC0AAAC+lBMVEV7e3uQkJAAAAAMDAyIiIhdXl68vr0rKytmZmZsbGy7vb0mJiZxcXF+fn6NjY17e3u1tbV0d\
34486# HRzc3OEhISbm5u8vLy2t7Z4eHh6enp3d3eVlZWUlJSdnZ1vb2+4uLiamprOzs5XV1ezs7NpaWmHh4etra1eXl6Dg4NtbW2np6eDg4O9vb2YmJgrgnFwz7\
34487# xqzbn6/fxlZWVsbGwvNjRtzrtuzrt00b5iy7Z0y7pky7b3/Ptxz7z2/Pr5/PtbyLI9ppFnzLd2zLtyy7lozLhz0L3z+/ny+vhmy7dvb2++6eBszbrB6uH\
34488# a8+3W8ettzrr1+/nd8+/S8Orm9vN20b9SUlLo9/St49gqfGwuWVHj9fG5596G1sZcXFwyMjLw+fdopprk9fLP7+htxrRBkoLu+ffs+PbN7ufG7ORajIIs\
34489# g3Lf9PC76N9+1MNpp5teo5VboZPU8Ouv5Nmr4te12tJ40r9ku6pmsaNZp5hNmInq9/W25tyZ3c+B1cR+tak4jn0thXRdXV1QUFDR7+mz5dup4tam4dWj4\
34490# NSQ2suN2cp60sGZx79frJxlppljpZjY8ezK7OW649qb3dCU281808KFyrxqvq1NnY09k4Ivi3lOTk7E6+PI5+G43tag3tKk1cqK2MhPsp4+kIA4jHwzjH\
34491# vE5d2t2tGp186MxbpWq5pWoZJAmIdFlYYyk383inpWV1fQ6eOx5dqo3dKd3tGw1s+oz8eYy8F20b5pzblvyLaDwbWDv7N5vLCIurBwr6Nrr6FFrplEqJR\
34492# OopJSno81iXkrg3JaY2FDSEfV8eve7+ud2MyizcWSzcBtyLVfyrRww7NzvK50tahZtaJksaJol45EnItdj4VUioAziHdbdXArfW0qeGlTbmlkaGdiYmJg\
34493# YGAmYVU3W1RQVFMoXFJIU1E+Pj7T6+aY1MiOz8KG0MGOzMCNy75owrB7s6hknZFBo5BEoY9DoY5UnI41mYVjgnxfgns9cWcnZ1o+X1k1TUg5QD4sPjsfP\
34494# zgzMzMkKilq0aUXAAAALXRSTlMdvgAO0AmQAv33kAjy38w1MBPu1rJ4bibm5MS8uKqJdV9ZEv20nJORfGVUPh6rD+f7AAAHWUlEQVRo3mJgggJuhsEMBJ\
34495# lgAOpgAQ1ZacZBDFTUtJSQHCwoKsyjbDiYAQenpIg2O9TB7PwinIZHvzg6EQcsqAYciQcXjhpyCIlxgx3MLi7C0XBbj6bA2gUFmJmZuegBSbwgGAJg3Lc\
34496# 37vBocoMczCzHGXP7DB7baOR8M+JAMMzhwZfvCPODHMzP23BjqqkevYE1DBDrcrNtRznVuZkYuEU5Pk81oaODMZ2OHyC8FW/Ix8/EwCxz77KeqaneIAfW\
34497# ZmbWO2IkFIEO5mu4agIFpkAwONxuHVxSYoae7s+E8IixMzCzwB2McPWAu7yjwC00OwdFyMVlR6MHFyvIwRleXiYowAsIQE4fOLcXuhkYuEVtQxVMavRgA\
34498# zkYQE0Z7KYNBAH03o+BOdSsdu1dIRnbMiYlsQQSdoUaiYQq+BAQl9jcIAQujYBTK0X8QpRD8wk99dqf6E901osDjhWlx+QdGHZmdubZB3/+Wi7XFZkwJg\
34499# 788+6fUrQir/V8zLKvs+wzAH4UHqTywkUy4SLF1v/sqReyL+7YHgPSudlnngm/NZIzJXzwxG9aeHLK0Jd/C9+J8PLWAMSK6+9D+FPQBIT7ia7rWlEYP2v\
34500# VqimT1Wo1Depkqqj+qFDOyvtkvlmNUKFQxWC+NO7w1qoLEt5ygyC4Xy0xbSrh79kbptSRUIyV1TQYDIJ2UteuXW+kOc545LrXzs+Z67qe58YP2LYceh4e\
34501# B9OEIk71po134vlGjlh7niwG92vN0YZ4R4JhrebX5LSy4yzbrnuHO6P5dOC68Wyx2++YSYeDMmaSq7iSpvPCKYSgiteyGGeG7df0E4AjndLJEcA5jU7Uj\
34502# OZZQGl4rA6WP6LIrGMbDCunCxwxVcsMqzMv6x14YkhTxjjtpJyOYD8oXZz2mgynds/VfhrFHQaHcH9NkbwwQeQFonsWR12DcauSEyZSOC1Ad05wm+zinF\
34503# +tCJ315B2GP40wFeZpH7+olC4M7Ep7jTYuQJQwUcIkvDTSIuPHqkxWNoc89pwgO+EPqfAfkrG8Qsmksr32LvWaFC4Rshc+N0OvB6wvhQ3vcdG3oRmQ7Rf\
34504# O/XiR9A0s1QgKt8JS8gs3jQkyaYE1JIpM+JEQJXzXBeZtxzftfp8o7gx4Ru9hJ6zthUVGpYdai6gkkLoULguxwRW3YiyFhYgawG7FBIVdIUIfjIEY2WCP\
34505# sP93A8DfiHsUjsjGBTjTBRKhcFs8oUthInAEqooHG1gcbh739dVzYd6YCDndNM2/O+F/1NNLaxNRFMDxT3TgrBQu9zKruSJNmRicagpOIpO0JDKpbakmC\
34506# xOIrQXTxKBpUrTGJrHvqpRasK19F+wDrJZCv4BvxBe4905m2kqTxm39LwYuzJzzW8x9+lva720QkUYcZ+/eG5AG4gI8IEm9VxEfSB1xwV48/VBFmpZeO8\
34507# Rz8UyXD+UhaZCyYK8k6mbIe6VWRF/rrSY/kssDkuiFC+W8dLjhqgAvSuYIkpYyfmSyq761+5RklzkKJlfEGNHS0tKPd9Y/fP4i7Bf9oKGIcef0qm6CdYC\
34508# EWPEYrl9GdD9vdlHkW3BNbFsfuqMSps7Ac8Ke9YFohCINBRrQiqbm9kDU4UX5NhwUNsFPoDyiH5bTShnlq38DVjcqwI3fwO7CpQrwWm7YwZlJ9mcqwFbK\
34509# lXFzmxUfzJXBE5Vg7+wq/BMcCw+6y2SW2jkGTIcmjgeL9vTsZDwiJtzXBdFpglOHYCK7G3Rzm3UIPowCbFKmzgFArI2h1mKC1fl+RD45VhUs7oJDgBecJ\
34510# higqCcaUxoybesYsH94rQY4HJzRC6XSggvxTqGOYWQ0Gr0hDuky+AHYCTBtB7thH1OmCsXouIrMuWKCXS3gYOifj1UBj99nqIaj0VkVySC8PNdTiJaK0x\
34511# ry6WPAkVmoBVZk59nuu00c6UZfRkZ0tHU6CSozNcAjzwjyeNdNNyLP52ywuE0YTFQBlz6KqfVtDUGC2hy8pNqr1ke3UgrzblcHM/d2TbCMVkrTd8jWKfu\
34512# HQA0wzDuINZv3Z8EGP9mUkTT/qgTDaLNNUupAgNGKT05UB9P0bi1wrsvpkxnhwXwiBn3Xp7wyIdzbsAO1wLDQrHJCNedQFvbBsHKOoPypEiymtrvMqe7O\
34513# BMBIo1ujjPocw8tQHcw/Qy1wbKwl2WN4ki25NRDpoR6Ppyc0FgMohgxDOKwKIcPzEw4KZMV74iMdQEgNI1kEWB31GMaoOOeShicMfzduTw0I/W4o6TH39\
34514# YHdV/nIneutCj45LXfGIwwPe1864WCIxSY2Hq9zhRJCqBzMwEkHl8t+yU+1i/Idgf8D/GdIYIg7mGvUwbQE4X/ADuZb90B3aICbT4SAw63cqve+39IdEu\
34515# BbCC9wQJtdgfPYfd0hAf5ySIsDJ2XEpRoeDIkgfvSQR54d6GBuBYl1P27qDnrw6BiHjA54JlRQjifm8W/dwQ3u/3p4V1gAOtcsKMrL0fD4p/EgBsfW3eW\
34516# UEmCHz+YLyPIKcRoNYsAjyScPmc4HAHFPJOZJDzOgAAAAAElFTkSuQmCC"/></a>}
34517#@gui : url = link("Go to the donation page","https://libreart.info/en/projects/gmic")
34518#@gui : sep = separator()
34519#@gui : note = note{"\n<b>G'MIC</b> officially collaborates with <b>LILA</b> (<i>"Libre comme l'Art"</i>),
34520#@gui : a French non-profit organization, which promotes Arts and Artists as well as access to technics and
34521#@gui : knowledge for everyone.\n
34522#@gui : <span color="#EE5500"><b>LILA</b> collects donations to help developing <b>G'MIC</b>."}
34523#@gui : note = note{"<a href="https://libreart.info/en/projects/gmic"><img src="data:image/png;base64,iVBORw0KGgoAAAA\
34524# NSUhEUgAAAJYAAAAyCAMAAACJUtIoAAABd1BMVEX///8iHCSKb5H+/f7KyMrz8vTw7fGfiaWag6DNwdA5NDu8rMDm4OiOdJWgiqb8/P2hi6fv7+93c3i7\
34525# ubx/fIAwKzL49/j18/U4Mzr7+/v39ffJvc36+fq0oriUfJvs6O0oIyrb2tzVy9hBPEPy7/Po4+mCf4Pp5evZ0NvHusrDtcbBs8WvnLPu6u/c1d7Ft8iRe\
34526# JiPdpbj4uOwnrWrmLCmkauempuCfX1hW1s6MzPOw9G5qb23pru1pLpybW1DPT3l3+fi2+Tf2OHe1uDAsMO2pbuyoLaXlJTt7O3TydbQxdO3tbeppqichq\
34527# JVUFZKREQsJi7v7PDa0t3DwcKjjqmIhIc0LzYkHiby8fLT0dO/vb6olK6WfZxaVVhMR0vYz9vGxca7q7+yr7KtmrI+OD8nISnr5uywnbSsqqyal5uYlZe\
34528# McpONiYx0b3LY19iqlq+koqWhnqF1cXZpZGhjX2JdWF5RTE4yKysuJycmHh7Pzc6QjZHw7PF1dNCuAAAF40lEQVRYw+2Z+VfaQBDHh26JWKupWpPmABRR\
34529# LgFRaAuoBUSqKNb7vm21rdr7bv/4zmysUgM+fMY+fuj3h52Z3XnJJ7uzZKPwX1fUvuMq8sE/UsOtq+guGJL9jFspFYyQVYrBbZEcfSI6xx0QI8FGFa06y\
34530# kM2OktGbkS5yckBSS1GcyJ3IgzUXBlW870atXGOFXCEuW3SMvyuWj4TEQkvrmWH8oww8ums34uOe9C4f8sYdUtzocIcdfc4ee96KnuoAaX1e8DtLMNqYV\
34531# CbGs+w9HVPiDvOoshtKGrYOby3UqJJsjco2J5jFVMJmTv+IDcGVjFEwCqlxQ9918XyR/WESo7gXM+RldOJbUYpAQAWpwrMHLJyLGVfTmlmrNAENgsCTwv\
34532# v370eFus73C35DddX0rkVWqh2ttM4Wy00LbMLYjnWXceuc0ExYTVGceoxn9KYp3ARS1d5IEmMKZIq0RVFSVUVHGK6hFLKsYR1rzdSYDQngrtBorsKwkKY\
34533# LpDY9gacvLb2Na9PPsPKe7ze/bsmLGFhVGjKMyNN7LuI9X6TB8cjTF2IHbnWMwATrqPmUkCEWYcLFSjH6sEyVxp1wKSxoaxRU2NzjK9mdCylGI8YcDYJ1\
34534# DNBkCmEz/L19hk70+vhRgiNTYhwmvbLcwHr6TMePHrMZJfW40+47KA5RiOh5iALxzS/339whvVvZMaK4BM1Z0HDrcR2HWI41kND9YCVa/YRFpK46gbLGU\
34535# 270iJhiXLiLS7icCgU1MuxJnt7e3+qYNJAL+rNabCH/jFcqpeY8p7ViHUvnojFZdBi6wnXhg/CsUQ83ieVY9232WwPWsGk2zZU+2nQhn7HpVQrM5jSOVD\
34536# 7ItodedBcwaZmP4BpES3DemUjLbKascAZp0Vk6ZJ0c1hqN8daWqkZS25I85L3bgTYjWFN2gy9uwTr68jISNs4xxoMph0uYycyrV8Nx+LDw8Mh0Wos5dMp\
34537# 1hepKlb7CMd6+Zrpob63g4FZgAiRSGNZ+5ATlbcc602nzbZMV7LdroZVi6zG6sXxzfHn2I6w+sEav4Pje2yRsgbqB+slDt+xQ1cn2lfVsfQeqjzxQKZIF\
34538# UUVDTY89Lp1q7HEaeMnS/mMdkuqhiVGM14AVswFZIyiuVyAnz2VuEJhRrUaa5Vmqet01pZ3qmEpGgVMY9kM2pSmFdG4A9GgzI+2ktVYdIlpuvFKB3on1b\
34539# CYFhGw9cxpKmH1+FJofFkP/4wKjAoWY9mXcHSK3/iE8sar1ZYi6JTklSjAhqyigCKiVb1W19YeDs4YLGs2IqyLncio0D8avn6Hr2c9YA0s4+DkabBIRd9\
34540# VD1ivyy+w+gKjY1YJa36RB0UPFdYj/hyewtu+qADuYa4gsxBL38KxD38ikaJuvRLWwyc8GNzFZuf7FuX0lXadGxvug0Jh4ahQyFuJtUp5a2Vzh9q5BGss\
34541# BMBGPn3/QVhDAF5HEw56+q1dREZv6ecrZ3HXDE0eq44VbARwJ29vPjrFgkL8BrDsdCxtO4/FTYyXWqtjuQWAqSfiVHLFwNLvpW8Aa95GGN3n6qCOvepYh\
34542# P54EQ6+tSNWIhVI9PvAeqwPtkr6JF6GNfC1vbV184RBX3/DRiwD1mONP6iI1TFwGdbU92Qy+S3ZSosYdmnWYxmfFp1/6QX/1LgM6/HH+fn5vW9riIUg/Y\
34543# L1WB+psla7yrVDXNOiGSs5OTnZLgP0fKXfE/blxCj5ButL3kcF3sagXArtxZkuE9ZUEvUEV7d9i7/XH36RhvJotx2I6i9ZiTVFSfMXOh9S52sTltRKEtF\
34544# ZMY7QraKk82Mz9imqhVj88LCkV3p3b4nXf1Uvrd3+SxWxZv7OWRURoJPWsOLR/sWb62KZNFMBy6TndoD3aJfXKv+Z5Gc5VmlWqE1RC7D4tDxQzScw2ovd\
34545# rAzrVnONOrIAixdRG5jECNe2eq3/+RzfMamb+n+QNw+GFs1J0zJMkd0Bs97RwKuzMNB0FQlAkuxm8cIlRwGoliQzo1cEsxQaUKGO9Rt9fgZfvm2LPQAAA\
34546# ABJRU5ErkJggg==/></a>"}
34547#@gui : sep = separator()
34548#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/03/13</i>.</small>")
34549fx_support_us :
34550  if $!>0 k[0] fi
34551  gui_print_preview ""
34552  l[]
34553    filename=${-path_tmp}gmic_donations.png
34554    need_update={"Y = date(0); M = date(1); D = date(2); date_current = Y*365 + M*31 + D;
34555                  Y = date(0,'"{/$filename}"'); M = date(1,'"{/$filename}"');
34556                  D = date(2,'"{/$filename}"'); date_file = Y*365 + M*31 + D;
34557                  date_current - date_file>=15"}
34558    if $need_update i https://gmic.eu/img/donations_latest_months.png o. $filename
34559    else i $filename
34560    fi
34561    0 t. "Latest donations to the G'MIC project:",0,0,24,1,1 rows. 0,120% *. 255 channels. -3,0
34562    rv a y,0.5 drgba 200 frame 4,4,200 frame 2,2,0 to_rgba
34563  onfail rm
34564  endl
34565  if w>w#0" || "h>h#0 rr2d. {-2,[w,h]},0 fi
34566  j.. .,{([w#0,h#0]-[w,h])/2},0,0,0.85
34567
34568#@gui About G'MIC : _none_, _none_
34569#@gui : note = note{"<center><a href="https://gmic.eu"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAA\
34570# BXCAMAAACJMYjxAAAAXVBMVEX+/v4cO0s7OU4wPnEiJSju6+qiqrCMlZu3u8BufoHd3+FDY2JRLzljS2bLz9Lp9vrteR34lyZqHE3puI3S7/hbX6GUW4z\
34571# wza97QiOaYUa6mqTdfz25i22+VxzqoGYqrht5AAAWRklEQVR42uyabZfiKBCFFYUCDDg7TqIh0f//M7duBSEx2p09Z18+7NzTPUYaE/J4qyjI7H7rt/5m\
34572# nXe/9VtfS+vdb21U7Me/hNbblPzu/6czEs/Y39LmT/jU36C0+7/p/OvXeaf7S3+7bkQ19uN4uVzGG3/i31X0/3GyEFjXnu+991sCMN36dIGu479tLR9Cl\
34573# +J/ll3PLMBKbJXbipbW+nq9Lgan2U/cl8V/4Tz3r8r+hEKMu/9CDCrDStdLz7RKXDGly+MxTBqZjM4hyJ3GR4Y1/suwOoHVuf8EFmgxK6YFWMyBI0yaGd\
34574# Two+qPH8PwuDAvLX36fiyw/oFKRcdEHYuS9UtXB4GVSP/9HLw15JwjMtGfzx/jELwyLCjp3fUx/AEBUzlgYI8rh6oItBCGl+/TjH975Rj9p5QI+4gaVug\
34575# YWPnQxOrvN5an0yFrv9+rE9m3oz7zD5xlxyviEBpHRiV+KqQgHAzoIeLAxAe+nw2tifq88pKn5P3bUTccZqzC69g0nc0f7f4ZY3l3OLQioQUdTsb/eu0W\
34576# /RSKu8iwsrXuHHWvKsju2Vlgde3774cd2dxRe0s2xjI4psEhtl5ZpvAzdIVVNVgQXDo8jaWJQ9RGCdLtgeZjtAi26BftBqgyrCrg0suqxdvcoEdm1fdgx\
34577# Uiqo1YOuxdjbasc6DmO1j5haeqgkLxe5e9uxQpid/EdpoYPA5FGxwYi+55WtCzDIlaMku6EgFL7gzJ2Hvbu0BKrsKq07KKfib/K150uOQoZzicVWr0E4a\
34578# 3XG2AZMhgGw/LzcollzeLzuiu2Wotj0YcmG8sz6aYBMPM+fSl1UECzVyxjcEnFDS2i7HCYw9KnfQukS1Lod2ILzg1oTfTPW/DjBOtebPVKaUZrFFa3DYl\
34579# WOzLmgKGbikaH0DIr8i+Z/QXVglqiCD6pMxwFAqthWDTjXRWVaqkFLdx1hnUgk3OStTNWh/YV1WmC5cwsN6K+MtZH87QW0wKrAmjOavq3RGJ/vT5uN7sp\
34580# dz7tbWNtVbCWNFSljmml9POtGjaUt10TkN2TIBVrBeN3azlmxbBYFRY72+QmYxcxiAEKo6JWPjY3FiZBS9YXJ/N67zGxenwOQ2hAJG5k1eLKIuNrs7E2d\
34581# fB0VQwpMau3MZgzlY+J/bVLPRJeppXsGzerYLquPYSDaILVSjZol7DoUPP66XQCMMkYMBbRnBVEZA35PLVzgr8Lq8vlBRfauLHQYlQbqgYGUFmpEP0MVp\
34582# QEUBU7NhZ+wwoU+CWHzjryffq+76q1yOxWsgod2swqCCxdqVRYtrDKtFrhJZnNOL+Edf4VnTPGABZ07X9kLqBVA5KboFrV3/uL3jBZW6SJIoql3NJ4lbm\
34583# wZrEEUBlV6JKoe2b3TpjggxptcBYnLT56F4cyfTzDsM2wGN0rLH+qrNDTiMidOK85fAsra52MLcGphwGskL3FRiVjXbIeBdYGVlKmE3+1eTjB+o/rHNsh\
34584# BGEWqEsO8wIlFrCwscqH/E9+3zMxwFrFYc2IXeCJMKgShqDxhGLnQVjKKoOLEpEAY2OtYJ396eScezrrDmMB1eUKWktjQUOGdU+bHmboGC2PrQWq0JqPh\
34585# HUnsEJmRS7q536ssYnZ2YoCjPpeWCEO3eqsRmAFwOLL5jD0cqzmzvJBWO0zK0dRT9eMfFGy690ZnPp0Utlyo0ShFPLjDNYPbuJlRz+D9dFaZ1zK4BuyXg\
34586# LOU2gxYMvKCN4ZC4KHEHNUawrhZRPpJSx0E3EcpvgahSqwlKgmeD5+gUXzIKQCPQPT7zaUz+HIbtWzlDU2Y2qa8TJz1tiH4/EYZrDub/O7NxzvEM/IJlq\
34587# pryPKQH4X/eddziCsWAp371531crIK6wGKvNhsfKEBcZ6AwuqsHQ7M5Yj0lv2SO2xCYrw9oLCgac6e22OxwbOyrQefeIW1fRDhTW+OR0dGBOEuGcjTTmK\
34588# z26snnWrL1m2EVaN8MqFc+2Onh9gsRWbzujF+QhRWKz1ARab3s6NhRT1LSzQcrBWRBRKlXUftQ4Mq6+whhEtoPWjwOr96mwtk5LrE5lqBm0dWHnnQDDGC\
34589# IMtAThJMpAKKDu/2vjygMRgWRlbiuhUunHiE1TfwNJUWSEOdltgcZzg4o4v2Aus4bo7O0ZzGzIrNHmGtVfuNoO1SvEGrKRWIDsnqWUNaLFIZFkogViRlr\
34590# oo3yAS1lznNSyo0mpSwr3nE8rdFFisAmuvFrCe6X2/0ViSs1gEa1meC5+wrFNHNYel6XRUh1PqMyvWsIrDYixeKmj9mnK8td5r7JIYsqGbP3mwKvwEK9F\
34591# 6t+r8AuvYzMVLnuRn/nPcxD6tsMISViuw5KLFWOC5VVodQwhaYLEuu2idc/0cliEub91lmMHq9RtYeTxEbrXXiCmZnCDpQkewUt7Vo9Bx5DQKCmS/WUFV\
34592# TjXFV6Y6iLPU2lnsCGzRnDIsUihSJ4DOxbwP+oWtOGGJDJ8oULrdp9WOzNZpnIfh1aAeyLDu0GO13rHTOgHI2N5tG5zxHknKskzX5jsIiiFxW2JcTUD0c\
34593# NleYJHzfxEWth7qTRrZilZvYYFVmQ15NO0hwzKoeeVHz3iti9KcFNlaamRYU6Glr8yKYZUEz0V9onTlakJYyXpnlbR0ttbkbqQDHLYQv1VZoTOWlZg+Xm\
34594# yEGQIytmr4l0hvhFUVbAW8NlYNQ2EFeja/z3WDMjQRgfjlLatKKx4bsleeDbGl/ED9joUgU2IBliyBuHHIrADrNr6eMeLiUFs8VqWODArFFyhFryGplQF\
34595# LyQ0GFk9Lm2Dd5rSsnf9VWK1z1mQslWHF4/4QcnpHg6ACj/fOmmDlN2TxPLUfhMZDuNwHNhQzY1x3pgUN8NRkrsdlvafsY766WGlBCqBkp9eua9MISp3t\
34596# Al7IboA1Lo01W/KkEF5ZlZ1SNa/gLb/l99NwKWZWrC+sVdo1i601INCYDfYXJlbyoJXh8KF4amKFh/1xt6ZFe/7GXtUSMwIopK8YnERNlT2q4iyi+D2s/\
34597# trfFrCaso8YXmDBS7MELw0Cy4BjzllTnnzCOn/I7/gLDsCq0GIhhXPsZY0XpgUNhdXt8X4DXsdIbYXUGgk6cPI+GpkKwyotGYEl6Yxh+e9h3fopDsfQZC\
34598# Uz+SEWVqbQqrBYBRbhgOGV/H6usM7vH0aDE5gJLAnEsgU/cAAKKn6Qwz4rAkjeg+8/bJT6aCFjJ0X8IjWRUxDGR+YTLAG5ARaDAi2imuJ1jsJMy5njN7A\
34599# wOeYwRH6vzvpsrHOOxgLrxt7KsC5PVgKrosLj6C+egmmfgcXImKKhVsbUYnABG7avOZxU0SZYRc7WFB/zTk92lnErWDUuAcsdi7P2Aqsm+PPnyVAOsrPw\
34600# KJBpAcqAnA5cMjMWWBOqy4b/+BaJKK8mltPinsdaVBYQrLCCpckVAQfkb002FvupxCFZuWZZNNl3sMISFqZGHt9+goUYg/jwa1jnJ6x0Y2sJLZ4KJ00lA\
34601# 5rQeAeqccsGvJ0/tMRx0SusKWKCc2tY6lj0/IuvK8OQahwaL1GYa1tHQX3tLJKL7iGEIaQ/FfGCaOI00cxR2PR4ajGwYCxgkRfOX9CIRu6QNmwqWzCCch\
34602# lRZa3Wi55SW/OQD29gcdZvBJYrsIpCSuUYAHRN71tgIWWJDGUoAuxLWKwCK/AUAx4PcdTY31h9P/KxmExQjVHvNsiAi7B6kVnDkoG70wqW54bUHdewssi\
34603# EfCSzhi2wyLyDFWqCFztzBIqmi+pC6nMYZmkA6dnfNyZ1haRBlN9PpvK7bTLqhdXzKBirX4pSZLa9fOmO/CwWvMIzH0ahlmFYCRFeGixtTdy55mksZ9ew\
34604# JDPu1T4Xpcc/STe7JSdCIArLCDQ97OCFVZaxtnz/x7RPA2kGknEsj7pGFoT56NPLTyKdbgZLDXYLFhTEakiaHw/kJQix1d71h5ACKQTVTZFHMhhZ4Tdep\
34605# Cln7QJJB67r/NLt8E1h0Q/W5x5hCRMABKyi3/NAkvYeO0ehfZth1fDdXIO1g1I7AscMic0uYInqOsti67tsI/LHQ7K46PMTyATWdymvhvz8l0+osHdu9S\
34606# BglTQ1wATXBLJlzf5BWSkskeSsGRbiSURpP8DKq/MSFmp4ySUBlptguZbQNWcFvK6spKDU1eaF1rgLvz4O2tVvKknsYkqQgnCSfF8H4hyimdZkQ3BVXJv\
34607# IUQxQxbVL+DQABsvj9A9FgBWifAtcDk4kVTU+iUqosNwJVjt1UFjfssFyKDAat5GVoid0WMdUXHCfkoIeTLHs98JLYBmfZ4Q5PXoOq2Mx45DBUht6ZBqV\
34608# RZZHDuuVi7oQKnwIANCKHELF5s42bCldo5eciaTGLVZjkk+Uh5XNQ9VYiR/l3gfF2Evsd05fDZYh02hLU/OCvFu1HbFYaFnmGWBhCB0Wznd0TKKUcw2nL\
34609# M/eYTlL8K6haqvi5A3WelF7eQIIUoy+vOkDYSVSVhBgVW3HJa8e9i/k5HcqpyEALLrGL03UQVUja8ubW2E1gQrJv7UG91Umc6mw6hT0yAIm9A4bosANSu\
34610# WfYEVMS84SWabP7w0X1qi/DVaLr8sOkt9ew+ozawo7t8gCF05liCy4cIK1mWCfIiOWlmNC59BhbQYLL5VV6z+wM3EKtz5cUWkxYjgzyVeLLGwOdSPd3kE\
34611# zwNqgdLlyyPkNrK+Uzldde3FQfd4eWl/GyHIjLH+CFTKG02BDWUs396RlsKAO61u8H1q2iAeruLHg4lTGpHUAkpyatn2jhNaHsVJdTEc+udCNtPJ5HkMI\
34612# AsC5nmMyQgsVDJYbYdXeUZaVOnzYLdddqClT9Tz8m374KT5TnvNo2FcDthPAmGMUDxIx3OiruO5xoF1pPc6s/ArL0pDHuF7JidrIbEeW9HFt4KEvSl2H5\
34613# aacpZ5VWMVXyL15REXg7wUtZ3VWHVZgb7A8lzAMKWFC12zVLjSqDTNYVeHdDsdD94Vd338aKQ/F91GbBlYrrjgHPZ7NlKPSUlgZjzzDciOskFuyao0p2n\
34614# 84wvLKqsGCihvk8/OisxCcTeXFogGs4GC1/ihXE9cvoVRhsT/puLI5e4WVF1D6hdIC17tRWI+LSAKrP4zB6qGFWu08rBuzubDD+noJCyk+D7g2pogT3ea\
34615# eF2nsuT8MkbPwaTnL9MGfDwGW6BhLN06X+RDDtgvqSS4ve8ydR1q+yWV7mBFWNx3FZ4ScXGg5C3QGWF8NFhpml+deTWbEFRYiTXhx9quwvxhB0d821LEm\
34616# 3RlUv632UzpF1nInSXNBknONDQNjNrSyb1nBmAtf2vDJzvkOK0Rnsn67Ynp7hQ9YUGEkrEWW2incOXLoUQ9ABsrpnaL3a4jv5CcnGirIs8GC3UZYZC3hw\
34617# hVWAKxngVHYyUpN/pnFymqZad21H6qtHxqdJN+If2cVEtbvdSJNHVx2jAx/acRVRH1vZFQpPvlV6bHXfVihcG26yox4+VnNwIdpm1jdg6WbELcIsNQoGL\
34618# 7pYuAmP8Lq9Sh9OfvQbxR12idYAbC8wbrq1GDd2AX9NyyoxNwAQQZL1wX1jGalRRe0PNsKXoXHabBCREGtxQWwQoWlf3yNLG+KaezU+TewMnG6AevHD4N\
34619# 1y4arSoq8GaSmzDFiO/PGvDE3XNgVTWmXDFZXjpbHqhxqIbJGpP2noTVLp07f+v7ChpY88NnJ/4SFOcNCibNrws4zTqjWJrSt0ZWdd0wU57tEijFNZZGG\
34620# EmplKZ6bUUxTp1NwecwqMZalN2CB1ltY9OWmQr/KF9W/UtnDdd/gi6A4jZuJmPqZUIhkKj08qMpqpakWCqzS0mluy6xOCqjCHQMd17D4Hiq7yy9JoI1vg\
34621# 7/GFYUGtvW5hgEpqWHkwbQWDj2b1mZnhd4pVDu8iP/l0xzM/BoWDpj+tHN2u23DMBSObfBXG32Tvf+r7hxaabu18RJsHXYxAi4tRTSlLyRttU3kc79oS4\
34622# pTf5GTcPxDc9in0xePTzi09V51n/WxLp8tu2Rm4WAU/DWBM7p8yuTrAevuY/xqv/N2quf5AHakvG2p5W3gp0vKU058nbDu7njiBH5FWIqbOrZRUGrlxJO\
34623# aqRF+jeEvEFw+xherypvWMuwnj6n56Jry0VLdUks8lTcBWBEBWPfkq5/Uu3WM8LFCe8YCHYal77HaWMYY9gorl6FCtf38mQoQz9cWjOrdmiwfXP2g6CO0\
34624# XBNohz/BqsBqwno+tBJs1LWwPgWVAClFKyq3wQBxVw1Xc2lYvWRtWKWK3KOqynSX7iEr3wZa2a/vVX1f9CUyq0yvr3ZXKlP3lN21ZuQFSLk7wri7Kt386\
34625# kajKp6zD5eH08B0kuaean5clM2L/Dqw7sP6cje0fA2HEswwdbFgTInY6rZg1kQj6YwwK8ICBgTOtkXagJQ4foKpDhWGRDhmGhimxYgtgaKHhhVbS/+Vyb\
34626# axvZpnQIM0JDuq9sseS3fFyhHrgBEuDG2JyHVkhGr/cWBE1cLLaBbVaVr6OmGhvj8fWgbnByzfcR6LeSXiDSx0GX4Dam7eaXhAWSwdhKCip3s7VXV+FnY\
34627# bhrw+1rGiN2+wQgHJdH1vDlOGYw+sozwMM3jjVYPnMIqFQ5U8d5gU2swJL0DTMcpWqsizW2GzOof1ZdV7YWkyYXF6sSwMBOFvh3va8832LCGsGIypI/yz\
34628# 5zv8ipcdPWAr0hfjV40wFXXV2qImg6QvrhnwfzIPrHZgRA9stdNT4YihbXSg1UxYHbBMjPNoWFHUNqzH32UV6yOw8GCadyIL11b47z1zsWYxhoq5cYO1O\
34629# xYyYcXwMQwTTR3s9aExRgHW621p375Vt9i7de+MLKxxM3GsPZXJTHOD2/nsf8BqtTOGZRCnXgBBJqx+5wb0hCVywMrWDMaTeq9kdVqyzp+1dDUhMRBYzY\
34630# HektEhDJMxXjdyI6phKcaFYXIsUAByjU5N3ybbvWGNyg0Bou9gXRrWBnOk4zbNScEKIh3GhwEjBE4OWOsLLKnlPiwxFMqTu649BIvyMSxG+gFLbXh2gSc\
34631# e1isbrEBEJ9kReKRAVxss3glLtzDPC2F9iwKrCWv/9g3H9jGstc3txVyUYZTSxjGKHnVRHDOylglr9jWXhqU/Rhbv5XW2h57lnVl4JncqvOgIG13PK47i\
34632# EVYXWUcx4q3LRJjh9IBly1BlZEV0Gm6DFdyPkm/hcsAivgDxn9Nw1qxCBNzM6YGTCG/UhfMIB5F2SljxAot91CyurGMc9zYNIWfPc6LY7JwG1vlTaSo9K\
34633# JdZYWywfO4WIFaBRpRoKxbIqMRRQTRcoBpUELTJVedUD2NlpqVYZLuhSejOgfnOPOkpfOY8vRqctNPDSIU+Y/QwtGkWyQ638MT5ruHK7hNakDTgWk8fHF\
34634# ROzOfnmRJwvAWnO9vo4EvHECi0S3ik144+x/gRWjas5jBYQcrl1pI+B0BvswtN5Y35gPkaKdUeWrKUlrMrcToNBSDQJ+CJEe4ihYHlKY6ghA5OhHXlTMR\
34635# jPdnrnG8d9sc3vHsfrWbjGsPiw80Jx/F4Z/1Wprkxd0926Tcd7ej+Fa3vhiw455Ia64dPDNyTfp4cGWE5574/bz5T9yFLDz+HYEe9eECk+h9aXzn1feqT\
34636# halb8huwnzGXXwyVdH9qySJZkPz/XfH/5b/88/IdcfPxH+J0LVIAAAAASUVORK5CYII="/></a></center>"}
34637#@gui : note = note{"<center>is proposed to you by</center>"}
34638#@gui : note = note("<center><a href="https://tschumperle.users.greyc.fr/"><img src="data:image/png;base64,iVBORw0KGgo\
34639# AAAANSUhEUgAAAEAAAABBCAMAAABW61JJAAADAFBMVEX27vz06viVWEaZZlbz5/Tv3urwxMeFXE2icmuBUT7w4u7QzeKaaFqRVUPs5vWKVEKBRyvr4vKR\
34640# WEfSj4d/aGWuaVjkqqaca1+ATjnAfHCVWkh7UD/z6Pbv1d6DVUSGUT2TY1ONWUji1ubv0NiHeoaLd3zEgHW8d2qQYFCOX0+LUDri2uvcnpeQen+md3GKY\
34641# laYYlGXXk3p3u3Z0uTFyN7nr62Aa2uoaFehYE93Tz/tvb7su7yYf4XIhHqgb2WRZVieY1KKXk/u1+Lvys/uwcPPi4KkZ1eiZladXk2ZW0nn2unXxNHpwM\
34642# XqtrXntLPfr7DhpqKtk5yHdXuEcXSoc2uSbWeeZlaIWUjj3u/uzdPTvsrdu8LHrrmjipKQfYZ2ZWalbF9zYF+TaV6gaVurZ1aCYFakYlF9WEyJW0uDWUm\
34643# PUz5yTDyETDTd1efSyNrRt8HOtsG8pK7go52IgpTMlJCbhY+rbF2QXEt7U0RiSTxXQjJ1PiLv5/bx4/Hw2+Xfztvmxs6WpMLlvMHNsbvCp7CalqqkjpuR\
34644# h5iefX+iene3eW23c2VhXGWpbmKLZl2wa1unZFN0U0hrT0U/OzlRPC5aPCsrKyrKzOHCw9nnzdffx9K3u9KvudKnsszAtca+r76tqL2UnLfcqqp6iqiGj\
34645# KWxmKK5mZ7VnZmbipbZmpLUlo68jY1rdY3JioC7f3eveXGfdG6wdmxpY2yIbGqxc2ZRTVNkUk9LQTt6STGFSjBHOS8qJR4gHhkWFxLd2ey5wNjGvM2Jmr\
34646# mvobGCka+mnK6Pk6zZpKNpgaJ+g5mtjJB4eo6UgYu4hYHBh3+ZeHl2anBsV1RwSDM9MytmPig7IxPSz+ShsM3lubvetbl4k7TOq7LFoqnSpqd1hKLHnZ9\
34647# paXmVcm6FaWRMU2F7XVZDRUpeRTZTMBxgMReaqsezrcC8n6mklaKfkaC/l5pbcY+nhoqvhYNMY4Cnfnx3b3xbY3eOc3W1cGFcWGBiVVZbTEliS0O4qbjS\
34648# oKBQaYhBSFRDMiYCeYKqAAAKU0lEQVRYw42WBVhaaxjH4XDOoUREUhBEBQRFBLu7u2N2TqdzttO56czN3Kauu7u77rq7u7fb3X2/g9db3OeBv/Dg83D+P\
34649# /7v+d7vPR8OhmE8Jhx4GSLsSuytFYyHceD9l/+fiPFrwD9/0/FjiHEM3tiYMXaZ9vt/+rVULNxfoHE39oH5MTcex8DhYB03EDCmL+z6+NTPVStWLj+xPA\
34650# oGtH+GGxNAjPPB57/lOPNS/74D+18m7d7d359/3btTCAj/QowVB0LouLGvnbd+8/2Bw7cOHz584Kf9+3+4keSdMAXRvQxE0Amvzb/ss1f7h5tv3Rr5/ff\
34651# Dzc0/zct/kZ2QMAHWJWgpugu17Mt9Tc0HRppHm0dGR9++bX7blP8y4aMOoc5CG4M/XRkv+/LNyEhzU/lUUVrARKKDQ/GNn35smnVxymKK7sX/V8Hdz74f\
34652# fjdKLi1tJBBS85KTe32uSH840OSQKTTXTWv8P7yH3+w/cFBOAApzcxvY2ysLYSqz9428zqQY1qrOX/7wI9FI6w8NbRjYa2MWwuTEf/5jQSZiEIBx/rf9x\
34653# SQMkOof6ubG7sMANS5xl1OqFxkEeO/X/te+JMyv9ncLZbP7zMyYTE6MV/vG9Y8M2m4Pn+c7+HIJav9UcAfC3NhsGzOmkhODCk90P55hgP/4+eusAl9QQV\
34654# hqqFtDQ2gD20bG5Lh4oojd2exoAxIseOq6hwQAqeAWlLiFjiXgeAajCLKkPhM24BY83UMikQYVRAGZrvEPCy3SAoJRAKD2PKLoByzbmkIicec2jb7//m0\
34655# yFD6nKLShoXfbhlg0EGntemwA4IPnJBWpeG75zZu3D5LpUAm7kJDWVB6wY3MQJfJUpwGAmbt3qmbtKSb67fDzOGgLsWXbjSx5Nz3SemMojmc+MgBwMufa\
34656# i8+/HSwOMJo41YOn+S7u2UTBQQuaenvcDMrZ9QYA1vTv/Mo75fU8oHKBwK8vfhaoR6D29fklg7JqPWxACf2k2df2TJw7r7z8puBguE2NN2tu01uLbXVxb\
34657# cKVj/QCjFf2sJJJvgUOxLKpHqIyD3ez2E7XufNul+/d4rIoavlivN4usJ+VsgPsJEVpY5qInkYrkcU8uvhyIj2sZvXqzJ+r2/QCloEm2EEqJKghuoUF3R\
34658# SaI4uPjd+yk7tXpryfuXij/k78QOWbDABhBD5EmwRB4SV9yuDVzO1m25gcNIOS5WmtL8LSr/J2qUh5oe4REA2CNCVFQ/FI9BYwEWqCUSEyIUGmD7Bw1sC\
34659# u2aS8VPdwQACvkqJN94VbGmxC4tEg6xkZCdtgfXvRPg8DhLlH8EEJ797R2HmjVwfY3/nUoNbW1tHrz+lLcFySl0wiFWIl2NraVlTQbOp2MZPDi8yUwRnW\
34660# 1pkbMvQ++2eqVMmkwtQICAIEMrlizjaO57PC8O+YMWhG0ON4/Z1ot+KiimTkD4EloNF5PAtan9Kz/aI/2ycWRdEn7doK9N7HnVwN1gOgFeg0/lBI7P3Hu\
34661# 9x8OGhQUHQgzgDdrVUZhdPJPBML3qFDkyLYPkrlFzsIZvEAYA0bkoCxlsRVQ4BQMc3E5BDE3hx/pUAcZlaDZgSO+/VM99OkQjUEKjA1nUQDvczxav80ZQ\
34662# CUkAGPGVsWdnbM+BuhC5v5woig4YM+VJta0N3BAnrGbfFhuqBjBeDxH9SqZLGANX5i021newJB3Qjx1RpL8sEKdkgs+uDJBiUHHdsHeOcLKYShDfBfBz1\
34663# dQPoF8FhpbOTz+TSybbgN08WrPY7DcUG1sc0ZJ7+Va4qYQlhrNsebm//PTQAPRjkxjUa3oEUMhShdUM/ouDgvdLr2YHX3aQqB7w4AY348rAvArykAE4Uo\
34664# IpN5FlCJjdIlGkWjY70QGPPjl1wqEPPdNyEw5ofxxuYtuhuq1k8sJhInepDJZAtoKORce3Bb8APwk1rAUkmBBhriAABsDsPGeGNYdxXsxfIAAJgqwAjuv\
34665# XWrH7Q/CMJjUUHiBbVG/PC+GCwB3MLAw/8F4BlLan3VmlIsgQfIwKMXXa17sjpa+OdpFLa7QIhwt8EA5uCkyzBnMIBpvL9aIiuXbH3lQNBAjUTR1LkeHg\
34666# IyT6BW1f0SgyIUBA8DAmy3JiVijpknBYbh6S3TGY7gsA1WogWhWFm1RmV19fT8Np8M8fmNpSKBQHCTTBaQ1bvqXLxiAx0roygYIP00FyrZ5oVMh7VuR0d\
34667# HnFNr1ASgqhWf5OS4SoeP3qZBfFNLOkYANYgK6+I80csJFKvjkaAKSvoabvicTVgiBoNh55iezsBNAerI6ly3MTExKTf36LE7h2iQKd80TUTmlfMEctBI\
34668# KPrxDSF1uqMdSEC9IIaKNnkhiB0jMjLdzsrODldVVZXVvTGhW+LqmpSbf+TYHZMKGoSFEIl4ZNO9Sk8UDTrzZrWTFeVuZIvxaVeyRYQsVohgXiuryZOtc\
34669# Fkd1R9vrM++LGW5uubemH/s2DSTCjCQTC1NIRotXAYGOprRtefqs+qsrI4JCy8Npvn3bj7XBqxO4AWE6zy1rt47J8dbwmK5sjDA+yYmk0ACrcAo8vIK/v\
34670# QayTc5WUXaqbpy5eoGl/uZ0dZWVKoV1cmJSnXCJdRftgfyTsQA+fNvHQUAWzpkSoNABnaIC4cDxqQvF6iggEtKlm2KRgIXzYCXfeh4nIoJJ5FIpVJ7b3v\
34671# pdUDInX/k6DsT8FyEsIliCRUx489tAac2LtihRlyuEbeQa8MxpyAIbunZVvMxQE99vSRJai+1T8rPZwHAkTsmh2xtaYBgSYfmMDfXzeYSjLTy8xODUeHu\
34672# EwgjM6Y7nZxCXagFdHd3f5KTKJV6S3YXu+4ePnLrzjQTW1ADEI3G9pEpr2lP3sCuKA5QyBXQ3hgKskgYebJj5konZ2olrn7FihVnahMT7bt357KuDx89C\
34673# gAmFSCCRgNBYQ1mLi8xO8FPEUAEe0xhaem/yTqwdfHi89UPOxnOzlSc9/qoyhMf1kq9O7bmsr59M3/fkWKyCbYQ7AECBJWEfDWSJldYitJ4PB65jEgUiS\
34674# CzGCRwwpTn1acDcZOdnXE52YuWUyvPSj+PWvM1K2ff4a3De1hTsYluI/NtpLtvfk0QK0rp4LwzelsgArtcpOj1QiitWd+cWgkz0gFgbXbb2irqPUm29Um\
34675# Jq/T7fScubEzCCJPMNs8qtXD3zzMCBWAjoqwMBAgQc7c/80QolMpLH0Y5Uo2PO+POZEedX9e6tKfe+t7axKTrw9Vbq2pZL/wqbIdqvnCg204jgFvvJ5dj\
34676# Q6ZULh64uiGuLRBBKJEnVlktWMrAOePWfVL19LJwydps6/fWSpKS3kz49Yvll1gklT+bOTuAZzJNQxAPOsjFAQqxQk4gpPadC0LwAECx6oq8t9IRPxnn3\
34677# fPRWUnWkq51Ue+tkiQmvqr+LP/TrueslO2y7XLgt9wpHhx0cCBiUmBrwAxGhEIEyGrBw+WRduk4+5yEVZL1lavOLHZeJZEmvlp3vn/2qa9zWbOvXCsDgC\
34678# dfOwz6OWgJZZallnS+j+eitqBABHTjvTWVTlTGH/5VIUZ4CClbAAAAAElFTkSuQmCC" />   David Tschumperlé</a>     and    
34679#@gui : <a href="https://foureys.users.greyc.fr/"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAM\
34680# AAACdt4HsAAAC/VBMVEW3xMQAAADUqJoDAwEICALFmozFqIwGBQG1jYA7O0EfICUdHiMPDAoODASth3svLzYFBQQoKS8LCQM1NTwaGx0SDg0bHSEHBgYJ\
34681# DQTRpZfCmIpuVk4XFxh5XlU3N0C+lIemgnaif3OPcGZ2XFM/P0clJSoXHAoUFQfAloi4kINrVE0QFAgJCAece3BZRj9WQz03LCYODw8UFRjNo5WXdmuEa\
34682# F6AY1plTkYpKysuJSEYGBwlHhsYFRELEAWotbWxin2ffXEyMzlPPjhMOzVHODMkJi4yJyILCQi0wcGTnp6CZlxPVVVDQ1VxWE9oUUopKzQsLTJDNC8iIy\
34683# UrIh9nbm6LbWRZWGOGaWBJU15eS0IxMT0gJCkgGhYSEhUUEg8XFguyv7+Ejo7Ep4u7koWphHibeW6ZeG2VdGqJa2JzWlJERE00O0NSQjouMzkhGxkfJgo\
34684# cFgkQEARBQUk3P0diU0ZBREQxNj4uLzoqMDZAMyoyKSY1PyEQERMcIQ8LCwwiGwgYEgYSDwSturqfqqrKoJOvlX2UfmpXW1tJSVFHR1BIT087O0hhTEVZ\
34685# S0UiHxsoIA8nMA6wvLyMlpZ+iIh9YVhPT1hFT1h7aVc6QUlHR0c3OEVdRz86PDwxMzMpJyQnIR4qLxoeGBMcHAocIwjHnZDCpYpubXpvdnZvcnJkZG9XV\
34686# myTc2leXWhWVWBOTk5LS0srKzA/Ni47LyovNRofIBQkKxEQGwq8oIVcYmJcXl5RUVtWV1dMS1VHPzgrKRSXoqJhY3iljHVbWnGbhG5naWVQW2WIc2BNTW\
34687# BTUl0+P0xFSkk4OTZ6hIR4gYFze3ungHFQUGVjYmGDb1xoXlVdWVA+SFBAQFA9QEE4MjIuLy80Lyo5LRmFhJG6k4d1dIC0mX9vbm5UU2eNd2NJSVtGSi0\
34688# 4RBYyPRQ6LQ+lsbGapaWAf4tsb4Z5eIVYY299aWlDSlN0Y1JvX09RSEBMQz5CPz4/SxinrKyVmZmNjIufeWF2Z12ObFYuJgeXgXY+PimhEuIiAAAMWklE\
34689# QVRYw7yTX2hSURzHPZ57UeIqcvE2/IdOiZz5Z+OqUxRnc/iXDUrHGJhOlpuDMIN8mA+p0xJGU2a9jDlXIRgEjUYPG6PtZU+tFVHQ/hD0EKxeeqgoiIqu+\
34690# 1Mrrce+cO49f+75nO/5/X6X9h808O/lMcvfVjKumjIZT3HsX4C3RRrN2eDYR7enNrjE1tbHucTG4rXJ0F995KouZ7XOxcraxw/laDlh8psD0Y1o28yZ23\
34691# 8DHPGUxkJ/enT52ogNP4SEFygJwDFjDliyvMk0BAjX45bxT79fovgxYOZgsw6oNI4qIXaTg2EQlnIrDQEGD3fhczF0eOrN3GmHIOJ1MxX6qLHrqk5hw0x\
34692# +AGYsjQHjuaXqo98cWB4qFWQky6PzeJJmhUqalbrJl60AxC2NAa9y48Xf9zsUZ3t5Ffq+Xko7SOrZa1YWnZb6hHnGF0vVMdehMPowQUXWjUoPALzs3ksV\
34693# zQ08ri+J0NTdBd9YaPUnemWzhxSPnqWTJP0PWVcbVdTjtuq4J+OJHxTKpIGI8BQROr1Zsntuyy9Atmu1AeDFyczk5NuFjPMAIGwdtSp0tQjubrpDQX5aW\
34694# GtQCty7S5MZn8+zvnIAGNX0yw8Zp8K3F4MKKdM8qg/i2sy19Zm7S4uPB/YBs9PRTt5hAG8vGJUIaRVw6zP5HBoMU9y5tYMYWPJGwka2qLL0OvE7JVt/An\
34695# y055e4r0qbc679n9DlafLrjFKyIiLrAD32ygMK4Mw497+lkl+irT4vx59Oeaq1SeeAL795U0u6+3gkX7Vr//BltApFIrRwO3Qst1CdHHAVPZeowqCVVg2\
34696# LL3IzPgpgcVXj5bI3q4tIVa1aZm1PM/uwhYr6jA84hADi5pnNo7OngXDFQ1szTJWE3IcLb9/QckueYlnozarYIjMw6/v6OujkvgPenb5aZkYuxQN5IlbY\
34697# 3i4AvMAAwOygGcvxMnZm3VdcsVwz5F5wgDc7JOoSCLqvtzq8CrK3km05Zzun9tpPySlAk8GfD2zfYuDpGJ5kJHGHktYUJHAEaVMKuaeFTW2Y0pEYmieIU\
34698# 10Jqa5HOMJDeb3XE8b3l7/NG9UUgPPgCwZYOzvpFJLGWTuxJItmBgDHY0kGUH6/ceOGn0jkmzp7ulVqAnb15LF+Se+dL1cmavpgktBHsGf3TIBRSMfArW\
34699# QhCWMpQLOD4wAGWRAoX7++HyBknd3XO1qmNRrO1jNp74h/3mS6N/EuqmwavWgy2bTGAHSkYoVYGsdBOgVvAkCj2p5uvl7ehq3t04RdEtaEZecmnmkiNuZ\
34700# gpJ1te/dudjpiPyuax6IExmDFGAA5j4DkDmRRAFATQvXMy4UglMkEmLq7Y1DhZrL1IzKvGB02Dl2fmNA163SivmbbVZGdEWQdh6kLt1iprxCnABACKh0Q\
34701# AYXtWAz49e6AWn1V6pZq+eI+tUksVykEeiYTbRmShZvl7m6RLQU5QQAhA0my0pACpBCIBzngAtgV8eRJoF9uVQgEonBYpjGKmV2Doqtn+WK2VNRuZfcLh\
34702# qNgXwiCfK1dgWrgxv0TjAPAtEOAarUoKrcyVWa7uOWcTDrUYu206SUSvs6mkAEIaoLwAkgjNQdBanSLFYMIYw9wyjSI8vkoynSjEuMwija3C6xsvlgsR9\
34703# lhk1qur3llMBgsiOMQT1OAPLXt/vJyAUGOU732jlOz4kE+WhM/3MMcVqHtw2zm7jgik/Uz7YASC+5eASJpqpB+sFQ2IUqEYRyfl/eVmUCHYSt8UQcdrRl\
34704# nRx0NP1piUFxxwVAwMxEqCb8SpMGFDrUNdSk6SAjZIaIvpSA2otbVW12KDtWlCIqIiKg9dIqgQ3TpHet3GIYXnv888/z/zwyacWCzVjtrQ9BKzwVkVZ4X\
34705# MJl00O22MGrdMof1tDKCFdA2MAfSEk0UKKQhSYREdcFKhO2F4tFK/FrU7Y67k8XlYpzpX+P9Fj/pJzbstDZaVrKKCIDeAqDRuYWRDVCTrW+DgaTBmlSDg\
34706# KzYzrzP39m3L1zOlxL5kvVEqcwKbJ6xMMEU0+onreTRBJpkyNbrIShRmqSL+tYWtiFAqEn2ht/h57vHPdalnSuZdNpZDW2UU4V4pxLzt6os6YDkEAEagg\
34707# Uk2UCPAhCLhoIh+I+d9fvqFn89tJLrqDmejzqj+bjcXWowwXTscBb2SCmBXMjUEXFBGejKE2KANUCOFmjI5YjhQsoZVePxYW69HE9Z8jud6rrF4k62AxD\
34708# UaKDR/6YooXM0oNBYMaSRSHMcGQ+EHLgZDVa8+X5TCPed/VKp0FrMqnJIIInwdlEPmOGh//tAxggoHSARYGIE0nVNH+NZoOBsMJmkW24W98VzsWFiI1Rg\
34709# TIE6exhIpl+kXsEaNCXMV9BmmjLDioY1DWJzkmHWwWQc3mjQE1oOrKSvNRx1byxJglDeCfB870ww/ndHYaRhPJvNtKORCJ4ZSFRE35BJy6lKxuNgvd6kz\
34710# DgYdZElM8gAhMF/FwDqQWhDCFEAcBo5VeDJy69cylQUp8al4w5vnlUTxfbGkA8XCkIydpptNBJ28A+MTZ3BVP9FIkTdIkwVZQIjr465ADD1Nh+32KjcZs\
34711# LZ5cCyM7GeYCu8pameDq9IY4I+GG8NpoNfv6a6CEVqjfB1bbImGq4XR0j9Gdeex4+dAsmCp+KRfesCry42u4ybf37h/JufNzYREgfit2+SJo7GYm+MKQN\
34712# CaEynhkE+ChC+2//g84Mdp085vUmhWiqwQaERdft9xTr/5cfShQ+rn+6u7lrdZud0BU0MGkqiQhlmCxOMUGTTRf57By+/DB3OJoqynBTasQRbDfssjlCx\
34713# 8+VOeOnp+107rnzfdv/jvUNXIpGjoydPJkQAcByHJ2uTva+fXd2z48Dlt7//3M5mni+m2zmmwjc8cpPxpLtf7twJP6puX3149+HFQ9/v7vr+8eO967vRa\
34714# C7wt6ayCW0aDsN4RCzm/xfTP4kpaA0eEhDxq0Fwxh2MGi8GEouWmFjBXoTUeGhr20npraVd1Yu0q1URD461tm47FWuFtqNrK6hzuokgzrGjil/gB17MBH\
34715# /393ne530Or8uVvPHg0cQlsRi31HNXn/8Z3nS6PnN0+v7mzee3bVy36/zhx7MBR/3xi4M1b7nJ8SaLr0KW3+WzZ+3XduP9i0cTLx5xghGlw+jck9/7nWv\
34716# uphztO8Ondq3fuP3QvgPDtn878msikc28/fguDzPVqm+8AbkpUP2IY7ceTg6h1wzo1xi6q8UvPdty89iZg18DgZnIhU0njl05Ox2Zvee4F7n9EOpUtWaa\
34717# RSZcZHNsbnm5NRhkxrHjbpgh+zhrIXf8U7AIT04c2ToyMhZwBJaW0mPz6XTKYZN6+fMNIng2iVpdujColJTeoqCaZCOEsURTzTA0Ho2rYAHQweT1oZGRv\
34718# XvvOQLz7bELewKOf/PTvyY5hrGPqHygmVxFknOyXFSrPuYjNi4KUBV8FKHQyHDdjtOj1717926YrQcCS/PtmVWBQGp68pIR7FUk2I9+HsC5FkBRXQZ4iD\
34719# HLWI0RcC8xjnhe6vqHRqkSGL3+fWRDZEfd8Z/Uq8kbKEfKHbgTfYrmvKUWSBpSh1Y1/F0ZIxmzLzTIMmuAIE0kaDgHnpz7dvd0atV76XSkbuf/8cA1V4J\
34720# zDFqrFTslmJPXUD2dgTWg9msYp2bjkunJQpcnGPYwxJSuJ0afnrw2vbpBfWa2PT8TGeL1SrArkoSzEPsAPSvyWkCQeeQjCZLF8q6MXQNu22tEMpGFMQU0\
34721# p+DDB7vbY6tEvqYj1NTKSicXRAS91hYQhF6MJgS/iji7liZGSXiIoqNRCLUoT3hdJGMgnFtz8UL6y90NZ76mD1/DG6GerNe8Ccbr0oTlxVIlyQJfk8jbv\
34722# fqqmCto1TjNWIiDoEYRIuFnOUjGPUn/cGQs/eXOxZ1ixvtZIX0myWQF65PSEphlvMHhoawPiawdwSTDMLxgRAsgXOAJXZY4WpQVxIeAy3V5WJEInGM/A1\
34723# +ZdJMiwAuxFX+PMxEtIicNRESSGBJBtBi3V0gYQYHhdcMUqVJUkaBVXLCCMUlnGI4uNTiax1XCFlAkmcapAZ53rnGzXreFAcPSDAXGw0OEpWYggfwxmZc\
34724# lq4AoIRyWlQrQKyLbcIfjRYvktDBoDZrqYg6nPHzflzU5DAF/AqeUboxPJCQ9V4LOWMW/KFmwKcbU7KKkzHWE/DjhmSpoCcLZ1fQO7uMId1VI+rM4zXr/\
34725# AnoH9FcRhS6kAAAAAElFTkSuQmCC" />   Sébastien Fourey</a></center>")
34726#@gui : url = link{"( IMAGE Team / GREYC Laboratory - CNRS UMR 6072 )","https://www.greyc.fr/?page_id=443&lang=en"}
34727#@gui : note = note{"\n
34728#@gui : This plug-in is based on our open-source libraries <b>G'MIC</b> and
34729#@gui : <b>CImg</b> (C++ Template Image Processing Library),
34730#@gui : available at:"}
34731#@gui : note = note(<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA/CAMAAABnwz74AAAC/VBMVEUCA\
34732# wMBAQEDBgcPExQIDQ4KFBcGCwwQGRsPISjxhycoS1YZNT7rgCEkP0g2PTiOVzXidSJ4OxgXGBg3XGkTLDQbEw4eHRxCa3gyGw0aHB/uchSHPxlYVlNwNx\
34733# kdHRtNKhNNeYgvMT8XEAnvn2vhjkjQfUi2hGWMZEo1SkzXbiQjISAmIiAcGRlnjZoTExM/IArWw7TnhWfti0igTjYmJzKlVizpcyeoqqOoRhfIZR8jIiB\
34734# mMxQqJyqFpa9VLxhhhI8mIyno3cC8vJqJiHvkqGzujWbukle5e1CBRyyEn6I7XGN/RSJBPj0iICFnOR1aY2SgVBl5nKciIyFEJhRcLg7DvK7nqH3vr22l\
34735# Z0qsY0TeZT1kOSerSyKTRiHg3NeESSd5VT6PsLuLr7tGRESFiIN0OBM+TlwyGgwODQ1XMhM6QVprSDzSdjloj5suMy6lfWE6VVxxSC2QPROUcFePbVSJj\
34736# ojTx720zdXDazPGurA2U12bgnFybWhjW1YkISAnJCNFM1Tzdx32exgyNzMhHx75liH7nyH4jiFMM1UtMi/5kiDpfS/ZrowrKSf0ih/2za9JRkX8pB+aXj\
34737# 9EQD8rLirteR3ncxz68Obz1sD1wZjUo4DBjWlRNVg6K0D6mSP3hRxsNxe/URb56t7muprtvZjhr4lqa2gyLy70jinwgx/1gBvpx6/uxKbzyKXztITcqYB\
34738# KcH1mT3hmRGpgOVxOM0w+NzV5QiTuciCmUB/8qhzfZhbVWRP65NPgwp3btJn4yYrOoILBkXL7yW7QjmxaNlZOTEpBLknxjDkvKDLQZSDgbR59ao11XYT3\
34739# xH14dXNycG/Wm25RP2fCiGCKblw7V1yhdlVtXVJEMVBTUE+daky5ckkyQkFVRz/5oC3CWiKwyL2Rsbn0xai+o47714byp31kYmGvfl1fXV36uVrmmFR2W\
34740# kayYzSXSCJJMSG1ThfW5+PE1tL72pPmvopxWH9WS3adg3FYT1v7s00+OUX7rDJkj5mrlYVSaG9uaWbbdTHVCtkHAAAAfnRSTlMDAQkhFRoPLij+Vj7+Sf\
34741# 7+/v53aTRHm3pkXf79/LSsqIh4PP7+/v38+/v5zb2ahk7+/v7+/v7+/Pz38uXBr56QU/7+/v7+/v7+/f368N3Yx7enkYtw/v7+/v7+/vzp5uPMu7u3sJu\
34742# XcGZd/v7+9uzo5OLg39zb2NXUy62ggmXEYn5YAAAF/0lEQVRIx+2UVVAbURSGl92NJ00guBeKFau7u7u7u7tmG0KSxiGQYC0ORQu0RVociksp7nV39053\
34743# aR+g02VgOn3r95DcmTvny7n/PTfAf/7zbyD9TTGBCOhM2Yy7DRqYzRo+H8CH6MgGR0k1df64qQHo782WZkiZ+rh+MpFKA9aEKIdbAL+ALJy3OkO/ts2Sf\
34744# KVhobFSM9z+R5OpbGh/jF+gJvYj8II5Q9VqdXExy27OAggAZ2kqrf3CfGIzRgF4wATztbQD3tKkQM350Ea6XC6WC/n+4kh1pJw1Z5RRUlIgM85HkY13BM\
34745# YGCCQxYBNvaUOgptFQuUql4kZFBbhwVfJIVqSqOEUzMFDpo1AwYZwEBr/YgH3rp1cqNTVb5WKBQBCACoQCd4HAn66KSkYNfgrFfTOcesLsly+x+Im77xs\
34746# ZPQ8Q+IsDAtwvowjkqki5MEDlkpzCjFFkDyfgjA9bb+qLg1h3OtZ+KZGClk/u7kJ3d1QhEPP5LREBUVyu+mlNw3ASzgRQR46cPfXmJhAzrC7yb8m5k/OJ\
34747# zxegCjn/Q1lmWYELl8ttnW4G480AzXw0dc/NnW1+Z7r/w1ev7uS0qNAc+OoPdzIzM8uiuC4uV1ai44QDpE0jGFR/1cXWJmL/nNc1NWUP6f4Cf35kTpxNV\
34748# VhmASooHuLc2TsggQ4ebhaoK0gc8aj2tsfjh3Q+Cn2CTWKhR5gE6+C5CYAPw5FMktXbA8CCVD6fOXbwgEIrihiFMrnPgAEftw/CBEErYHwBRIUB3cR6LQ\
34749# 2TVDG/l9cNXiETFQiFFN+FogHbqpJRQWrQE338p0rEhnlY3gzCviAhJhg/3iZCiEKRjguPj49D6w1TgxpxzwAxqNir23LOWNcoiCWMSB87bnAlJpBTKtD\
34750# lwsVY/ZWg3NUA3hzMHcnAhsB+fJ+MoXZ01uL0dKkVvd+kXgWSXq9j00MfqQ1TDQ2HxE0n4hi09cy1sX+NE17BaZPpdIrdI0s7yd1bdy0nFrB2TJhgVaS+\
34751# 4mIY9MR3Fu4l0kgwAGsTDwV7eXk/oURESFgsy74TJ/bNDe1lV1Sk5nLRS0yR2mzGSRAmUEkQzJ5LW+WFCu4PlFAoFMn5RX3Rep8KFhelKOXp0rgaM9xJZ\
34752# juyCSBDj3w0ODgtuoo5cJBEIjnvc36RT6xPxaDk5ORBQxt9s5jzAdwERq5ypMIgDJwMDg7uk+jn12jVzzLU13p5Q25W2PlJkyzjQrJsRpHwh0DbfK0eBG\
34753# AcV4TWNjdnh8TExipirPv375+lUNyKkVqv0cfyxxeQ9cjkn+utR6Y3Nw9sbfTLkoaEWC3zjQnLbphiAmn8BNcAQG2fbcD28yDnjSsHKpV+vrm+S5ICjYY\
34754# MQQWdGjAIZAYBe++gjpPpzN4cjmz5EqVSyZyStGKZpPjZMHtTLQugUwXZfK42oGFh2qO6iSfyRDgc16rKyUv7P22V9OsnecZBPEW8XbokPAGBBjPWHWYT\
34755# 502znTY1vkmEIKjhS0hFZcYt9DYLUAEHEfF4PXpo/dkAstcxyFQ9Glx9Mz7+xmdbHtYBp09oWIi3d1pa2t3vCCJrq+cNwxEwHGkgDAEaWtOabOOrm3qg8\
34756# HjGtY9vR0dHe3tHP0Y8eSgiz2EGOGcACSCRTUNDgpxsRSJbT5FIhAXhmvjuXUb07VqOTOQpQzgznQidhAiPpgKogTzaoOexmTJXV1eOazuQwhEGELYP4E\
34757# ME23qByNrmPS9cuHj10qX8s23k558+c/1tT6DLgNCYhISECxcvXj1z+hTGaRwBPmPelJaihvaChG4JDEpLSt78JtjUHYHW+5KS0vaC03Uf13dHYJr34MH\
34758# 7Syh1dXX5aJSFCEdmDHa9Hp5hHH7P7RzCaQfiNqYbJ3C7dy0cE7Tn3PpuNJAXfi3c4zeBLK/LLeh6GF8rf+Dh2VGA8GaDXbxDD/QE5fWJMk5HZG5dHIUR\
34759# HvXh5d/ceMhvAuTcvC7Va5BGuIWXG2MRdKS3E9g1gQagMyLPLVGGdCzXhYBuAGk59G5fba9FALoL0WKLk6mDg4OprpYOEfgPPj8AH7GtomieWx8AAAAAS\
34760# UVORK5CYII=" />  <a href="https://gmic.eu/">https://gmic.eu</a>     and    
34761#@gui : <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA/CAMAAABnwz74AAADAFBMVEUAAAD8+/v77u/9+/v8/Pz47\
34762# vT25Of04+aYbXL03eD+/fzx2tz89/v99/n98PHDn6ns4OnQtLqshov88PP/8/T51NT46evz4evguLmlYWn56PDgwMK7govXp6u0YG/CjZWUd5Dt1tqTXG\
34763# WIWWLx0eLl0NrAcnnIeYDw4Onlxtf54e3z1+Xs0d/rzNvz5+704ev33ejt2ePdv87vt7ngqqyoconZpqSoUlpwPFndxdHgsbTGorHDm6zMg4XBbHPPaGq\
34764# jOUW0NUTxzN/jv9PSl7PMj46zYWmfTFv+/P7YvcjuwMLOsLzmvrq1i5/WjZe3Z22oRk6UQk359vf77/TuutbkuM/brsPQqLrXuLXMpbXrrbCxbYi3cHaq\
34765# WGOEQ2LDT1e8RU+YLDykLDqPHDD17/Lgs8nWqr70oKi+kqa0gpnKf43AdoWVXnvMc3qSU3DEYWTNXGKSQ1WbSVScP0uAJDj66fLrxt3sxNfw2dbqvtPdu\
34766# svfxcPWoLnKkqq+i57hhZHTfYSpZoK8enqsbnauXGv66ez93N7qzNbrycjJkZ6xeJLTiZHDiIm/gYSxeICjYXudWHfCYG+OUGl6QmB/N1a5T1WvQVCsMj\
34767# 9eHzz0x9/wwNr8zc/VtMPhnaPsmqHYlKHVnp27mZy9fJKYYoSld3yLWXmET3GEXGyZVFqGOU+PNki8g5zkkpvRlJiihI7Xh4yLaHqYb3nbbnFlMU2pP0l\
34768# xLUn44t7rttDKhpXhkZCaeoavZXSjZ22AS2iXX2ezU2GPTlq7HjH61unt1M/irMuMcoGfWm+bT2mlUWa5WF3psc7/q7fXm6yrfIuzhInfe4Sjb4LafHb9\
34769# 8vrm0MvgpMPBiafLn5+ieZW/kZGXcYueZoeQZG2OQ2OiXWKJRFR8Lz+cIjH5zuXhtNP9t7zSnqWUf46kaY17OkX91dj+xMGsjZTth5GFOT5RFjGGEybjx\
34770# sz7wMjks8H5r7HMrarJgqHjoJhvDinu4NrWjn7NgXOhO12uTFzKaYJvHzpyRElCABtwx6DiAAAAJHRSTlMA/pTZ66c8G5RtyJC8rw7PnpSQfSTMUfTllM\
34771# /Ev6+U7Ojg3lN8FIp+AAANFUlEQVRIx5SUaYhSURzFm4pWKlopiqB4+hz35ZW4jFuluTsjjUuZa2pOpqVONlZO5VYThZFKuLRQUUMRyCRtYBZUUmBT0AI\
34772# tQn0oilbaC7pWEEHrvV/el/fjnPP/nztgfhsRQ8KSiCRiwKYePGjU+ElDB/zXaWqyETEYEgaLJRIDAaLNZmtrGzV8yL9TmuY3qUlkFANFDpBI0WhUrY6+\
34773# DV4LnQtOHD5h6L8CsAQGSyyOM3BkHCkQjZbOyZDd8+btZiLPJw4fOvrvgKsBfBebynqWo1IZDBQeFUzI9CdOvHe7e32tBzfuZk4cPvqPGcy/GsB1saXSu\
34774# JjKQDGaUahn2tmv373r7+da6vxMqsoRoo/MZk4c+RtDg9vagIK4VEqNSxlUMZCASsgA4H0/l5vJ8DN8L9dT4xYKwkUb5s2ePHLCLwA2GwlHwOGwZBQFJQ\
34775# YaxN8BHq4lw+ebvEWvscjl1rj9bg6cP/Jg8tThE34GXFWTCRCOTMZRMJgoMUokBSXI6/fcfi/XwjfxTUVwU7Uat1blcAo1hwJ9Z7fk2Ywx44f8UIAld86\
34776# R5iAsNkAkkjCkaCyrf30COChmgAdjQ8FjT/fjWjfncPhxOBx2oyWlEFusDs2cNm4IADTWoNPudOp0bDEZhcJSsDEJonjXX+QWLXxwvF6jN9XvDnMO5/NK\
34777# pbs7XDs8O1R6FlNLOpLhg8MGDJ5vowjmtMvtTl2XLicVU6Xirk1bWtwej7cByFhMIIKiJ4k+qqfRnm84fCn5mE47F4xKEzHFY751CgA0BQhdGjkbAFgEq\
34778# pQq7tKs27qIrkoZjfV6I0RwTSn3iRZEwpQ87KGXU+H7D2OhZ4novEzFAgBt8wNQczOLpdGwdAxxjioW6zZtRQtTRpPRWgcCjOAjk3KvhPOIJMt83uFL1+\
34779# 4jDyQS3cNsvWKZDjJoImJw+LNnsTiIQkGh8GJqbh0AqFIpk9ViMaWNANHnKbsPL5qt1Say2TvJpGNDXu/qyDoqlToAgDpT8DgIwoJJYkkULBYbZx5xcar\
34780# VlNds5qV5fV6eiefhhIV3dstC50pBJp3j0AsdPc8vWSoNC4NAnYnYs805AoQnYzBqUgPw4Djs8XC5X/9PeXjpZBKNRu+SyELBUCmWF74s+OFHDiu/UvGP\
34781# HDCozRbFE3LyWXa2XNecyzFY1FUvNvXASo+nmE4DAM9hNnpVwpY8IpMltKVSKbbxudLfq79kqlTMtIEAACycXdW+dA1b49SwWFKpxr53I5qu6uNxLWZeX\
34782# 5/qcR9QAPeAemuvnwuGrgUfKI7eo8Xo9UolnAWApiY1WbC6/coap12+wM6ya+z2y4tgukpVNVqt5ow53e3pU3E2tMxDZt+UyWSht+C12CjLxgpWS12vBQ\
34783# AbhoKDBAvk7Ro5a06OlWPp2h9shQ1gEbwWqyVj4Z3wqHxHWza0HDmyezYTSVy7ppU8TJQeRqxWh+QcyICIAVNEQZ0QhIdwBDw+3ryaedylLFf7uDyruW5\
34784# WHe2mG2B40aKefB7Zpadd1wIXz2JZvzXtkES/KYDwBAIoJJglmQxqHX9xWdHidnP7eT4QAd3RQleiOUL46CmXS7HrCMIMXgvGafetfEf+YfxriBSwiasW\
34785# CATNAogAEORVLy73wG530cNT+T76Vr58Xz0BRzifPn24ePHQva3MxLVgjIl+DABbJHFgYf5VEkSw3zrgdN6yy+Xs9j063daNLoMqBSyYfb1ldMtr+lEDP\
34786# enzv+ldETn9PBE6Jzvd4Xj6NFM4gkgHAsB8NRlaNXd5+5qlc+V2uY5tX7vpVKRVxUtVn75pvWeAnxTovarkB/+HD2/8EdorbQg5H770lP/0koJ2Mza2AQ\
34787# hQCF26ZWvsm9cs1TjbWRrnplMdIrq5Wu31X1xpQCtfCn2f/G8+fOr1H9rFZO5JZA+G39SemmFaNqH9qiCKau7S7LfrbrCdJ9k5to51ZaNLtNLn8/haRfc\
34788# MPSsLSrT/jf/zvQuHjh+fvU8meSQqm3lW/0s9DQkNHDCiqY2EwxM6d67u7OyEGq8jBEJUuNAwp1vUGllhUHYbOi7cu33o0IUzrl278gjt0f1I2ZyuXtIj\
34789# 93ftGdkAEPGEuEAggCBCM+jk2bNzXjxQuODublgkuigyRJStC2/vWHzhwpmN+gJaqXikv6/ipY28S3n9S/SDsQCgjuLjccGSnau7Vgk6VwkIAqDgOAxGD\
34790# 9NFEZGho+PgofXHlm87cxc53PqkzDnhKFvNaevTw4oejgH5CiDh57Q7t69bN2udRi7XyZ1rNp8/taK17BP6ey9+6bzcQ5OKozgewaKo6AUVPSgoXfaedu\
34791# 1WGpHXrefKUltdE6KsTBO02rLUpkY15yuVUHv6dtlMEXM5Nam9sJRRura5LVa01Wj/1KA/osdPiCDoQX25f104n3M4v985v3OEL+wmsylsNptfsI6sP1l\
34792# 2bjWZfHnDgQcHPt24cbe3vhDBrt2LllH2qAw+lQxCfFzpUdlRe0uj6MSnnQ9OioTCPrYpDL7P4bo72PXV1Xewd28cP/jg8qcbd4rJVOKlQhLBRbLSJeyc\
34793# KteOSLmIzCepSbQIaZevXD5JE8pNsBEOGcPhOl4pDXvnRvWO44/PndhZLTqCvU5ddeEeAGwCr7uYI40jkIvLJG1fx+VyZO1Koaj003FaUmcwup8DAD+Uv\
34794# EnbX333/v0d6x/f9dbVJ2sJ+251fPg4HgAOYXAUirjK+vBhVWWVpb/fwmRKGzQ8HqgeHjcYaO6Co56gJx3zsooBgFX8+Hoi1qElrFq7dtvp06NHTQC1AB\
34795# 71h2cebsQt3Gixbux/WMUh1dgzGo1aq00Hml+/bg4EAtGhoFOrBlOHun5fR8Zms21ZW1FxoQJEMGF5oStTzjDFnL0cDukwh7SOeTQtcwJlnEE/sPe/9vv\
34796# 9tuiQf8hm6yASCQTb0Afb1rMVFR8enf4IIlhUgqEwSXx+OwRBKgjazk3L0u3pYDDvzAQBoLkQQrM/Gh2K+jOdnWrqrY50dN2QreLsow/3nm0GgJI1eA5T\
34797# KlDp5XYdxIW4XK5M1p4J5vNarTMf9bwONDd7SEFPwB90ao6do1LrO50AXbPtQoXt3rNHAHBo+eJKSc7gE0A+HZRu50KQ1KfTJhKxmFprb3c97wp0RbcHA\
34798# kP+fIxXfY5892bCmcgr6+s7nJltbyqmjpqwa/eSqqac0QipDEfXbV8n3rOHIZE22Btqy8sbUb4j8BxonScYzeeTCWwx9g5rUBvLJM8r1J2dV7d4CoBTJR\
34799# QGI5Wiiy1Vy0BPAx36MCJrsJcfq21EHS5Xt+P5c09B7eB2rS5dT1YMJnvPHyvX9N4i9MbGjpq4e8XCjRRGhG6JWMXi/v6N/RZ6BHlyE+WVFreajHA8Hne\
34800# 5XSEY9kAtwv2rsaW1VEJisJdXo1YSropYU0dNLMHgGJamVDabQp48gVTS7m7IwG6pqytrEwnDRiMMO9wudwiADJ+FIixZoSYStwwm9l3tUL+NiWrHjipa\
34801# tNTSn0XYbD1qQuWoQSVHdXp9S2udsIzWZmYbBaEQ7BowwiDFfWU77lA16lvEbQkl4RLx7Vsv9nwBsFCczY6YULbdgOpQvRyVy3UCua6v9XYbb3gAdoMQu\
34802# nNcd7evRVSGPULWqNW31mqUhLWxV69oJwBg4tNTliwyYrL3SA2o3MFX8XU+Pl9q1zXwykf0Pr6D73IP5LrdcYNQVFZaW8sqDJzEmFJDxr5/t5oFAIdWVk\
34803# YkqDksRxy5FJ0eSUXodElO+qQBdED9gANyGd3xeAg2ht+JRPt3lO8AI+stgtf7glb97t3+5FQAWNJPjzQ1NUWs1hTdQgfnEYk0IYj0ZtuxRrPAPGwOhcx\
34804# G2BwO006sXs9i7fD2Ulft03jf9ZG9ClpjIQcYPIPBsVotVrqVHrHSU1kJJLC3DJbdvl33VcBmmz3Dbjg0zAY/VpM1peRiBXUfgapOeoV9XuUXAFgB2gGD\
34805# wwH2OUQq0CNyA2pgG0yDdW2324b1PewQbObDIbiv7Hxp8X2qQqHRaIhE4pdEYs7YBYUBvgizhoITM6FheY/A1KrvsaMCVCDQ64BDYYs5zh9wG0EW2YY+0\
34806# N/J5RqFQtn5YebsaWN+DNyTMSVLlzFk8LC+VWfqkfeMjAhUbDYqTzbuP2YXOLoH+G6Xgxvvaz3fyFJmOifNHTdm1E8qOlWy9CKnCTHLe/QmvUkgkaoQnw\
34807# +paSg/dlMFzPl8h6OrK55Pz5s091cLHQAsXojjZEfCOj3bJ5VJrHQxg8OUPKkBSnc7XIFAV9f8SVOA299o8qIllS9TWUTVBOqxiiG2VlLObN2zh0SqsZH\
34808# 8Uf/2SX9cmAoAXGXlS3ANXnIYFMY1PIWCB9Pyta1bt66ZUfTd7V8AJRsrXxZEweHOnMFv3roGU7Ji1rQf297fAZilOByFcwaPW4MHtjOKpgPbf9HoRUtx\
34809# eDwes3TRLHC4wPZfVbTp6fIZRYVl+T81fdwY4Pb/9Q3i0ArjHjdFFQAAAABJRU5ErkJggg==" />  <a href="http://cimg.eu/">htt\
34810# p://cimg.eu</a></center>)
34811#@gui : note = note{"\n
34812#@gui : If you appreciate <b>G'MIC</b>, you are welcome to send us a nice postcard from your place, at:\n\n
34813#@gui : <small><samp>David Tschumperlé,\n Laboratoire GREYC (CNRS UMR 6072), Equipe Image,\n
34814#@gui : 6 Bd du Maréchal Juin,\n 14050 Caen Cedex / France.</samp></small>"}
34815#@gui : note = note{"Postcards senders automatically enter the <i>Friends Hall of Fame</i> :) !\n\
34816# You may also consider <a href="https://libreart.info/en/projects/gmic">making a donation</a>!"}
34817
34818#@gui Contributors : _none_, _none_
34819#@gui : note = note{"
34820#@gui : We would like to thank all these people who contributed to <b>G'MIC</b> in one way or another.
34821#@gui : A big hug to : \n\n
34822#@gui : <b> -</b> <i>Sylvie Alexandre</i> <small>(packaging, testing & filters)</small>
34823#@gui : <b> -</b> <i>Partha Bagchi</i> <small>(packaging)</small>
34824#@gui : <b> -</b> <i>Daniel P. Berrangé</i> <small>(packaging)</small>
34825#@gui : <b> -</b> <i>Sébastien Bougleux</i> <small>(debugging)</small>
34826#@gui : <b> -</b> <i>Jérome Boulanger</i> <small>(testing & code)</small>
34827#@gui : <b> -</b> <i>Claude Bulin</i> <small>(packaging)</small>
34828#@gui : <b> -</b> <i>Aurélien Ceyden</i> <small>(packaging)</small>
34829#@gui : <b> -</b> <i>François Collard</i> <small>(testing)</small>
34830#@gui : <b> -</b> <i>Patrick David</i> <small>(testing & filters)</small>
34831#@gui : <b> -</b> <i>Maxime Daisy</i> <small>(code & testing)</small>
34832#@gui : <b> -</b> <i>Frédéric Devernay</i> <small>(code)</small>
34833#@gui : <b> -</b> <i>Iain Fergusson</i> <small>(filters)</small>
34834#@gui : <b> -</b> <i>Tobias Fleischer</i> <small>(testing & code)</small>
34835#@gui : <b> -</b> <i>Roberto Ferramosca</i> <small>(packaging)</small>
34836#@gui : <b> -</b> <i>Jérome Ferrari</i> <small>(testing, code & tutorials)</small>
34837#@gui : <b> -</b> <i>Andrea Ferrero</i> <small>(testing, code)</small>
34838#@gui : <b> -</b> <i>Chris Fiedler</i> <small>(gfx)</small>
34839#@gui : <b> -</b> <i>Sébastien Fourey</i> <small>(G'MIC-Qt, ZArt code & G'MIC online)</small>
34840#@gui : <b> -</b> <i>Gentlemanbeggar</i> <small>(filters)</small>
34841#@gui : <b> -</b> <i>David Gowers</i> <small>(testing)</small>
34842#@gui : <b> -</b> <i>Claes Holmerson</i> <small>(tutorials)</small>
34843#@gui : <b> -</b> <i>Arto Huotari</i> <small>(filters)</small>
34844#@gui : <b> -</b> <i>Dan Leinir Turthra Jensen</i> <small>(debugging)</small>
34845#@gui : <b> -</b> <i>Tom Keil</i> <small>(testing, filters & tutorials)</small>
34846#@gui : <b> -</b> <i>Andy Kelday</i> <small>(testing & filters)</small>
34847#@gui : <b> -</b> <i>Alan Kwan</i> (afre) <small>(testing & filters)</small>
34848#@gui : <b> -</b> <i>Angelo Lama</i> <small>(testing & EKD integration)</small>
34849#@gui : <b> -</b> <i>John Lakkas</i> <small>(filters)</small>
34850#@gui : <b> -</b> <i>Stéphane de la Linuxerie</i> <small>(design)</small>
34851#@gui : <b> -</b> <i>Mark</i> <small>(translation)</small>
34852#@gui : <b> -</b> <i>Mahvin</i> <small>(testing & design)</small>
34853#@gui : <b> -</b> <i>MareroQ</i> <small>(translation)</small>
34854#@gui : <b> -</b> <i>Ramon Miranda</i> <small>(translation)</small>
34855#@gui : <b> -</b> <i>Tou Omiya</i> <small>(translation)</small>
34856#@gui : <b> -</b> <i>Mauro Quercia</i> <small>(translation)</small>
34857#@gui : <b> -</b> <i>PhotoComiX</i> <small>(testing, translation & filters)</small>
34858#@gui : <b> -</b> <i>Garry Osgood</i> <small>(documentation & filters)</small>
34859#@gui : <b> -</b> <i>Jehan Pages</i> <small>(testing & code)</small>
34860#@gui : <b> -</b> <i>Andreas Påhlsson</i> <small>(filters)</small>
34861#@gui : <b> -</b> <i>James Prichard</i> <small>(testing & filters)</small>
34862#@gui : <b> -</b> <i>Guilherme Razgriz</i> <small>(translation)</small>
34863#@gui : <b> -</b> <i>Karsten Rodenacker</i> <small>(packaging & code)</small>
34864#@gui : <b> -</b> <i>Marc Roovers</i> <small>(clut data)</small>
34865#@gui : <b> -</b> <i>Dani Sardà</i> <small>(translation)</small>
34866#@gui : <b> -</b> <i>Yuri Shemanin</i> <small>(debugging)</small>
34867#@gui : <b> -</b> <i>Silvio Grosso</i> <small>(debugging)</small>
34868#@gui : <b> -</b> <i>Stepanekos</i> <small>(translation)</small>
34869#@gui : <b> -</b> <i>Thorsten "otto" Stettin</i> <small>(packaging)</small>
34870#@gui : <b> -</b> <i>Lukas Tvrdy</i> <small>(Krita integration)</small>
34871#@gui : <b> -</b> <i>Martin Wolff</i> <small>(testing & filters)</small>
34872#@gui : <b> -</b> <i>Bernd Zeimetz</i> <small>(packaging)</small>
34873#@gui : <b> -</b> <i>Matthias Zepper</i> <small>(testing)</small>
34874#@gui : <b> -</b>"}
34875
34876#@gui Download External Data : gui_download_all_data, gui_no_preview(1)
34877#@gui : note = note{"This filter will download all external data files used by some filters of the <i>G'MIC</i>
34878#@gui : plug-in (<i>Color Grading</i>, <i>Light Leaks</i>, <i>Grain</i>, etc...),
34879#@gui : and will install them as persistent files on your hard drive. After this operation, you won't need a permanent
34880#@gui : internet connection anymore in order to use some of the G'MIC filters."}
34881#@gui : note = note()
34882#@gui : note = note{"<b><span color="#EE5500">Warning:</span></b> A <b>lot of data</b> will be downloaded.
34883#@gui : This can take a long time !"}
34884#@gui : sep = separator()
34885#@gui : Force re-Download from Scratch = _bool(0)
34886#@gui : sep = separator()
34887#@gui : note = note{"<b><span color="#EE5500">Alternative (manual) method:</span></b>\nIf, for any reasons,
34888#@gui : your plug-in is unable to retrieve data from the Internet, you can download all
34889#@gui : those data files manually (as a single .zip file) at this address :"}
34890#@gui : url = link{"https://gmic.eu/gmic_all_data.zip"}
34891#@gui : note = note{"You must then decompress all files contained in this archive at the following location:\n
34892#@gui : - for <b>Unix</b>-like systems : <span color="blue"><samp>$HOME/.cache/gmic/</samp></span>\n
34893#@gui : - for <b>Windows</b> systems : <span color="blue"><samp>%APPDATA/gmic/</samp></span>
34894#@gui : "}
34895#@gui : sep = separator()
34896#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/16/04</i>.</small>")
34897gui_download_all_data :
34898
34899  # Color Presets.
34900  l[] clut foo rm onfail rm endl
34901
34902  # Grain.
34903  _filenames_grain=${-_fx_simulate_grain}
34904  _url_grain=https://gmic.eu/data_film_presets
34905  _prefix_grain=grain_
34906  _ext_grain=cimgz
34907
34908  # Light leaks.
34909  _filenames_lightleak=${-_fx_light_leaks}
34910  _url_lightleak=https://gmic.eu/data_lightleaks
34911  _prefix_lightleak=
34912  _ext_lightleak=cimgz
34913
34914  # Sample images.
34915  _filenames_sample=${-__sample}
34916  _url_sample=https://gmic.eu/img
34917  _prefix_sample=sample_
34918  _ext_sample=png
34919
34920  # Demo thumbnails.
34921  _filenames_demos=gmic_demos
34922  _url_demos=https://gmic.eu/img
34923  _prefix_demos=
34924  _ext_demos=cimgz
34925
34926  # Manage downloads.
34927  _n=0
34928  _N={narg($_filenames_grain,$_filenames_lightleak,$_filenames_logo,$_filenames_sample)}
34929  progress 0
34930  _gui_download_all_data[] grain,$1
34931  _gui_download_all_data[] lightleak,$1
34932  _gui_download_all_data[] logo,$1
34933  _gui_download_all_data[] sample,$1
34934  _gui_download_all_data[] demos,$1
34935  progress 100
34936
34937_gui_download_all_data :
34938   repeat narg(${_filenames_$1})
34939     filename=${_prefix_$1}${arg\ 1+$>,${_filenames_$1}}.${_ext_$1}
34940     e[] "Download "$filename
34941     if $2" || "!isfile(['{/${-path_cache}$filename}']) l[]
34942       ${_url_$1}/$filename o ${-path_cache}$filename rm onfail
34943     endl fi
34944     progress {100*$_n/$_N}
34945     _n+=1
34946   done
34947
34948#@gui Filter Design : _none_, _none_
34949#@gui : note = note{"
34950#@gui : <b>G'MIC</b> is an <b>open</b> image processing framework. Thus, including
34951#@gui : <b>user-defined filters</b> into this plug-in is possible.\n\n
34952#@gui : To do so, you need to create a <span foreground="purple" style="italic">.gmic</span>
34953#@gui : file in your <i>$HOME/</i> folder (or <i>%APPDATA%/user.gmic</i> on Windows).
34954#@gui : It will be read each time the plug-in is launched, or when the <i>Refresh</i>
34955#@gui : button (under the central pane) is pressed. It must be a regular text file, containing the declarations and
34956#@gui : implementations of the filters (written in the <b>G'MIC</b> language) that will be added
34957#@gui : to the list of available ones."}
34958#@gui : note = note{"
34959#@gui : Existing filters are already defined this way.
34960#@gui : Writing a filter from scratch in <b>G'MIC</b> requires some skills, but
34961#@gui : can be generally done in very few lines.\n\n
34962#@gui : <span foreground="purple" underline="single">Example of a valid <i>.gmic</i> entry :</span>"}
34963#@gui : note = note{"<samp>#@gui My effect : my_effect, my_effect</samp>"}
34964#@gui : note = note{"<samp>#@gui : Sigma = float(2,0,10)</samp>"}
34965#@gui : note = note{"<samp>my_effect :\n     +blur $1 n 0,255 xor</samp>"}
34966#@gui : note = note{"
34967#@gui : Look at the reference documentation and the tutorial whose links are given below, to learn more.
34968#@gui : By the way, you are encouraged to share your nice custom filters with us on our forums,
34969#@gui : for inclusion into next releases of <b>G'MIC</b>.
34970#@gui : "}
34971#@gui : sep = separator()
34972#@gui : url = link(0,"[1] G'MIC reference documentation","https://gmic.eu/reference/")
34973#@gui : url = link(0,"[2] G'MIC scripting tutorial","https://gmic.eu/tutorial/index.shtml")
34974#@gui : url = link(0,"[3] G'MIC filter template","https://gmic.eu/template.gmic")
34975
34976#@gui Friends Hall of Fame : _none_, fx_friends
34977#@gui : note = note{"\n<span foreground="purple" underline="single">Supporters:</span>"}
34978#@gui : note = note{" <b>-</b> A big hug goes to these friends who supported the project:"}
34979#@gui : note = note{"<i>
34980#@gui : Christian Stenner,
34981#@gui : Daniel Balle,
34982#@gui : Matthias Fuchs,
34983#@gui : <a href="https://www.augustus5star.fr/">Alban Bourrat</a>,
34984#@gui : <a href="http://viewbug.com/photo/65310796">Elizabeth Hayman</a>,
34985#@gui : Nicolas Künzler,
34986#@gui : Mikael Wargh,
34987#@gui : Giovanni Bianchessi,
34988#@gui : Job van der Zwan,
34989#@gui : <a href="http://espitallier.net/">Laurent Espitallier</a>,
34990#@gui : Mark van der Grijp,
34991#@gui : Patrick Wauters,
34992#@gui : <a href="https://pocketvj.com">Marc-André Gasser</a>,
34993#@gui : <a href="http://www.flickr.com/photos/ssshupe/">Steven Shupe</a>,
34994#@gui : Mika Yrjölä,
34995#@gui : <a href="https://www.silviogrosso.com/">Silvio Grosso</a>,
34996#@gui : Marek Kubica,
34997#@gui : <a href="http://www.flickr.com/newmikey">Mike Bing</a>,
34998#@gui : <a href="http://pixby.com/">Dave Allen</a>,
34999#@gui : Margaret Wong,
35000#@gui : Adrian Bottomley,
35001#@gui : Pamela Young,
35002#@gui : <a href="http://chrisbowness.com/">Chris Bowness</a>,
35003#@gui : Peter Howarth,
35004#@gui : Marlon Montalvo,
35005#@gui : Christian Freiherr von Malchus,
35006#@gui : Nolan Tyrrell,
35007#@gui : Gilles Bouquerel,
35008#@gui : Mihail Balabanov,
35009#@gui : Rolf Niepraschk,
35010#@gui : Volkmar Geske,
35011#@gui : Menno Tjoelker,
35012#@gui : <a href="https://borkarabhijeet05.blogspot.com/p/about-me.html">Abhijeet Borkar</a>,
35013#@gui : <a href="https://www.behance.net/kontaktarl1e0a">Arleta Lesniewska</a>,
35014#@gui : Nicola Giaccobe,
35015#@gui : Helmut Mühleisen,
35016#@gui : Paul Buckley,
35017#@gui : Olivier Lecarme,
35018#@gui : Edward Ingram,
35019#@gui : <a href="http://www.gus-verlag.de/verlag/Artelier/">Stefan Städtler-Ley</a>,
35020#@gui : Michel Pastor,
35021#@gui : Sz.U,
35022#@gui : Sven Kraft,
35023#@gui : Frederik Elwert,
35024#@gui : Jessica Leonard,
35025#@gui : <a href="https://www.viewbug.com/member/KennZaney">Kenneth Simons</a>,
35026#@gui : <a href="https://www.flickr.com/photos/114936163@N05/">Milos Ciuk</a>,
35027#@gui : Manlio Barolo,
35028#@gui : John Lewandowski,
35029#@gui : <a href="http://mediaklan.com/">Didier Lima</a>,
35030#@gui : Žygimantas Tauras,
35031#@gui : Massimo Ferri,
35032#@gui : <a href="https://github.com/sina-ht">Hiroshi Takekawa</a>,
35033#@gui : Freelance writer,
35034#@gui : <a href="https://www.flickr.com/photos/49284009@N04/">Elaine Hutchings</a>,
35035#@gui : András Somogyi,
35036#@gui : <a href="https://www.flickr.com/photos/tonurics">Jason Dora</a>,
35037#@gui : Boris Hajdukovic,
35038#@gui : <a href="https://mappish.com/pages/about-us">Jeff Combs / Mappish</a>,
35039#@gui : <a href="http://flickr.com/photos/btraven">BTraven</a>,
35040#@gui : <a href="https://500px.com/spodeworld">Steven Brener</a>,
35041#@gui : Susanne Gabrielski,
35042#@gui : Andrea Correani,
35043#@gui : Mads Thomsen,
35044#@gui : Djek Eykhout,
35045#@gui : Michael Calabrese,
35046#@gui : Joachim Steiert
35047#@gui : Christian Dubettier,
35048#@gui : J. Casseur,
35049#@gui : <a href="http://www.gnomelibre.fr/">Okki</a>,
35050#@gui : Dariusz Duma,
35051#@gui : <a href="http://www.mahvin.com/">Mahvin</a>,
35052#@gui : Elleen Hennessy,
35053#@gui : BluffStuffPlus,
35054#@gui : <a href="http://www.bertrandchan.eu">Bertrand Chan</a>,
35055#@gui : Mirella Scotto,
35056#@gui : <a href="http://www.photopablo.com">Paul Sauve</a>,
35057#@gui : <a href="https://darktablemaster.de">Lars Mielke</a>,
35058#@gui : Devin Sorell,
35059#@gui : <a href="http://www.quesepuedehacerenlinux.net">Pepe Baeza</a>,
35060#@gui : <a href=" http://www.lesnoy-tanets.com">Andrey Pivovarova</a>,
35061#@gui : <a href="http://doliver.co.uk/">David Oliver</a>,
35062#@gui : <a href="https://ello.co/errore">errore</a>,
35063#@gui : <a href="http://www.anudai.de">Anudai</a>,
35064#@gui : James Stalnaker,
35065#@gui : <a href="https://plus.google.com/u/0/b/117441237982283011318/112547676857320288448/about">Paolo Finetti</a>,
35066#@gui : Luigi Scarselli,
35067#@gui : <a href="https://patdavid.net/">Pat David</a>,
35068#@gui : Juan Jose Rodriguez Vela,
35069#@gui : Thomas Jakob,
35070#@gui : Kim Bartholomew,
35071#@gui : <a href="http://www.captivemoment.com">Sudi</a>,
35072#@gui : Michael Prostka,
35073#@gui : Arkadi Gelfond,
35074#@gui : <a href="https://joeysl.wordpress.com/">Sabine Schäfers</a>,
35075#@gui : <a href="http://www.viewbug.com/member/KennZaney">Bull O'Woods</a>,
35076#@gui : Jost Jakob Schaper,
35077#@gui : Dominik Wefers,
35078#@gui : Frank McLaughlin,
35079#@gui : <a href="https://29a.ch/">Jonas Wagner</a>,
35080#@gui : <a href="www.ixaarii.com">Void lon iXaarii</a>,
35081#@gui : Mark Boadey,
35082#@gui : Laura Haglund,
35083#@gui : Lee Elliott,
35084#@gui : Bernard Desenclos,
35085#@gui : Randy Gordon-Gilmore,
35086#@gui : Eddie Dedrick,
35087#@gui : <a href="http://mindprints.org/">Greg FitzPatrick</a>,
35088#@gui : Zsolt Szabo,
35089#@gui : Daniel Hanna,
35090#@gui : Peter Bengtsson,
35091#@gui : Diego Nassetti,
35092#@gui : William Tweedy,
35093#@gui : Shawnee Horn,
35094#@gui : Stephan Munsch,
35095#@gui : <a href="http://www.mysticali3n-wear.com">MysticAli3n-Wear</a>,
35096#@gui : Mika Mantere,
35097#@gui : Christian Beuschel,
35098#@gui : Tore Busch,
35099#@gui : Douc McGregor.
35100#@gui : Marcel Dahm,
35101#@gui : Susan Voitel,
35102#@gui : <a href="https://www.flickr.com/photos/henkkoning">Henk Koning</a>,
35103#@gui : Arnie Jordan,
35104#@gui : Carol Jennings,
35105#@gui : Sébastien Huart,
35106#@gui : <a href="http://www.jessstryker.com/">Jess Stryker</a>,
35107#@gui : Rui Luis,
35108#@gui : <a href="https://www.flickr.com/photos/sallesrenato/">Renato Salles</a>,
35109#@gui : <a href="http://www.viewbug.com/member/alef0"> Petr Zagalak</a>,
35110#@gui : <a href="http://www.antonio.cat">Antonio Vicién Faure</a>,
35111#@gui : Vincent Bermel,
35112#@gui : Christian Stocco,
35113#@gui : <a href="https://www.flickr.com/photos/136307651@N04/">Richard Benedict</a>,
35114#@gui : Dr. Helmut Jarausch,
35115#@gui : <a href="http://www.michaeljamesbeck.com">Michael Beck</a>,
35116#@gui : <a href="http://rickleone.tumblr.com/">Riccardo Leone</a>,
35117#@gui : Gisela Looram,
35118#@gui : <a href="https://plus.google.com/u/0/+FrankTegtmeyer/posts">Frank Tegtmeyer</a>,
35119#@gui : David Kettrey,
35120#@gui : <a href="https://www.youtube.com/user/kncpt1">Peter Hoge</a>,
35121#@gui : Alexander Heitmann,
35122#@gui : <a href="http://harlequin.webcomics.fr/page/episode-1-page-1">Olivier Larski</a>,
35123#@gui : <a href="http://victorfandrey.blogspot.ca">Victor Fandrey</a>,
35124#@gui : Stefan Peter,
35125#@gui : <a href="https://plus.google.com/u/0/+DimitriosPsychogios">Dimitrios Psychogios</a>,
35126#@gui : <a href="https://plus.google.com/+AnttiLuoma">Antti Luoma</a>,
35127#@gui : <a href="https://twitter.com/jeyoung">Eddy Young Tie Yang</a>,
35128#@gui : Thomas Elfstrom,
35129#@gui : Valentine Boyce,
35130#@gui : George Harnett,
35131#@gui : Darius Manka,
35132#@gui : Chris Knox,
35133#@gui : <a href="http://tomtappingphotoblog.blogspot.fr/">Thomas Tapping</a>,
35134#@gui : Phillip R Ziesemer,
35135#@gui : Jean Francois.
35136#@gui : Franz Ziereis,
35137#@gui : Alessandro Renzi,
35138#@gui : Tsuda Koshi,
35139#@gui : <a href="http://www.boxrec.com">Boxrec Ltd</a>,
35140#@gui : <a href="http://www.wolfgangschweizer.com/">Wolfgang Schweizer</a>,
35141#@gui : <a href="http://www.ramonmiranda.com/">Ramon Miranda</a>,
35142#@gui : Volker Bradley,
35143#@gui : <a href="http://plus.google.com/+MarcoZara">Marco Zara</a>,
35144#@gui : <a href="http://plus.google.com/+MarcoTedaldi">Marco Tedaldi</a>,
35145#@gui : <a href="http://cybertographer.com">Rodney Lee</a>,
35146#@gui : Konstantinos Blatzonis,
35147#@gui : Simon Chanson,
35148#@gui : Herbert Malle,
35149#@gui : <a href="http://www.matthias-zepper.de/">Matthias Zepper</a>,
35150#@gui : Christian Mariucci,
35151#@gui : M. R.,
35152#@gui : Mark Link,
35153#@gui : <a href="http://blog.meetthegimp.org/">Rolf Steinort</a>,
35154#@gui : <a href="https://plus.google.com/112357088505488756823/posts">Daniel Tauro</a>,
35155#@gui : <a href="http://geniisoft.com/">Ben Langhinrichs</a>,
35156#@gui : <a href="http://www.openlabs.it/">Paolo Pedaletti</a>,
35157#@gui : <a href="http://blog.photomontager.com">Ricardo Corin</a>,
35158#@gui : <a href="https://plus.google.com/115953666279509959258">James Prichard</a>,
35159#@gui : <a href="https://plus.google.com/116658221461047313647">Matt Jones</a>,
35160#@gui : <a href="http://www.flickr.com/people/twekkel/">Eddy Vervest</a>,
35161#@gui : <a href="http://www.flaviocdc.net/wiki/">Flavio Casadei Della Chiesa</a>,
35162#@gui : <a href="http://www.artwanted.com/artist.cfm?artid=10918">Lyle Kroll</a>.
35163#@gui : </i>"}
35164#@gui : sep = separator()
35165#@gui : note = note{"\n<span foreground="purple" underline="single">Postcard senders:</span>"}
35166#@gui : note = note{" <b>-</b> We've received <b>46</b> postcards from <b>G'MIC</b> enthusiasts so far.
35167#@gui :               You could be the <b>47rd</b> sender :)"}
35168#@gui : note = note{" <b>-</b> A big hug goes to these postcard senders (recently received first) :"}
35169#@gui : note = note{"<i>
35170#@gui : <a href="https://cimg.eu/img/postcard73.jpg">Benjamin Russell</a> (Portsmouth/USA),
35171#@gui : <a href="https://cimg.eu/img/postcard72.jpg">Andreas Weissenburger</a> (Bochum/Germany),
35172#@gui : <a href="https://cimg.eu/img/postcard70.jpg">Patrick Wanters</a> (USA),
35173#@gui : <a href="https://cimg.eu/img/postcard69.jpg">Josep Febrer</a> (Pregonda/Menorca),
35174#@gui : <a href="https://cimg.eu/img/postcard68.jpg">Richard Gledson</a> (Newcastle upon tyne/England),
35175#@gui : <a href="https://cimg.eu/img/postcard67.jpg">James Jaworski</a> (Winnipeg/Canada),
35176#@gui : <a href="https://cimg.eu/img/postcard66.jpg">Powlux</a> (France),
35177#@gui : <a href="https://cimg.eu/img/postcard65.jpg">Volker Doebel</a> (Haldern/Germany),
35178#@gui : <a href="https://cimg.eu/img/postcard64.jpg">Patrick Wauters</a> (Bilbao/Spain),
35179#@gui : <a href="https://cimg.eu/img/postcard63.jpg">Sebastien Fourey</a> (Konstanz/Germany),
35180#@gui : <a href="https://cimg.eu/img/postcard62.jpg">David Revoy</a> (Toulouse/France),
35181#@gui : <a href="https://cimg.eu/img/postcard61.jpg">Giulio Canevari</a> (Pavia/Italy),
35182#@gui : <a href="https://cimg.eu/img/postcard60.jpg">Bruno Steinbach</a> (Pondicherry/India),
35183#@gui : <a href="https://cimg.eu/img/postcard59.jpg">Steve Gillow</a> (Fort Worth/Texas/USA),
35184#@gui : <a href="https://cimg.eu/img/postcard58.jpg">Peter Neave</a> (Sydney/Australia),
35185#@gui : <a href="https://cimg.eu/img/postcard57.jpg">Andrea [Photoflow]</a> (Italy),
35186#@gui : <a href="https://cimg.eu/img/postcard56.jpg">Garry R. Osgood</a> (New York/USA),
35187#@gui : <a href="https://cimg.eu/img/postcard55.jpg">Justin Pletzfeld</a> (Germany),
35188#@gui : <a href="https://cimg.eu/img/postcard54.jpg">Werner Meier</a> (Germany),
35189#@gui : <a href="https://cimg.eu/img/postcard53.jpg">Patrick Wauters</a> (Roma/Italy),
35190#@gui : <a href="https://cimg.eu/img/postcard52.jpg">Marc Lis</a> (Belgium),
35191#@gui : <a href="https://cimg.eu/img/postcard51.jpg">ZondeR</a> (France),
35192#@gui : <a href="https://cimg.eu/img/postcard50.jpg">Bill C.</a> (USA),
35193#@gui : <a href="https://cimg.eu/img/postcard49.jpg">Michael T.</a> (France),
35194#@gui : <a href="https://cimg.eu/img/postcard48.jpg">Patrick Wauters</a> (Lisboa),
35195#@gui : <a href="https://cimg.eu/img/postcard47.jpg">Akky [Gimpchat]</a> (Australia),
35196#@gui : <a href="https://cimg.eu/img/postcard45.jpg">Michel Thomas</a> (Germany),
35197#@gui : <a href="https://cimg.eu/img/postcard44.jpg">Pierre-Yves</a> (Ile de Batz/France),
35198#@gui : <a href="https://cimg.eu/img/postcard43.jpg">Family Hamacher</a> (Trier/Germany),
35199#@gui : <a href="https://cimg.eu/img/postcard41.jpg">Benoit Gauzere and Francois Lozes</a> (Hokusai/Japan),
35200#@gui : <a href="https://cimg.eu/img/postcard40.jpg">Dr. Rainer Teubner</a> (Seligenstadt/Germany),
35201#@gui : <a href="https://cimg.eu/img/postcard39.jpg">Mauro Mitrino</a> (Mantova/Italy),
35202#@gui : <a href="https://cimg.eu/img/postcard37.jpg">Werner Meier</a> (Mettlach/Germany),
35203#@gui : <a href="https://cimg.eu/img/postcard36.jpg">Arto Huotari</a> (Helsinki/Finland),
35204#@gui : <a href="https://cimg.eu/img/postcard33.jpg">Benoit Gauzere</a> (California/USA),
35205#@gui : <a href="https://cimg.eu/img/postcard30.jpg">Arkadi Gelfond</a> (Foster City - California/USA),
35206#@gui : <a href="https://cimg.eu/img/postcard29.jpg">Corinne Masimann</a> (Neuchatel/Switzerland),
35207#@gui : <a href="https://cimg.eu/img/postcard27.jpg">Mahvin</a> (Portland/USA),
35208#@gui : <a href="https://cimg.eu/img/postcard26.jpg">Vincent Roullier</a> (Caen/France),
35209#@gui : <a href="https://cimg.eu/img/postcard24.jpg">M????</a> (Munich/Germany),
35210#@gui : <a href="https://cimg.eu/img/postcard23.jpg">F. Albior</a> (Jaca/Spain),
35211#@gui : <a href="https://cimg.eu/img/postcard22.jpg">PhotoComIX</a> (Frascati/Italy),
35212#@gui : <a href="https://cimg.eu/img/postcard21.jpg">Guy Poizat</a> (Cabestany/France),
35213#@gui : <a href="https://cimg.eu/img/postcard20.jpg">Institut for Biomathematik und Biometrie</a> (Neuherberg/Germany),
35214#@gui : <a href="https://cimg.eu/img/postcard15.jpg">Jean-Michel Webbe</a> (Guadeloupe/France),
35215#@gui : <a href="https://cimg.eu/img/postcard14.jpg">Jaime</a> (Barcelona/Spain).
35216#@gui : </i>"}
35217#@gui : sep = separator()
35218#@gui : note = note{"\nMay the force be with you!"}
35219fx_friends :
35220  if $! ratio={w/h} else ratio=1 fi
35221  rm _heart80x73 scale3x r 150%,150%,1,1,0,0,0.5,0.5
35222  +*. 70 +*.. 110 +*... 255 *[-4] 255 a c
35223  blur_radial 4 sharpen 300
35224  i.. ${fitratio_wh\ {w},{h},$ratio},1,3
35225  rand.. 0,255 sh.. 1,2 /. 2 rm.
35226  blur_radial.. 20 sharpen.. 50
35227  r. ..,..,1,4,0,0,0.5,0.5 blend alpha
35228  143,80,1,1,0 t. "Greetings to\n  all G\47MIC\n  friends!",2,-2,27,1,1
35229  +dilate. 3 *.. 255 to_rgb.. j... ..,{[w#-3-w#-2,h#-3-h#-2]/2},0,0,1,.
35230  rm[-2,-1]
35231
35232_heart80x73 :
35233  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
35234
35235#@gui Gmicky - Roddy : fx_gmicky, fx_gmicky_preview
35236#@gui : Mascot Image = choice{"Gmicky (by Deevad)","Gmicky (by Mahvin)","Gmicky & Wilber (by Mahvin)",
35237#@gui : "Roddy (by Mahvin)"}
35238#@gui : sep = separator()
35239#@gui : note = note{"<b><i>Gmicky</i></b> is the name of the <b>G'MIC</b> mascot.
35240#@gui : He is a small and cute tiger who knows how to do magic.
35241#@gui : <b><i>Gmicky</i></b> is a tiger, i.e. fast, agile and elegant, just as the <b>G'MIC</b> code is :).
35242#@gui : As many magicians, <b><i>Gmicky</i></b> knows lot of <b>gimmicks</b>,
35243#@gui : and he is a direct and friendly companion of
35244#@gui : the ImageMagick's wizard, or the GraphicMagick's frog."}
35245#@gui : note = note{"<b><i>Roddy</i></b> is another mascot designed specifically for the
35246#@gui : <i>Artistic / Rodilius</i> filter of <b>G'MIC</b>.\n"}
35247#@gui : note = note{"<b><i>Gmicky</i></b> and <b><i>Roddy</i></b> have been both created and drawn by "}
35248#@gui : url = link("Mahvelous Mahvin","http://www.mahvin.com/")
35249#@gui : note = note{"and"}
35250#@gui : url = link{"David Revoy (Deevad)","http://www.davidrevoy.com/"}
35251fx_gmicky :
35252  rm
35253  if $1==0 sp gmicky nm "name(Gmicky)"
35254  elif $1==1 sp gmicky_mahvin nm "name(Gmicky)"
35255  elif $1==2 sp gmicky_wilber nm "name(Gmicky & Wilber)"
35256  else sp roddy nm "name(Roddy)"
35257  fi
35258
35259fx_gmicky_preview :
35260  fx_gmicky $* rr2d $_preview_width,$_preview_height,0,2
35261
35262#@gui Privacy Notice : _none_, _none_
35263#@gui : note = note{"This plugin may download up-to-date filter definitions from the
35264#@gui : <a href="https://gmic.eu">gmic.eu</a> server.\n\n
35265#@gui : It is the case when first launched after a fresh installation, and periodically
35266#@gui : with a frequency which can be set in the settings dialog.
35267#@gui : The user should be aware that the following information may be retrieved
35268#@gui : from the server logs: <i>IP address of the client; date and time of the request;</i>
35269#@gui : as well as a short string, supplied through the HTTP protocol <i>"User Agent"</i> header
35270#@gui : field, which describes the full plugin version as shown in the window title
35271#@gui : (e.g. "<i>G'MIC-Qt for GIMP 2.8 - Linux 64 bits - 2.2.1_pre#180301</i>").\n\n
35272#@gui : Note that this information may solely be used for purely anonymous
35273#@gui : statistical purposes.
35274#@gui : "}
35275#@gui : sep = separator()
35276#@gui : note = note("<small>Author: <i>Sébastien Fourey</i>.      Latest Update: <i>2018/03/01</i>.</small>")
35277
35278#@gui Release Notes : _none_, _none_
35279#@gui : note = note{"
35280#@gui : - <b>2009/01/13</b> : version <i>1.3.0</i> (initial plug-in release).\n
35281#@gui : - <b>2010/09/03</b> : version <i>1.4.0</i>.\n
35282#@gui : - <b>2011/07/07</b> : version <i>1.5.0</i>.\n
35283#@gui : - <b>2014/08/20</b> : version <i>1.6.0</i>.\n
35284#@gui : - <b>2016/03/25</b> : version <i>1.7.0</i>.\n
35285#@gui : - <b>2017/05/29</b> : version <i>2.0.0</i>.\n
35286#@gui : - <b>2017/10/09</b> : version <i>2.1.0</i>.\n
35287#@gui : - <b>2018/02/15</b> : version <i>2.2.0</i>.\n
35288#@gui : - <b>2018/06/21</b> : version <i>2.3.0</i>.\n
35289#@gui : - <b>2018/10/04</b> : version <i>2.4.0</i>.\n
35290#@gui : - <b>2019/03/15</b> : version <i>2.5.0</i>.\n
35291#@gui : - <b>2019/04/29</b> : version <i>2.6.0</i>.\n
35292#@gui : - <b>2019/08/14</b> : version <i>2.7.0</i>.\n
35293#@gui : - <b>2019/12/04</b> : version <i>2.8.0</i>.\n
35294#@gui : - <b>2020/03/28</b> : version <i>2.9.0</i>.\n
35295#@gui : - <span foreground="purple"><b>2021/12/09</b> : version <i>3.0.0</i> (Current stable).</span>\n
35296##@gui : - <b>2021/10/05</b> : version <i>3.0.0</i> (Current pre-release).\n
35297#@gui : "}
35298#@gui : sep = separator()
35299##@gui : url = link{"View changelog to upcoming major version (3.0)","https://discuss.pixls.us/t/on-the-road-to-3-0"}
35300##@gui : url = link{"View latest minor changelog (2.9)","https://discuss.pixls.us/t/release-of-gmic-2-9"}
35301#@gui : url = link{"View latest major changelog (3.0)","https://discuss.pixls.us/t/release-of-gmic-3-0-0"}
35302
35303#@gui What's New? : _none_,fx_whatsnew_preview
35304#@gui : note = note("Here you'll find a list of filter additions and deletions in the plug-in, since your last visit.
35305#@gui : When you have seen what's new, press the <b>Got It!</b> button to reset the list of changes.")
35306#@gui : sep = separator()
35307#@gui : nochange = value(0)_0+
35308#@gui : note = note("\nThere have been no changes since your last visit.\n\n")
35309#@gui : New Filters = text(1,"")_0
35310#@gui : Removed Filters = text(1,"")_0
35311#@gui : Got it! = button(0.5)_0+
35312#@gui : sep = separator()
35313#@gui : note = note("<small>Authors: <i>David Tschumperlé</i>.
35314#@gui :       Latest Update: <i>2021/01/18</i>.</small>")
35315fx_whatsnew_preview : skip "$*"
35316  file=${-path_cache}whatsnew.txt
35317  l[]
35318    parse_gui whatsnew
35319    if isfile(['{/$file}']) it $file date={date([0,1,2],['{/$file}']):/} else 0 date={date([0,1,2]):/} fi
35320    utf82html.. utf82html.
35321    if $4 ot.. $file newfilters,delfilters= is_changes=0
35322    else
35323
35324      # Check for changes.
35325      newfilters,delfilters=
35326      +- is_changes={max(abs(im),abs(iM))!=0} rm.
35327
35328      if !w # First call to the filter
35329        rm. s -,10
35330        repeat $! newfilters.=" - "{$>,t}{`10`} done
35331
35332      elif $is_changes # Estimate changes
35333        eval "
35334          ref(vector256(),str0);
35335          ref(vector256(),str1);
35336          for (s0 = s1 = 0, s0<h#0 && s1<h#1,
35337            e0 = find(#0,_'\n',s0)%h#0;
35338            e1 = find(#1,_'\n',s1)%h#1;
35339            str0 = 0; copy(str0,i[#0,s0],e0 - s0);
35340            str1 = 0; copy(str1,i[#1,s1],e1 - s1);
35341            ref(lowercase(str0) - lowercase(str1),diff);
35342            for (l = 0, l<size(diff) && !diff[l], ++l);
35343            cmp = diff[l];
35344            !cmp?( # Same strings
35345              s0 = e0 + 1;
35346              s1 = e1 + 1;
35347            ):cmp<0?( # Added filter
35348              run('(\'\"',str0,'\"\') html2utf8. newfilters.=\" - \"{t}{`10`} rm.');
35349              s0 = e0 + 1;
35350            ):( # Deleted filter
35351              run('(\'\"',str1,'\"\') html2utf8. delfilters.=\" - \"{t}{`10`} rm.');
35352              s1 = e1 + 1;
35353            );
35354          )"
35355      fi
35356    fi
35357    rm
35358  endl
35359  u "{0}"_{$is_changes?0:2}\
35360    "{"{``$newfilters}"}"_{['$newfilters']!=0?2:0}\
35361    "{"{``$delfilters}"}"_{['$delfilters']!=0?2:0}\
35362    "{0}_2"
35363
35364#@gui ____<b>Arrays & Tiles</b>
35365#-------------------------------
35366
35367#@gui Array [Faded] : fx_array_fade, fx_array_fade_preview(1)
35368#@gui : X-Tiles = int(2,1,10)
35369#@gui : Y-Tiles = int(2,1,10)
35370#@gui : X-Offset (%) = float(0,0,100)
35371#@gui : Y-Offset (%) = float(0,0,100)
35372#@gui : Fade Start (%) = float(80,1,100)
35373#@gui : Fade End (%) = float(90,1,100)
35374#@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes")
35375#@gui : Size = _choice("Shrink", "Expand", "Repeat [Memory Consuming!]")
35376#@gui : sep = separator()
35377#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35378fx_array_fade :
35379  if $7&1 mirror x fi if $7>1 mirror y fi
35380  array_fade $1,$2,$5,$6,$8
35381  shift -$3%,-$4%,0,0,2
35382
35383fx_array_fade_preview :
35384  fx_array_fade $1,$2,$3,$4,$5,$6,$7,0
35385
35386#@gui Array [Mirrored] : fx_array_mirror, fx_array_mirror_preview(1)
35387#@gui : Iterations = int(1,1,10)
35388#@gui : X-Offset (%) = float(0,0,100)
35389#@gui : Y-Offset (%) = float(0,0,100)
35390#@gui : Array Mode = choice(2,"X-Axis","Y-Axis","XY-Axes","2XY-Axes")
35391#@gui : Initialization = choice("Original","Mirror X","Mirror Y","Rotate 90 deg.","Rotate 180 deg.","Rotate 270 deg.")
35392#@gui : Expand Size = _bool(false)
35393#@gui : Crop (%) = int(0,0,100)
35394#@gui : sep = separator()
35395#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35396fx_array_mirror : skip ${7=0}
35397  if $5==1 mirror x
35398  elif $5==2 mirror y
35399  elif $5==3 rotate 90
35400  elif $5==4 rotate 180
35401  elif $5==5 rotate 270
35402  fi
35403  if $7
35404    if $4==0 columns 0,{100-$7}%
35405    elif $4==1 rows 0,{100-$7}%
35406    elif $4==2 z 0,0,{100-$7}%,{100-$7}%
35407    elif $4==3 z {$7/2}%,{$7/2}%,{100-$7/2}%,{100-$7/2}%
35408    fi
35409  fi
35410  shift -$2%,-$3%,0,0,2
35411  array_mirror $1,$4,$6
35412
35413fx_array_mirror_preview :
35414  fx_array_mirror $1,$2,$3,$4,$5,0,$7
35415
35416#@gui Array [Random] : array_random, array_random(1)
35417#@gui : Source X-Tiles = int(5,1,20)
35418#@gui : Source Y-Tiles = int(5,1,20)
35419#@gui : Destination X-Tiles = int(7,1,20)
35420#@gui : Destination Y-Tiles = int(7,1,20)
35421#@gui : sep = separator()
35422#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35423
35424#@gui Array [Random Colors] : fx_array_color, fx_array_color(1)
35425#@gui : X-Tiles = int(5,1,20)
35426#@gui : Y-Tiles = int(5,1,20)
35427#@gui : Opacity = float(0.5,0,1)
35428#@gui : sep = separator()
35429#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35430fx_array_color :
35431  repeat $! l.
35432    $1,$2,1,3 rand. 0,255 to_colormode. {-2,s} ri. .. *. $3 *.. {1-$3} +[-2,-1]
35433  endl mv. 0 done
35434
35435#@gui Array [Regular] : fx_array, fx_array_preview(1)
35436#@gui : X-Tiles = int(2,1,10)
35437#@gui : Y-Tiles = int(2,1,10)
35438#@gui : X-Offset (%) = float(0,0,100)
35439#@gui : Y-Offset (%) = float(0,0,100)
35440#@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes")
35441#@gui : Size = _choice("Shrink", "Expand", "Repeat [Memory Consuming!]")
35442#@gui : sep = separator()
35443#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35444fx_array :
35445  shift -$3%,-$4%,0,0,2
35446  if $5&1 mirror x fi if $5>1 mirror y fi
35447  array $1,$2,$6
35448
35449fx_array_preview :
35450  fx_array $1,$2,$3,$4,$5,0
35451
35452#@gui Ascii Art : fx_asciiart, fx_asciiart_preview(0)+
35453#@gui : Charset = choice(5,"Custom","Binary Digits","Digits","Lowercase Letters","Uppercase Letters",
35454#@gui : "Ascii","Card Suits","Math Symbols")
35455#@gui : Custom Dictionary = text{" .oO0"}
35456#@gui : Analysis Scale = int(16,8,103)
35457#@gui : Analysis Smoothness = float(15,0,100)
35458#@gui : Synthesis Scale = int(16,8,103)
35459#@gui : Result Type = choice(2,"White on Black","Black on White","Colored on Black","Colored on Transparent")
35460#@gui : sep = separator()
35461#@gui : Gamma = float(0,-3,3)
35462#@gui : Smoothness = float(0.2,0,5)
35463#@gui : Colors = choice("Full Colors","2 Colors","3 Colors","4 Colors","8 Colors","12 Colors","16 Colors",
35464#@gui :                 "Grayscale","2 Grays","3 Grays","4 Grays","8 Grays","12 Grays","16 Grays")
35465#@gui : sep = separator()
35466#@gui : Output Ascii File = _bool(0)
35467#@gui : Output Folder = _folder()
35468#@gui : Output Filename = _text("gmic_asciiart.txt")
35469#@gui : sep = separator()
35470#@gui : url = link("Click here for a detailed description of this filter.",\
35471# "http://www.gimpchat.com/viewtopic.php?f=28&t=10047")
35472#@gui : sep = separator()
35473#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/27/03</i>.</small>")
35474fx_asciiart : skip "${10=},${11=},${12=}"
35475  repeat $! l[$>] to_rgb
35476    apply_gamma {10^$7} b $8% n 0,255
35477    if $1==0 dict="$2"
35478    elif $1==1
35479      dict=" 01"
35480    elif $1==2
35481      dict=" 0123456789"
35482    elif $1==3
35483      dict=" abcdefghijklmnopqrstuvwxyz"
35484    elif $1==4
35485      dict=" ABCDEFGHIJKLMNOPQRSTUVWXYZ"
35486    elif $1==5
35487      dict=" !\042#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\133\\\135^_\140abcdefghijklmnopqrstu"\
35488           "vwxyz\173|\174~"
35489    elif $1==6
35490      dict=" \16\17\20\21"
35491    elif $1==7
35492      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"
35493    fi
35494    if $6==1 negate fi
35495    if $-3 +img2ascii $dict,$3,$4%,$5,"$-2"/"$-1"
35496    else +img2ascii $dict,$3,$4%,$5
35497    fi
35498    wh=${}
35499    if $6==0 k. n 0,255
35500    elif $6==1 k. negate n 0,255
35501    elif $6==2" || "$6==3
35502      r[0] $wh,1,100%,1
35503      if $9>=7 luminance[0] fi
35504      if $9%7 quantize[0] {arg($9%7,2,3,4,8,12,16)-1},1,0 fi
35505      r[0] [1],[1],1,100% *[0] [1]
35506      if $6==2 rm[1]
35507      else *[1] 255 a c
35508      fi
35509    fi
35510  endl done
35511
35512fx_asciiart_preview :
35513  repeat $! l[$>]
35514    w={w} h={h}
35515    fx_asciiart $1,"$2",${3-9},0,foo,foo
35516    r $w,$h,1,100%,0,0,0.5,0.5
35517  endl done
35518
35519#@gui Chessboard : fx_chessboard, fx_chessboard_preview(0)
35520#@gui : First Size = int(64,1,512)
35521#@gui : Second Size = int(64,1,512)
35522#@gui : First Offset = int(0,0,512)
35523#@gui : Second Offset = int(0,0,512)
35524#@gui : Angle = float(0,0,180)
35525#@gui : Opacity = float(0.5,0,1)
35526#@gui : First Color = color(0,0,0,255)
35527#@gui : Second Color = color(255,255,255,255)
35528#@gui : sep = separator()
35529#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35530#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35531#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35532#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35533#@gui : sep = separator()
35534#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35535fx_chessboard :
35536  to_rgba chessboard ${1-14}
35537
35538fx_chessboard_preview :
35539  gui_split_preview "fx_chessboard $*",${-3--1}
35540
35541#@gui Dices : fx_dices, fx_dices(0)
35542#@gui : Resolution = float(2,1,10)
35543#@gui : Size = int(24,8,64)
35544#@gui : Color Model = choice(1,"Black Dices","White Dices","Dices with Colored Numbers","Dices with Colored Sides")
35545#@gui : sep = separator()
35546#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/06</i>.</small>")
35547fx_dices :
35548
35549    # Create dice patterns.
35550    repeat 6 {2*$2},{2*$2} _dice$> done
35551    if $3%2 negate[-6--1] fi
35552    frame_round[-6--1] 10,10,0,0,128,128,128,0
35553    r2dy[-6--1] $2 a[-6--1] x
35554
35555    repeat $!-1 l[$>,-1]
35556
35557      # Prepare input images.
35558      +luminance[0] rv[1,2] r[0,1] {100*$1/$2}%,{100*$1/$2}%,1,100%,2 quantize[1] 6,0
35559
35560      # Convert input image to dices pattern.
35561      *.. $2 channels.. 0,1 r.. {$2*100}%,{$2*100}%
35562      $2,$2,1,2,'if(c,y,x)' r. ...,...,1,2,0,2 +[-3,-1] +warp. ..,0,0 rm...
35563
35564      if $3<2 rm[0] mv. 0
35565      else r[0] [2],[2],1,100% rv[0,-1] blend[0,-1] multiply
35566      fi
35567
35568    endl done rm.
35569
35570_dice0 : ellipse. 50%,50%,5.2%,5.2%,0,1,255
35571_dice1 : ellipse. 25%,25%,5.2%,5.2%,0,1,255 ellipse. 75%,75%,5.2%,5.2%,0,1,255
35572_dice2 : _dice1 _dice0
35573_dice3 : _dice1 ellipse. 25%,75%,5.2%,5.2%,0,1,255 ellipse. 75%,25%,5.2%,5.2%,0,1,255
35574_dice4 : _dice3 _dice0
35575_dice5 : _dice3 ellipse. 25%,50%,5.2%,5.2%,0,1,255 ellipse. 75%,50%,5.2%,5.2%,0,1,255
35576
35577#@gui Drawn Montage : fx_drawn_montage, fx_drawn_montage_preview(1) : *
35578#@gui : Layer = choice("1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th","11th","12th",
35579#@gui : "13th","14th","15th","16th")
35580#@gui : Associated Color = color(0,0,0)
35581#@gui : Zoom = float(-10,0,10)
35582#@gui : X-Centering (%) = float(50,0,100)
35583#@gui : Y-Centering (%) = float(50,0,100)
35584#@gui : Angle = choice("0 deg.","90 deg.","180 deg.","270 deg.")
35585#@gui : Pargs = value(-1)
35586#@gui : Args0 = value(0:0:0:0:50:50:0)
35587#@gui : Args1 = value(0:0:0:0:50:50:0)
35588#@gui : Args2 = value(0:0:0:0:50:50:0)
35589#@gui : Args3 = value(0:0:0:0:50:50:0)
35590#@gui : Args4 = value(0:0:0:0:50:50:0)
35591#@gui : Args5 = value(0:0:0:0:50:50:0)
35592#@gui : Args6 = value(0:0:0:0:50:50:0)
35593#@gui : Args7 = value(0:0:0:0:50:50:0)
35594#@gui : Args8 = value(0:0:0:0:50:50:0)
35595#@gui : Args9 = value(0:0:0:0:50:50:0)
35596#@gui : Args10 = value(0:0:0:0:50:50:0)
35597#@gui : Args11 = value(0:0:0:0:50:50:0)
35598#@gui : Args12 = value(0:0:0:0:50:50:0)
35599#@gui : Args13 = value(0:0:0:0:50:50:0)
35600#@gui : Args14 = value(0:0:0:0:50:50:0)
35601#@gui : Args15 = value(0:0:0:0:50:50:0)
35602#@gui : sep = separator()
35603#@gui : note = note{"<small><b>Note:</b>
35604#@gui : This filter requires a top layer containing the desired montage layout defined as free-form shapes
35605#@gui : of different colors. You can then assign each layer to a layout color to create the montage.
35606#@gui : </small>"}
35607#@gui : sep = separator()
35608#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/29</i>.</small>")
35609_fx_drawn_montage :
35610  nb_layers={min(16,$!-1)}
35611
35612  if !$nb_layers
35613    error="A top layout layer is missing for this filter to make it work properly!"
35614    if $-1 gui_warning_preview $error return
35615    else error $error
35616    fi
35617  fi
35618
35619  # Manage parameter changes.
35620  pargs,args0,args1,args2,args3,args4,args5,args6,args7,args8,args9,args10,args11,args12,args13,args14,args15=${9-25}
35621  if $1!=$pargs # Get back saved parameters for selected layer
35622    ('${args$1}') replace. {':'},{','} arg_R,arg_G,arg_B,arg_zoom,arg_xcenter,arg_ycenter,arg_angle=${u\ {t}} rm.
35623  else # Set new parameters for selected #L
35624    arg_R,arg_G,arg_B,arg_zoom,arg_xcenter,arg_ycenter,arg_angle=${2-8}
35625  fi
35626  args$1=$arg_R:$arg_G:$arg_B:$arg_zoom:$arg_xcenter:$arg_ycenter:$arg_angle
35627  status=\{$1\}\{$arg_R,$arg_G,$arg_B\}\{$arg_zoom\}\{$arg_xcenter\}\{$arg_ycenter\}\{$arg_angle\}\{$1\}\{$args0\}\
35628         \{$args1\}\{$args2\}\{$args3\}\{$args4\}\{$args5\}\{$args6\}\{$args7\}\{$args8\}\{$args9\}\{$args10\}\
35629         \{$args11\}\{$args12\}\{$args13\}\{$args14\}\{$args15\}
35630
35631  # Read parameters for all layers.
35632  c= repeat $nb_layers
35633    ('${args$>}') replace. {':'},{','} R$>,G$>,B$>,zoom$>,xcenter$>,ycenter$>,angle$>=${u\ {t}}
35634    cols=$cols$c${R$>},${G$>},${B$>} c=, rm. rotate[{1+$>}] {90*${angle$>}}
35635  done
35636
35637  # Get bounding boxes for each associated color in layout layer, as well as labeled layout layer.
35638  to_rgb[0] to_rgba[^0] $nb_layers,1,1,4,[inf,inf,-inf,-inf] [0],[0]
35639  f[0] "
35640    begin(colors = [ "$cols" ]);
35641    repeat ("$nb_layers",l,
35642      I==colors[3*l,3]?(
35643        if (x<i(#-2,l,0,0,0), i(#-2,l,0,0,0) = x);
35644        if (y<i(#-2,l,0,0,1), i(#-2,l,0,0,1) = y);
35645        if (x>i(#-2,l,0,0,2), i(#-2,l,0,0,2) = x);
35646        if (y>i(#-2,l,0,0,3), i(#-2,l,0,0,3) = y);
35647        i(#-1,x,y) = l + 1;
35648      );
35649    ); I"
35650
35651  thumbnail=0 boundaries=0
35652  if $-1
35653
35654    # Render thumbnail of current layer, for preview.
35655    l={1+$1}
35656    if $l<=$nb_layers
35657      if {$l,w>h} +r2dx[$l] {(0$_preview_width?0$_preview_width:400)/6}
35658      else +r2dy[$l] {(0$_preview_height?0$_preview_height:400)/6} fi
35659      frame. 1,1,0,0,0,255 drgba. to. "#"$l,0,-2,13 to_rgba.
35660      mv. -3
35661      thumbnail=1
35662    fi
35663
35664    # Render region boundaries of layout layer, for preview.
35665    +f. "const boundaries = 1; ref(crop(x - 2,y - 2,5,5),V); if (min(V)==max(V),0,i)"
35666    (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.
35667
35668    repeat $nb_layers
35669      xmin,ymin,xmax,ymax={-3,I[$>]}
35670      if !isinf($xmin)" && "!isinf($ymin)" && "!isinf($xmax)" && "!isinf($ymax)
35671        xc$>,yc$>={0,0.5*([$xmin,$ymin]+[$xmax,$ymax]-6)*100/[w,h]}
35672      else xc$>,yc$>=-1024
35673      fi
35674    done
35675
35676    mv. -3
35677    boundaries=1
35678  fi
35679
35680  # Render montage.
35681  if iM>0
35682
35683    # Resize layers to fit proposed layout.
35684    repeat $nb_layers
35685      l={$>+1} xmin,ymin,xmax,ymax={-2,I[$>]}
35686      if !isinf($xmin)" && "!isinf($ymin)" && "!isinf($xmax)" && "!isinf($ymax)
35687        dx={$xmax-$xmin+1}
35688        dy={$ymax-$ymin+1}
35689        nw,nh={$l,round(min(w/$dx,h/$dy)*[$dx,$dy]/(1+3*(${zoom$>}/10)^2))}
35690        r[$l] $nw,$nh,1,100%,0,0,{(100-${xcenter$>})%},{(100-${ycenter$>})%}  # Centered crop
35691        r[$l] $dx,$dy,1,100%,6                                                # Resize
35692      else r[$l] 1,1 # Save a bit of memory!
35693      fi
35694    done
35695
35696    # Draw image pixels over layout layer.
35697    to_rgba[0]
35698    f[0] "
35699      l = i#-1;
35700      l1 = l - 1;
35701      l<=0?I:(
35702        xmin = i(#-2,l1,0,0,0);
35703        ymin = i(#-2,l1,0,0,1);
35704        rx = x - xmin;
35705        ry = y - ymin;
35706        crop(#l,rx,ry,0,0,1,1,1,4);
35707      )"
35708  fi
35709  rm[-2,-1]
35710
35711  # Manage preview.
35712  if $boundaries blend[0,-1] alpha fi # Add shape boundaries.
35713  if $thumbnail # Add current layer thumbnail over preview
35714    drgba[0] rr2d[0] ${-gui_preview_wh}
35715    j[0] .,3,3,0,0,0.75
35716    rm.
35717  fi
35718  if $-1 repeat $nb_layers
35719    to "#"{1+$>},${xc$>}%,${yc$>}%,13,1,0.75
35720  done fi
35721
35722
35723  k[0] c 0,255
35724  u $status
35725
35726fx_drawn_montage :
35727  _fx_drawn_montage $*,0
35728
35729fx_drawn_montage_preview :
35730  _fx_drawn_montage $*,1
35731
35732#@gui Extract Objects : fx_extract_objects, fx_extract_objects_preview(1)
35733#@gui : Background Point (%) = point(0,0)
35734#@gui : sep = separator()
35735#@gui : Color Tolerance = int(20,0,256)
35736#@gui : Opacity Threshold (%) = int(50,0,100)
35737#@gui : Minimal Area = float(0.3,0,5)
35738#@gui : Connectivity = choice("Low","High")
35739#@gui : Output As = _choice(0,"Crop","Segmentation")
35740#@gui : sep = separator()
35741#@gui : Preview Guides = bool(1)
35742#@gui : sep = separator()
35743#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/23/02</i>.</small>")
35744#@gui : url = link("Filter explained here","http://gimpchat.com/viewtopic.php?f=28&t=7905")
35745fx_extract_objects :
35746  if $5 min_area=$5% else min_area=6 fi
35747  repeat $! l[$<] to_rgba
35748    nm=${-gui_layer_name}
35749    w={w} h={h}
35750    x={$1%*(w-1)}
35751    y={$2%*(h-1)}
35752    color={I($x,$y)}
35753    if $7==0 # Output: Crop.
35754      +replace_color $3,0,$color,0,0,0,0 autocrop_components. $4%,$min_area,$6,2
35755      repeat w
35756        +z[0] {1,i($>,0)},{1,i($>,1)},{1,i($>,3)},{1,i($>,4)}
35757        nm. pos({1,i($>,0)},{1,i($>,1)}),name($nm" "[$>])
35758      done rm[0,1]
35759    elif $7==1 # Output: Segmentation.
35760      replace_color $3,0,$color,0,0,0,0
35761      +autocrop_components[0] $4%,$min_area,$6,2
35762      autocrop_components[0] $4%,$min_area,$6,1
35763      repeat w nm[$>] pos({i($>,0)},{i($>,1)}),name($nm" "[$>]) done rm.
35764    fi
35765    $w,$h,1,4 fc. $color nm. name($nm" [background]")
35766  endl done
35767
35768fx_extract_objects_preview :
35769  x0,y0=${1,2}
35770  if $5 min_area=$5% else min_area=5 fi
35771  repeat $! l[$>] to_rgba
35772    x={$x0%*(w-1)}
35773    y={$y0%*(h-1)}
35774    color={I($x,$y)}
35775    +replace_color $3,0,$color,0,0,0,0
35776    autocrop_components. $4%,$min_area,$6,2
35777    repeat w
35778      xycoords={1,i($>,0)},{1,i($>,1)},{1,i($>,3)},{1,i($>,4)}
35779      rectangle[0] $xycoords,0.3,0,0,255,255
35780      rectangle[0] $xycoords,1,0xFFFFFFFF,0,0,0,255
35781    done
35782    drgba[0]
35783    to[0] {w}" objects",2,2,13,2,0.3,255,255,255,255
35784    k[0]
35785    if $8
35786      line 0,$y0%,100%,$y0%,0.5,0xF0F0F0F0,255 line 0,$y0%,100%,$y0%,0.5,0x0F0F0F0F,0
35787      line $x0%,0,$x0%,100%,0.5,0xF0F0F0F0,255 line $x0%,0,$x0%,100%,0.5,0x0F0F0F0F,0
35788    fi
35789    circle $x,$y,3,1,0,255,0 circle $x,$y,3,1,0xFFFFFFFF,0
35790  endl done
35791
35792#@gui Grid [Cartesian] : fx_imagegrid, fx_imagegrid(0)
35793#@gui : X-Size = int(10,2,512)
35794#@gui : Y-Size = int(10,2,512)
35795#@gui : sep = separator()
35796#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
35797fx_imagegrid :
35798  imagegrid $1,$2
35799
35800#@gui Grid [Hexagonal] : fx_imagegrid_hexagonal, fx_imagegrid_hexagonal(1)
35801#@gui : Resolution = int(32,1,128)
35802#@gui : Outline = float(0.1,0,0.5)
35803#@gui : Anti-Aliasing = bool(1)
35804#@gui : sep = separator()
35805#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/12/01</i>.</small>")
35806fx_imagegrid_hexagonal :
35807  repeat $! l[$>]
35808    if $3 r 200%,200%,1,100% fi
35809    imagegrid_hexagonal $1,$2
35810    if $3 r 50%,50%,1,100%,2 fi
35811  endl done
35812
35813#@gui Grid [Triangular] : fx_imagegrid_triangular, fx_imagegrid_triangular(0)
35814#@gui : Pattern Width = int(10,8,128)
35815#@gui : Pattern Height = int(18,8,128)
35816#@gui : Pattern Type = choice(0,"Horizontal","Vertical","Crossed","Cube","Decreasing","Increasing")
35817#@gui : Outline Color = color(255,255,255,128)
35818#@gui : sep = separator()
35819#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/08/07</i>.</small>")
35820fx_imagegrid_triangular :
35821  repeat $! l[$>] split_opacity l[0] to_rgb
35822    imagegrid_triangular ${1-3},{$7/255},${4-6}
35823  endl a c endl done
35824
35825#@gui Make Seamless [Diffusion] : fx_make_seamless, fx_make_seamless_preview(1)
35826#@gui : Equalize Light = float(0,0,100)
35827#@gui : sep = separator()
35828#@gui : Preview Original = bool(0)
35829#@gui : Tiled Preview = choice(3,"None","2x1","1x2","2x2","3x3","4x4")
35830#@gui : sep = separator()
35831#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35832#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35833#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35834#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35835#@gui : sep = separator()
35836#@gui : note = note{"<small><b>Note:</b> This filter helps in converting your input pattern as a <b>seamless</b>
35837#@gui : (a.k.a periodic) texture.</small>"}
35838#@gui : sep = separator()
35839#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/24/02</i>.</small>")
35840fx_make_seamless :
35841  repeat $! l[$>]
35842    if $1 +b {20.5-$1/50}% -[0] [1] fc. ${average_colors.} + fi
35843  endl done
35844  periodize_poisson c 0,255
35845
35846fx_make_seamless_preview :
35847  u={arg($3,2,1,2,3,4)} v={arg($3,1,2,2,3,4)}
35848  gui_split_preview "if !$2 fx_make_seamless $* fi if $3 array "$u","$v" fi",${-3--1}
35849
35850#@gui Make Seamless [Patch-Based] : fx_frame_seamless, fx_frame_seamless_preview(0)
35851#@gui : Frame Size = int(32,0,256)
35852#@gui : Patch Size = int(9,3,64)
35853#@gui : Blend Size = int(0,0,64)
35854#@gui : Frame Type = choice(1,"Inner","Outer")
35855#@gui : Equalize Light = float(100,0,100)
35856#@gui : sep = separator()
35857#@gui : Preview Original = bool(0)
35858#@gui : Tiled Preview = choice(3,"None","2x1","1x2","2x2","3x3","4x4")
35859#@gui : sep = separator()
35860#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
35861#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
35862#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
35863#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
35864#@gui : sep = separator()
35865#@gui : note = note{"<small><b>Note:</b> This filter helps in converting your input pattern as a <b>seamless</b>
35866#@gui : (a.k.a periodic) texture.</small>"}
35867#@gui : sep = separator()
35868#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/15/12</i>.</small>")
35869fx_frame_seamless :
35870  repeat $! l[$>]
35871    if $5 +b {20.5-$5/50}% -[0] [1] fc. ${average_colors.} + fi
35872  endl done
35873  frame_seamless ${1-4} c 0,255
35874
35875fx_frame_seamless_preview :
35876  u={arg($7,2,1,2,3,4)} v={arg($7,1,2,2,3,4)}
35877  gui_split_preview "if !$6 fx_frame_seamless $* fi if $7 array "$u","$v" fi",${-3--1}
35878
35879#@gui Ministeck : fx_ministeck, fx_ministeck_preview(1)
35880#@gui : Number of Colors = int(8,2,24)
35881#@gui : Resolution (px) = int(64,16,256)
35882#@gui : Piece Size (px) = int(8,1,64)
35883#@gui : Piece Complexity = int(2,1,10)
35884#@gui : Relief Amplitude = float(100,0,256)
35885#@gui : Relief Size = float(0.3,0,1)
35886#@gui : Add 1px Outline = bool(0)
35887#@gui : sep = separator()
35888#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/14/01</i>.</small>")
35889fx_ministeck :
35890  repeat $! l[$>]
35891    if w>h r2dx {min($2,w)} else r2dy {min($2,h)} fi
35892    split_opacity l[0]
35893      +colormap. $1 index.. .
35894      [0],[0],1,1 rand[2] 0,1 dilate[2] $4 +[0,2]
35895      r[0] $3""00%,$3""00%
35896      +g[0] xy,1 !=[-2,-1] 0 +f[0] 'i(x+1,y+1)-i(x,y)' !=[-3--1] 0 -|[-3--1]
35897      z[0,-1] 0,0,{w-2},{h-2}
35898      if $7 . fi
35899      +shift. 1,1 *.. -1 +[-2,-1] b. {$6*$3/5} n. -$5,$5
35900      map[0] [1] rm[1] +[0,-1]
35901      if $7 ==[1] 0 * fi
35902    endl r. [0],[0],1,100% a c
35903  endl done
35904  c 0,255
35905
35906fx_ministeck_preview :
35907  repeat $! l[$>]
35908    w={w} h={h}
35909    fx_ministeck $*
35910    r $w,$h,1,100%,0,0,0.5,0.5
35911  endl done
35912
35913#@gui Montage : fx_montage, fx_montage_preview(1) : *
35914#@gui : Montage Type = choice("Auto","Custom Layout","Horizontal","Vertical","Horizontal Array","Vertical Array")
35915#@gui : Custom Layout = text{"V(H(0,1),H(2,V(3,4)))"}
35916#@gui : Merging Mode = choice(1,"Aligned","Scaled")
35917#@gui : Centering / Scale = float(0.5,0,1)
35918#@gui : Padding (px) = int(0,0,128)
35919#@gui : sep = separator()
35920#@gui : Frame (px) = int(0,0,128)
35921#@gui : Frame Color = color(0,0,0,255)
35922#@gui : sep = separator()
35923#@gui : Angle = float(0,0,360)
35924#@gui : Angle Variations = float(0,0,180)
35925#@gui : sep = separator()
35926#@gui : Cycle Layers = int(0,-255,255)
35927#@gui : Revert Layer Order = bool()
35928#@gui : Output As = _choice("Single Layer","Multiple Layers")
35929#@gui : sep = separator()
35930#@gui : note = note{"<small><b>Instructions:</b>\n
35931#@gui : - Don't forget to set the <i>Input layers...</i> option on the left if you have multiple input layers
35932#@gui : for your montage.\n
35933#@gui : - The <i>Custom layout</i> parameter is only active when <i>Montage type</i> is set to <i>Custom layout</i>.
35934#@gui : This is basically a string containing expressions such as:\n
35935#@gui : \n     . <i>H(a,b)</i> or <i>V(a,b)</i> stand respectively for an horizontal and vertical merge of two
35936#@gui : blocks <i>a</i> and <i>b</i>.
35937#@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
35938#@gui : <i>RRR(a)</i> for resp. 180-deg and 270-deg. rotations.
35939#@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
35940#@gui : version of <i>a</i>.\n\n
35941#@gui : - A block <i>a</i> can be a layer index or a nested montage expression itself.\n
35942#@gui : - Layer indices start from <i>0 (top layer)</i> and are treated periodically.
35943#@gui : </small>"}
35944#@gui : url = link("Click here for a tutorial","https://patdavid.net/2014/05/gmic-montage.html")
35945#@gui : url = link("+ video tutorial","http://www.youtube.com/watch?v=iM42vx22gwg")
35946#@gui : sep = separator()
35947#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/22/12</i>.</small>")
35948fx_montage : skip "${2=A}"
35949  if !$! return fi
35950  code0=X code1="$2" code2=H code3=V code4=A code5=B
35951  if $3==1" && "$4<0.5 r {max(10,$4*200)}%,{max(10,$4*200)}%,1,100%,2 fi
35952  to_rgba if $14 rv fi if $13%$! mv[{$13%$!}--1] 0 fi
35953  if $11" || "$12 repeat $! rotate[$>] {$11+u(-$12,$12)},1,0 done fi
35954  montage ${code$1},{if($3==0,$4,2+max(0,$4-0.5))},$15,\
35955    "if $""7%2 mirror x fi if $""8%2 mirror y fi "\
35956    "rotate {90*$""6} "\
35957    "if $5||$6 "\
35958    "r {max(1,$""4-2*($5+$6))},{max(1,$""5-2*($5+$6))},1,100%,2 "\
35959    "frame $6,$6,${7-10} "\
35960    "r {w+2*$5},{h+2*$5},1,100%,0,0,0.5,0.5 "\
35961    "else r $""4,$""5,1,100%,2 fi "
35962  if $15 gui_autocrop_layers fi
35963  gui_set_layer_name "[Montage]"
35964
35965fx_montage_preview : skip "${2=A}"
35966  if !$! return fi
35967  w={w} h={h}
35968  if $3==1" && "$4<0.5 r {max(10,$4*200)}%,{max(10,$4*200)}%,1,100%,2 fi
35969  drgba
35970  code0=X code1="$2" code2=H code3=V code4=A code5=B
35971  to_rgba if $14 rv fi if $13%$! mv[{$13%$!}--1] 0 fi
35972  if $11" || "$12 repeat $! rotate[$>] {$11+u(-$12,$12)},1,0 done fi
35973  montage ${code$1},{if($3==0,$4,2+max(0,$4-0.5))},0,\
35974    "if $""7%2 mirror x fi if $""8%2 mirror y fi
35975     rotate {90*$""6}
35976     if $5||$6
35977       r {max(1,$""4-2*($5+$6))},{max(1,$""5-2*($5+$6))},1,100%,2 fs={min(53,max(w,h)/3)}
35978       frame $6,$6,${7-10}
35979       r {w+2*$5},{h+2*$5},1,100%,0,0,0.5,0.5
35980       0 t. \\\#$""1,0,0,$fs,1,255 expand_xy. 3,0 [-1]x3 a[-4--2] c
35981       dilate. {3+2*$fs/20} a.. .,c j[0] [1],{5+$5+$6},{$5+$6},0,0,1,[2],255 k[0]
35982     else
35983       r $""4,$""5,1,100%,2 fs={min(53,max(w,h)/3)}
35984       0 t. \\\#$""1,0,0,$fs,1,255 expand_xy. 3,0 [-1]x3 a[-4--2] c
35985       dilate. {3+2*$fs/20} a.. .,c j[0] [1],5,0,0,0,1,[2],255 k[0]
35986     fi "
35987  nw={w} nh={h}
35988  resize_ratio2d $w,{$h-16},2,2
35989  drgba
35990  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
35991  a y
35992
35993#@gui Puzzle : fx_puzzle, fx_puzzle_preview(1)
35994#@gui : note = note("<small><b>Pattern parameters:</b></small>")
35995#@gui : X-Tiles = int(5,2,32)
35996#@gui : Y-Tiles = int(5,2,32)
35997#@gui : Curvature = float(0.5,0,1.5)
35998#@gui : Connectors Centering = float(0,0,1)
35999#@gui : Connectors Variability = float(0,0,2)
36000#@gui : sep = separator()
36001#@gui : note = note("<small><b>Blending parameters:</b></small>")
36002#@gui : Relief Smoothness = float(0.3,0,3)
36003#@gui : Relief Contrast = float(100,0,255)
36004#@gui : Outline Smoothness = float(0.2,0,3)
36005#@gui : Outline Contrast = float(255,0,255)
36006#@gui : sep = separator()
36007#@gui : note = note("<small><b>Recomposition parameters:</b></small>")
36008#@gui : Scale = float(100,0,150)
36009#@gui : Scale Variations = float(0,0,100)
36010#@gui : Angle = float(0,-180,180)
36011#@gui : Angle Variations = float(0,0,180)
36012#@gui : Shuffle Pieces = bool(0)
36013#@gui : Additional Outline = bool(0)
36014#@gui : Output Each Piece on a Different Layer = _bool(0)
36015#@gui : sep = separator()
36016#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/06/01</i>.</small>")
36017fx_puzzle :
36018  repeat $! l[$<]
36019    w={w} h={h} to_rgb
36020    puzzle $w,$h,$1,$2,$3,$4,$5
36021    +b. $6%,0 g. xy +[-2,-1] n. -$7,$7 +[0,-1]  # Relief.
36022    +b. $8%,0 n. 0,1 *. -1 +. 1 n. {(255-$9)/255},1 *[0,-1] c 0,255  # Outline.
36023
36024    if $10!=100||$11||$12||$13||$14||$15||$16
36025
36026      # Decompose puzzle into set of pieces.
36027      +-. 1 label_fg. 0
36028      +area_fg. 0,0 <. 50% -|... . ==. 0 *[-2,-1]
36029      distance.. 0 *.. -1 watershed. .. rm.. label. 0,0
36030
36031      repeat iM+1
36032        +==[1] $>
36033        coords=${autocrop_coords.\ 0}
36034        +z[0] $coords z.. $coords rv[-2,-1] *.. . *. 255 a[-2,-1] c
36035        x$>={arg(1,$coords)+round(w/2)} y$>={arg(2,$coords)+round(h/2)}
36036      done
36037      rm[0,1]
36038
36039      # Recompose puzzle.
36040      if $14 sort_list +,u fi
36041
36042      if $16 # One piece by layer.
36043        repeat $! l[$<]
36044          r2dy {max(0.1,$10+$11*u(-1,1))}% rotate {$12+$13*u(-1,1)}
36045          if $15 expand_xy 1,0 fi
36046          cx={round(w/2)} cy={round(h/2)}
36047          sh 100% if $15 dilate. 3 fi
36048          i[0] $w,$h,1,4
36049          j[0] ..,{${x$<}-$cx},{${y$<}-$cy},0,0,1,.,255 rm[-2,-1]
36050        endl done
36051
36052      else # All pieces on the same layer.
36053        i[0] $w,$h,1,{s}
36054        repeat $!-1
36055          r2dy. {max(0.1,$10+$11*u(-1,1))}% rotate. {$12+$13*u(-1,1)}
36056          if $15 expand_xy. 1,0 fi
36057          cx={round(w/2)} cy={round(h/2)}
36058          sh. 100% if $15 dilate. 3 fi
36059          j[0] ..,{${x$<}-$cx},{${y$<}-$cy},0,0,1,.,255 rm[-2,-1]
36060        done
36061      fi
36062
36063    else rm.
36064    fi
36065  endl done
36066
36067fx_puzzle_preview :
36068  fx_puzzle ${1-15},0
36069
36070#@gui Taquin : fx_taquin, fx_taquin(1)
36071#@gui : X-Tiles = int(7,1,20)
36072#@gui : Y-Tiles = int(7,1,20)
36073#@gui : Remove Tile = choice("None","First","Last","Random")
36074#@gui : sep = separator()
36075#@gui : Relief = float(50,0,255)
36076#@gui : Border Thickness (%) = float(5,0,100)
36077#@gui : Border Outline = int(0,0,16)
36078#@gui : Ouline Color = color(0,0,0,255)
36079#@gui : sep = separator()
36080#@gui : Random Seed = int(0,0,65535)
36081#@gui : sep = separator()
36082#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/13/01</i>.</small>")
36083fx_taquin :
36084  to_a repeat $! l[$>] srand $11 taquin $1,$2,$3,$4,$5%,$6,${7-10} endl done
36085
36086#@gui Tileable Rotation : fx_rotate_tileable, fx_rotate_tileable_preview(1)
36087#@gui : Angle = float(45,0,360)
36088#@gui : Maximum Size Factor = int(8,0,20)
36089#@gui : Array Mode = choice(0,"None","x-axis","y-axis","xy-axes","2xy-axes")
36090#@gui : sep = separator()
36091#@gui : note = note("<small><b>Note:</b> This filter implements the tileable rotation technique described by
36092#@gui : <b>Peter Yu</b>, at:</small>")
36093#@gui : url = link("[Peter Yu] Create rotated tileable patterns",
36094#@gui : "http://www.peteryu.ca/tutorials/gimp/rotate_tileable_patterns")
36095#@gui : sep = separator()
36096#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/26/05</i>.</small>")
36097fx_rotate_tileable :
36098  if $3 array_mirror 1,{$3-1},1 fi
36099  rotate_tileable $1,{if($3==0,$2,$2/2)}
36100
36101fx_rotate_tileable_preview :
36102  l fx_rotate_tileable $*
36103  onfail gui_warning_preview "Invalid image size" endl
36104
36105#@gui Tiled Rotation : fx_rotate_tiles, fx_rotate_tiles(1)
36106#@gui : X-Tiles = int(5,1,80)
36107#@gui : Y-Tiles = int(5,1,80)
36108#@gui : Angle = float(15,0,360)
36109#@gui : X-Shadow = float(3,-20,20)
36110#@gui : Y-Shadow = float(3,-20,20)
36111#@gui : Smoothness = float(1.8,0,5)
36112#@gui : sep = separator()
36113#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36114fx_rotate_tiles :
36115  to_rgba rotate_tiles $3,$1,$2 drop_shadow $4%,$5%,$6%
36116
36117#@gui Tiled Normalization : fx_normalize_tiles, fx_normalize_tiles(1)
36118#@gui : X-Tiles = int(25,1,80)
36119#@gui : Y-Tiles = int(25,1,80)
36120#@gui : Minimal Value = float(0,0,255)
36121#@gui : Maximal Value = float(255,0,255)
36122#@gui : sep = separator()
36123#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
36124#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
36125#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
36126#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
36127#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
36128#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
36129#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
36130#@gui : sep = separator()
36131#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36132_fx_normalize_tiles :
36133  repeat $! l. split_tiles $1,$2 n $3,$4 append_tiles $1,$2 endl mv. 0 done
36134
36135fx_normalize_tiles :
36136  ac "_fx_normalize_tiles ${1-4}",$-1
36137
36138#@gui Tiled Random Shifts : fx_shift_tiles, fx_shift_tiles(1)
36139#@gui : X-Tiles = int(10,1,30)
36140#@gui : Y-Tiles = int(10,1,30)
36141#@gui : Amplitude = float(10,0,100)
36142#@gui : Opacity = float(1,0,1)
36143#@gui : sep = separator()
36144#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36145fx_shift_tiles :
36146  to_rgba shift_tiles $1,$2,$3
36147  if $4<1 repeat $! s. c *. $4 a[-4--1] c mv. 0 done fi
36148
36149#@gui Tiled Parameterization : fx_parameterize_tiles, fx_parameterize_tiles(1)
36150#@gui : X-Tiles = int(10,1,30)
36151#@gui : Y-Tiles = int(10,1,30)
36152#@gui : Fitting Function = choice("Linear","Quadratic")
36153#@gui : sep = separator()
36154#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36155fx_parameterize_tiles :
36156  if $3
36157    quadratize_tiles $1,$2
36158  else
36159    linearize_tiles $1,$2
36160  fi
36161  c 0,255
36162
36163#@gui Tiled Isolation : fx_isolate_tiles, fx_isolate_tiles(0)
36164#@gui : X-Size = float(10,0,100)
36165#@gui : Y-Size = float(10,0,100)
36166#@gui : X-Border = float(5,0,100)
36167#@gui : Y-Border = float(5,0,100)
36168#@gui : Keep Tiles Square = bool(1)
36169#@gui : Keep Borders Square = bool(1)
36170#@gui : sep = separator()
36171#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/13/04</i>.</small>")
36172fx_isolate_tiles :
36173  repeat $! l[$>] to_rgba
36174    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
36175    if $6 bx={max($3,$4)} by=$bx else bx=$3 by=$4 fi
36176    s x,-$sx
36177    repeat $! l[$>] s y,-$sy r 100%,{100+$by}%,1,100%,0,0,0.5,0.5 a y endl done
36178    r {100+$bx}%,100%,1,100%,0,0,0.5,0.5 a x
36179  endl done
36180
36181
36182#@gui ____<b>Artistic</b>
36183#-------------------------
36184
36185#@gui Bokeh : fx_bokeh, fx_bokeh_preview(1)
36186#@gui : Number of Scales = int(3,1,10)
36187#@gui : Shape = choice(8,"Triangle","Square","Diamond","Pentagon","Hexagon","Octogon","Decagon","Star","Circular")
36188#@gui : Random Seed = int(0,0,65535)
36189#@gui : sep = separator()
36190#@gui : note = note{"<small><b>Starting parameters:</b></small>"}
36191#@gui : Density = int(30,1,256)
36192#@gui : Radius (%) = float(8,0,50)
36193#@gui : Outline (%) = float(4,0,100)
36194#@gui : Inner Shade = float(0.3,0,1)
36195#@gui : Smoothness = float(0.2,0,8)
36196#@gui : Color = color(210,210,80,160)
36197#@gui : Color Dispersion = float(0.7,0,1)
36198#@gui : sep = separator()
36199#@gui : note = note{"<small><b>Ending parameters:</b></small>"}
36200#@gui : Density = int(30,1,256)
36201#@gui : Radius (%) = float(20,0,50)
36202#@gui : Outline (%) = float(20,0,100)
36203#@gui : Inner Shade = float(1,0,1)
36204#@gui : Smoothness = float(2,0,8)
36205#@gui : Color = color(170,130,20,110)
36206#@gui : Color Dispersion = float(0.15,0,1)
36207#@gui : sep = separator()
36208#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36209#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36210#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36211#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36212#@gui : sep = separator()
36213#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/02/07</i>.</small>")
36214fx_bokeh :
36215  _shape=$2
36216  srand $3
36217  repeat $! l[$<] nm=${-gui_layer_name} pos=${-gui_layer_pos}
36218    100%,100%,1,3
36219    (${4-13};${14-23}) if $1>2 r. 100%,$1,1,1,3 fi
36220    repeat $1
36221      +rows. $> _fx_bokeh... {^} rm.
36222    done
36223    rm. nm. name($nm),opacity(100),mode(screen),pos($pos)
36224    rv
36225    if 0$_output_mode==0 gui_merge_layers
36226    elif 0$_output_mode<2 rm.
36227    fi
36228  endl done
36229
36230_fx_bokeh0 : shape_star $1,3        # Triangle.
36231_fx_bokeh1 : $1,$1,1,1,1             # Square.
36232_fx_bokeh2 : shape_diamond $1       # Diamond.
36233_fx_bokeh3 : shape_polygon $1,5,45  # Pentagon.
36234_fx_bokeh4 : shape_polygon $1,6,45  # Hexagon.
36235_fx_bokeh5 : shape_polygon $1,8,45  # Octogon.
36236_fx_bokeh6 : shape_polygon $1,10,45 # Decagon.
36237_fx_bokeh7 : shape_star $1,5        # Star.
36238_fx_bokeh8 : shape_circle $1        # Circle.
36239
36240fx_bokeh_preview :
36241  gui_split_preview "_output_mode=0 fx_bokeh $*",${-3--1}
36242
36243# [internal] Draw one step of bokeh with specified geometry on last image.
36244# $1=density, $2=size(in %), $3=outline(in %), $4=shade (in [0,1]), $5=smoothness (in %),
36245# $6,$7,$8,$9=RGBA (in [0,255]), $10 = color dispersion (in [0,1])
36246_fx_bokeh :
36247  radius1={r=max(w,h)*$2%;r+1-(r%2)}
36248  radius2={r=$radius1-($radius1*$3%);r+1-(r%2)}
36249
36250  random3d $1 *3d. {-2,w},{-2,h},0
36251  _fx_bokeh$_shape $radius1
36252  if $radius2>=1 _fx_bokeh$_shape $radius2 ri. ..,0,0,0.5,0.5 *. {max(0,min(1,1-$4))} -[-2,-1] fi
36253  sigma={-3,$5%*w}
36254  r. {w+5*$sigma},{h+5*$sigma},1,1,0,0,0.5,0.5
36255  b. $sigma,0 n. 0,255
36256  sprites3d[1] [2],1 rm[2]
36257  l.
36258    s3d r.. 3,{-2,h/3},1,1,-1 s.. x
36259    d={$10*255}
36260    rand[-4] {$6-$d},{$6+$d}
36261    rand... {$7-$d},{$7+$d}
36262    rand.. {$8-$d},{$8+$d}
36263    a[-4--2] x c.. 0,255 y a y
36264  endl
36265  j3d[0] [1],0,0,0,{$9/255},1,0,0 rm[1]
36266
36267#@gui Brushify : fx_brushify, fx_brushify_preview(0)
36268#@gui : note = note("<small><b>Brush parameters:</b></small>")
36269#@gui : Shape = choice(7,"Bottom layer","Top layer","Rectangle","Diamond","Pentagon","Hexagon","Octogon",
36270#@gui : "Ellipse","Gaussian","Star","Heart")
36271#@gui : Ratio = float(0.25,0,1)
36272#@gui : Number of Sizes = int(4,1,16)
36273#@gui : Maximal Size = int(64,1,128)
36274#@gui : Minimal Size (%)= float(25,0,100)
36275#@gui : Number of Orientations = int(12,1,24)
36276#@gui : Fuzzyness = float(0,0,10)
36277#@gui : Smoothness = float(2,0,10)
36278#@gui : Light Type = choice(4,"None","Flat","Darken","Lighten","Full")
36279#@gui : Light Strength = float(0.2,0,1)
36280#@gui : Opacity = float(0.5,0,1)
36281#@gui : sep = separator()
36282#@gui : note = note("<small><b>Painting parameters:</b></small>")
36283#@gui : Density (%) = float(30,0,100)
36284#@gui : Contour Coherence = float(1,0,1)
36285#@gui : Orientation Coherence = float(1,0,1)
36286#@gui : Gradient Smoothness = float(1,0,10)
36287#@gui : Structure Smoothness = float(5,0,10)
36288#@gui : Primary Angle = float(0,-180,180)
36289#@gui : Angle Dispersion = float(0.2,0,1)
36290#@gui : sep = separator()
36291#@gui : Preview Brush = bool(1)
36292#@gui : sep = separator()
36293#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/22/04</i>.</small>")
36294fx_brushify :
36295  _fx_brushify $*
36296  s0=0--3 s1=1--2 s2=0--2
36297  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
36298
36299_fx_brushify : # Insert brush at the end of the list
36300  N={0.9*$4}
36301  if $1==0 +autocrop. i.. 100%,100%,1,3,1 blend[-2,-1] alpha rr2d. $N,$N,0,3
36302  elif $1==1 +autocrop[0] i.. 100%,100%,1,3,1 blend[-2,-1] alpha rr2d. $N,$N,0,3
36303  elif $1==2 $4,$4 rectangle. 10%,10%,90%,90%,1,1
36304  elif $1==3 shape_diamond. $N
36305  elif $1==4 shape_polygon $N,5
36306  elif $1==5 shape_polygon $N,6
36307  elif $1==6 shape_polygon $N,8
36308  elif $1==7 shape_circle. $N
36309  elif $1==8 $4,$4 gaussian. 30%,30%,0
36310  elif $1==9 shape_star $N
36311  elif $1==10 shape_heart $N
36312  fi
36313  norm. r. 100%,{max(0.01,100*$2)}%,1,1,2 r. $4,$4,1,1,0,0,0.5,0.5
36314  spread. $7 b. $8% n. 0,1
36315
36316fx_brushify_preview :
36317  if $1<2" && "$!<2
36318    gui_error_preview "When a custom brush (bottom or top layer) is specified, at least two layers are required
36319                       for this filter to work.In this case, don't forget to set the 'Input layers' option!"
36320    return
36321  fi
36322  fx_brushify $*
36323  if $19
36324    _fx_brushify $*
36325    if $1==0 rm.. elif $1==1 rm[0] fi
36326    rr2d. {0,max(1,w/5)},{0,max(1,h/5)},0,2 n. 0,255
36327    frame. 3,3,0 frame. 1,1,255 frame. 1,1,0
36328    to_rgb. to. "Brush",4,2,13,2,1,255,255,0 to_a.
36329    j[^-1] .,2,2 rm.
36330  else
36331    if $1==0 rm. elif $1==1 rm[0] fi
36332  fi
36333
36334#@gui Cartoon : cartoon, fx_cartoon_preview(0)
36335#@gui : Smoothness = float(3,0,10)
36336#@gui : Sharpening = float(200,0,400)
36337#@gui : Edge Threshold = float(20,1,30)
36338#@gui : Edge Thickness = float(0.25,0,1)
36339#@gui : Color Strength = float(1.5,0,3)
36340#@gui : Color Quantization = int(8,2,256)
36341#@gui : sep = separator()
36342#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36343#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36344#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36345#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36346#@gui : sep = separator()
36347#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36348fx_cartoon_preview :
36349  gui_split_preview "cartoon $*",${-3--1}
36350
36351#@gui Circle Abstraction : fx_circle_abstraction, fx_circle_abstraction_preview(1)
36352#@gui : Number of Colors = int(8,2,16)
36353#@gui : Density = int(5,1,100)
36354#@gui : Opacity = float(0.8,0,1)
36355#@gui : Smoothness = float(0,0,4)
36356#@gui : Filled Circles = bool(1)
36357#@gui : Fill Transparent Holes = bool(1)
36358#@gui : Normalize Colors = bool(1)
36359#@gui : sep = separator()
36360#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36361#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36362#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36363#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36364#@gui : sep = separator()
36365#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/16/06</i>.</small>")
36366fx_circle_abstraction :
36367  repeat $! l[$>]
36368    b $4%
36369    +colormap $1 index[0] [1],0,0
36370    [0],[0],1,4,0
36371
36372    repeat $1
36373      rprogress {$>*100/$1}
36374      +==[0] $>
36375      skeleton3d. 2,2,0,1,0
36376      if {@7} # Process only non empty objects.
36377        s3d. l[-6--1]
36378          r[2] 3,{2,h/3},1,1,-1
36379          1,{2,h/2*$2%},1,1,1 r. 1,{2,h/2},1,1,4 r. 3,200% *[2,-1] y
36380        endl
36381        a[-6--1] y col3d. {1,I($>)}
36382
36383        [0],[0],1,4,0
36384        j3d. ..,0,0,0,1,{1+$5},0,0
36385        sh. 3 col3d... 255 j3d. ...,0,0,0,$3,{1+$5},0,0 rm.
36386        rm..
36387        blend[2,-1] alpha
36388      fi
36389    done
36390    k[2]
36391    if $6 +channels 3 <. 1 inpaint[0] [1],0,1 rm. channels. 0,2 fi
36392    if $7 n 0,255 fi
36393    rprogress 100
36394  endl done
36395
36396fx_circle_abstraction_preview :
36397  gui_split_preview "fx_circle_abstraction $*",${-3--1}
36398
36399#@gui Cubism : fx_cubism, fx_cubism_preview(1)
36400#@gui : Iterations = int(2,0,10)
36401#@gui : Density = float(50,0,200)
36402#@gui : Thickness = float(10,0,50)
36403#@gui : Angle = float(90,0,360)
36404#@gui : Opacity = float(0.7,0.01,1)
36405#@gui : Smoothness = float(0,0,5)
36406#@gui : sep = separator()
36407#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36408#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36409#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36410#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36411#@gui : sep = separator()
36412#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/05/06</i>.</small>")
36413fx_cubism :
36414  repeat $1 cubism ${2--1} done
36415
36416fx_cubism_preview :
36417  gui_split_preview "fx_cubism $*",${-3--1}
36418
36419#@gui Cutout : fx_cutout, fx_cutout_preview(1)
36420#@gui : Number of Levels = int(4,2,32)
36421#@gui : Edge Simplicity = float(0.5,0,3)
36422#@gui : Edge Fidelity = int(4,0,10)
36423#@gui : Normalize = bool(1)
36424#@gui : sep = separator()
36425#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36426#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36427#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36428#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36429#@gui : sep = separator()
36430#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Garagecoder</i>
36431#@gui :       Latest Update: <i>2014/03/06</i>.</small>")
36432fx_cutout :
36433  repeat $! l[$>] split_opacity l[0]
36434    median {10-$3}
36435    quantize $1
36436    +luminance. round. area. 0,1 med={ic} rm.
36437    inpaint_holes {$med*$2%},0,1
36438    if $4 n 0,255 fi
36439  endl a c endl done
36440
36441fx_cutout_preview :
36442  gui_split_preview "fx_cutout $*",${-3--1}
36443
36444#@gui Doodle : fx_doodle, gui_no_preview(0)
36445#@gui : Precision (%) = float(30,0,100)
36446#@gui : Smoothness = float(2,0,10)
36447#@gui : Coherence = float(2,0,10)
36448#@gui : Contour Threshold = float(1.5,0,10)
36449#@gui : Spacing = int(2,0,20)
36450#@gui : Minimal Stroke Length = float(70,0,255)
36451#@gui : Preview Progression While Running = _bool(1)
36452#@gui : sep = separator()
36453#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/07/08</i>.</small>")
36454fx_doodle : skip "$*"
36455   repeat $! l[$>]
36456    b $2 n 0,255 structuretensors b $3
36457    100%,100%,1,1,100
36458    if $7 w. ${"fitscreen ."},"[G'MIC] Doodle" fi
36459
36460    _stopflag=0
36461    eval "
36462      const dt = 0.5;
36463      nb_strokes = nb_consecutive_fails = 0;
36464
36465      while (nb_consecutive_fails<500*$1%,
36466        run('+store. canvas');
36467
36468        P0 = u([w,h] - 1);
36469        length = 0;
36470
36471        repeat (2,dir,
36472          P = P0;
36473          oiP = iP = round(P);
36474          C = I(#0,P); E = eig([ C[0], C[1], C[1], C[2] ]); oV = V = (dir?1:-1)*E[4,2];
36475
36476          while (E[0]>$4^2 && (i(iP)>$5 || oiP==iP),
36477            i(iP) = 0;
36478            oiP = iP;
36479            P+=dt*V;
36480            iP = round(P);
36481            C = I(#0,P,1); E = eig([ C[0], C[1], C[1], C[2] ]); V = E[4,2];
36482            dot(oV,V)<0?(V*=-1);
36483            oV = V;
36484            iP!=oiP?++length;
36485          );
36486        );
36487        length<$6?(run('rm. $canvas'); ++nb_consecutive_fails):(
36488          !(++nb_strokes%15)?run('distance. 0');
36489          nb_consecutive_fails = 0;
36490          $7 && !(nb_strokes%30)?(
36491            nb_consecutive_fails = run('if !{*} u inf else +neq. 0 r. {*,w,h},1,1,2 w. rm. u 0 fi')
36492          );
36493        )
36494      )"
36495    k. != 0 * 255
36496  endl done
36497
36498#@gui Diffusion Tensors : fx_diffusiontensors, fx_diffusiontensors_preview(0)
36499#@gui : Resolution (%) = float(10,0,20)
36500#@gui : Size = float(5,0,16)
36501#@gui : Color Mode = choice(3,"Monochrome","Grayscale","Orientation","Color")
36502#@gui : Outline = int(1,0,16)
36503#@gui : sep = separator()
36504#@gui : Sharpness = float(0.15,0,1)
36505#@gui : Anisotropy = float(1,0,1)
36506#@gui : Gradient Smoothness = float(0,0,10)
36507#@gui : Tensor Smoothness = float(3,0,10)
36508#@gui : sep = separator()
36509#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36510#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36511#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36512#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36513#@gui : sep = separator()
36514#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/19/10</i>.</small>")
36515fx_diffusiontensors :
36516  repeat $! l[$>]
36517    wh={[w,h]}
36518    +diffusiontensors ${5-8}
36519    r2dx. {max(1,w*$1%)}
36520    dt. {round(125/max(1,$1))},$2,{$3<3?$3:0},$4
36521    if $3==3
36522      remove_opacity.. r.. .,.,1,100%,3 blend[0] [1],shapeaverage0 dilate. {1+2*$4} a c
36523    else k. fi
36524    r. $wh,1,100%,2
36525  endl done
36526
36527fx_diffusiontensors_preview :
36528  gui_split_preview "fx_diffusiontensors $*",${-3--1}
36529
36530#@gui Ellipsionism : fx_ellipsionism, fx_ellipsionism_preview(0)
36531#@gui : Primary Radius = float(20,1,100)
36532#@gui : Secondary Radius = float(10,1,100)
36533#@gui : Smoothness = float(0.5,0,10)
36534#@gui : Opacity = float(0.7,0,1)
36535#@gui : Outline = float(3,1,3)
36536#@gui : Density = float(0.5,0.1,2)
36537#@gui : sep = separator()
36538#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36539#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36540#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36541#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36542#@gui : sep = separator()
36543#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36544fx_ellipsionism :
36545  ellipsionism ${^0}
36546
36547fx_ellipsionism_preview :
36548  gui_split_preview "fx_ellipsionism $*",${-3--1}
36549
36550#@gui Felt Pen : fx_feltpen, fx_feltpen_preview(0)
36551#@gui : Amplitude = float(300,0,4000)
36552#@gui : Density = float(50,0,100)
36553#@gui : Smoothness = float(1,0,10)
36554#@gui : Opacity = float(0.1,0,1)
36555#@gui : Edge = float(20,0,100)
36556#@gui : Thickness = int(5,2,32)
36557#@gui : sep = separator()
36558#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36559#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36560#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36561#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36562#@gui : sep = separator()
36563#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/25/10</i>.</small>")
36564fx_feltpen :
36565  repeat $! l[$>] +fx_hardsketchbw ${1-5},0,0 blend hardlight erode_oct $6 endl done
36566
36567fx_feltpen_preview :
36568  gui_split_preview "fx_feltpen $*",${-3--1}
36569
36570#@gui Fractalize : fractalize, fractalize(1)
36571#@gui : Detail Level = float(0.8,0,1)
36572#@gui : sep = separator()
36573#@gui : note = note("<small><b>Note:</b> This filter uses lot of random values to generate its result,
36574#@gui : so running it twice will give you different results !</small>")
36575#@gui : sep = separator()
36576#@gui : url = link("Click here for a detailed description of this filter.",\
36577# "http://www.gimpchat.com/viewtopic.php?f=28&t=10036")
36578#@gui : sep = separator()
36579#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/25/04</i>.</small>")
36580
36581#@gui Ghost : fx_ghost, fx_ghost_preview(0)
36582#@gui : note = note("<small><b>Ghost Effect:</b></small>")
36583#@gui : Amplitude = float(200,0,1000)
36584#@gui : Smoothness = float(2,0,10)
36585#@gui : Coherence = float(2,0,10)
36586#@gui : Gamma = float(1,-3,3)
36587#@gui : sep = separator()
36588#@gui : note = note("<small><b>Normalization:</b></small>")
36589#@gui : Amplitude = float(3,0,10)
36590#@gui : Radius = float(16,1,64)
36591#@gui : Invert = bool(0)
36592#@gui : sep = separator()
36593#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36594#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36595#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36596#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36597#@gui : sep = separator()
36598#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/01/30</i>.</small>")
36599fx_ghost :
36600  repeat $! l[$>] split_opacity l[0]
36601    diffusiontensors. 1,1,$2,$3 eigen. compose_channels.. + sqrt.. n.. 1,100 pow.. $4 n.. 0,$1
36602    100%,100%
36603    eval.. "*
36604      ca = i(x,y,0,0);
36605      sa = i(x,y,0,1);
36606      N = i(#-3,x,y);
36607      x0 = x + N*ca;
36608      y0 = y + N*sa;
36609      x1 = x - N*ca;
36610      y1 = y - N*sa;
36611      polygon(#-1,2,x0,y0,x1,y1,-1,1);
36612      I"
36613    k. normalize_local $5,$6
36614    n 0,255 if $7 negate fi
36615  endl a c endl done
36616
36617fx_ghost_preview :
36618  gui_split_preview "fx_ghost $*",${-3--1}
36619
36620#@gui Hard Sketch : fx_hardsketchbw, fx_hardsketchbw_preview(0)
36621#@gui : Amplitude = float(300,0,4000)
36622#@gui : Density = float(50,0,100)
36623#@gui : Smoothness = float(1,0,10)
36624#@gui : Opacity = float(0.1,0,1)
36625#@gui : Edge = float(20,0,100)
36626#@gui : Fast Approximation = bool(0)
36627#@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white",
36628#@gui : "White on transparent black","Color on white")
36629#@gui : sep = separator()
36630#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36631#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36632#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36633#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36634#@gui : sep = separator()
36635#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
36636fx_hardsketchbw :
36637  b $3
36638  if $7==4 repeat $! l[$>] +hardsketchbw $1,$2,$4,$5,$6 blend hardlight endl done return fi
36639  hardsketchbw $1,$2,$4,$5,$6
36640  if $7&1 negate fi
36641  if $7==2 r 100%,100%,1,4 repeat $! sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. done
36642  elif $7==3 r 100%,100%,1,4 repeat $! sh[$>] 3 *. 2 c. 0,255 rm. done
36643  fi
36644
36645fx_hardsketchbw_preview :
36646  gui_split_preview "fx_hardsketchbw $*",${-3--1}
36647
36648#@gui Highlight Bloom : fx_highlight_bloom, fx_highlight_bloom_preview(0)
36649#@gui : Details Strength (%) = float(90,0,400)
36650#@gui : Details Scale = float(60,0,255)
36651#@gui : Smoothness = float(60,0,255)
36652#@gui : Highlight (%) = int(30,0,100)
36653#@gui : Contrast (%) = float(20,0,100)
36654#@gui : sep = separator()
36655#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36656#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36657#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36658#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36659#@gui : sep = separator()
36660#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/24/10</i>.</small>")
36661#@gui : sep = separator()
36662#@gui : note = note("This effect has been inspired by:")
36663#@gui : url = link("This tutorial by Sebastien Guyader and Patrick David",
36664#@gui : "https://pixls.us/articles/highlight-bloom-and-photoillustration-look/")
36665fx_highlight_bloom :
36666  repeat $! l[$>] split_opacity l[0]
36667    +smooth $2,0.3,0.8,1,2
36668    -.. .
36669    amp=$3 do smooth. {min(50,$amp)},0.1,1,1,2 amp-=50 while $amp>0
36670    +retinex. 16,lab,0,15 j.. .,0,0,0,0,{$4%} rm.
36671    ac. "normalize_local {$5%},5",lab_l
36672    *.. {$1%}
36673    + c 0,255
36674  endl a c endl done
36675
36676fx_highlight_bloom_preview :
36677  gui_split_preview "fx_highlight_bloom $*",${-3--1}
36678
36679#@gui Hope Poster : fx_poster_hope, fx_poster_hope_preview(0)+
36680#@gui : Gamma = float(0,-3,3)
36681#@gui : Smoothness = float(3,0,20)
36682#@gui : sep = separator()
36683#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36684#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36685#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36686#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36687#@gui : sep = separator()
36688#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/07/11</i>.</small>")
36689fx_poster_hope :
36690  repeat $! l[$>] split_opacity l[0]
36691    apply_gamma {10^$1} poster_hope $2
36692  endl a c endl done
36693
36694fx_poster_hope_preview :
36695  gui_split_preview "fx_poster_hope $*",${-3--1}
36696
36697#@gui Hough Sketch : fx_houghsketchbw, fx_houghsketchbw_preview(0)
36698#@gui : Smoothness = float(1.25,0,10)
36699#@gui : Density = float(10,0,70)
36700#@gui : Radius = int(5,0,30)
36701#@gui : Threshold = float(80,0,100)
36702#@gui : Opacity = float(0.1,0,1)
36703#@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white",
36704#@gui : "White on transparent black","Color on white")
36705#@gui : sep = separator()
36706#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36707#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36708#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36709#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36710#@gui : sep = separator()
36711#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/05</i>.</small>")
36712fx_houghsketchbw :
36713  b $1 n 0,255
36714  if $6==4 repeat $! l[$>] +houghsketchbw ${2-5} blend hardlight endl done return fi
36715  houghsketchbw ${2-5}
36716  if $6&1 negate fi
36717  if $6==2 r 100%,100%,1,4 repeat $! sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. done
36718  elif $6==3 r 100%,100%,1,4 repeat $! sh[$>] 3 *. 2 c. 0,255 rm. done
36719  fi
36720
36721fx_houghsketchbw_preview :
36722  gui_split_preview "fx_houghsketchbw $*",${-3--1}
36723
36724#@gui Kuwahara : fx_kuwahara, fx_kuwahara_preview(0)
36725#@gui : Iterations = int(2,1,20)
36726#@gui : Radius = int(5,1,30)
36727#@gui : sep = separator()
36728#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
36729#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
36730#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
36731#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
36732#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
36733#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
36734#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
36735#@gui : Value Action = choice("None","Cut","Normalize")
36736#@gui : sep = separator()
36737#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36738#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36739#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36740#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36741#@gui : sep = separator()
36742#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/31/05</i>.</small>")
36743fx_kuwahara :
36744  ac "repeat $1 kuwahara $2 done",$3,$4
36745
36746fx_kuwahara_preview :
36747  gui_split_preview "fx_kuwahara $*",${-3--1}
36748
36749#@gui Linify : fx_linify, fx_linify_preview(0)
36750#@gui : Density = float(40,0,100)
36751#@gui : Spreading = float(2,0,10)
36752#@gui : Resolution (%) = float(40,0,100)
36753#@gui : Line Opacity = float(10,0,30)
36754#@gui : Line Precision = int(24,1,128)
36755#@gui : Color Mode = choice(0,"Subtractive","Additive")
36756#@gui : sep = separator()
36757#@gui : Preview Progression While Running = _bool(1)
36758#@gui : sep = separator()
36759#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36760#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36761#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36762#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36763#@gui : sep = separator()
36764#@gui : note = note{"<b>Note:</b>\n\n
36765#@gui : - This filter is our own implementation of the nice algorithm proposed on the webpage
36766#@gui : <a href="http://linify.me">http://linify.me</a>.\n
36767#@gui : - This is a quite resource-demanding filter, so please be patient when running it.\n
36768#@gui : - It actually renders better when applied on small images (&lt;1024).
36769#@gui : "}
36770#@gui : sep = separator()
36771#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/11/21</i>.</small>")
36772fx_linify :
36773  repeat $! l[$>]
36774    if $7 _debug=1 w ${-fitscreen\ {[w,h]}},0,"[Preview] G'MIC: Linify" fi
36775    linify $1,$2,$3%,$4,$5,$6
36776  endl done
36777
36778fx_linify_preview :
36779  gui_split_preview "fx_linify ${1-6},0",${-3--1}
36780
36781#@gui Lylejk's Painting : fx_lylejk_painting, fx_lylejk_painting_preview(0)
36782#@gui : Iterations = int(10,1,20)
36783#@gui : Abstraction = int(2,1,20)
36784#@gui : Radius = int(4,1,30)
36785#@gui : Canvas = float(10,0,100)
36786#@gui : sep = separator()
36787#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36788#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36789#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36790#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36791#@gui : sep = separator()
36792#@gui : note = note("<small>Authors: <i>Lyle Kroll</i> and <i>David Tschumperlé</i>.
36793#@gui :       Latest Update: <i>2015/23/02</i>.</small>")
36794#@gui : url = link("Filter Explained here","http://www.gimpchat.com/viewtopic.php?f=10&t=2624")
36795fx_lylejk_painting :
36796  repeat $! l[$>]
36797    nm={0,n}
36798    +l repeat $1 b 0.75 unsharp 0.75,10.49 c 0,255 mv. 0 done endl
36799    smooth. 300,0.26,1,0,7
36800    . rv[-3--1]
36801    blend[-2,-1] lighten,0.5
36802    blend[-2,-1] grainmerge,1
36803    fx_kuwahara. $2,$3,0,0
36804    texturize_canvas. $4,4
36805    nm $nm
36806  endl done
36807
36808fx_lylejk_painting_preview :
36809  gui_split_preview "fx_lylejk_painting $*",${-3--1}
36810
36811#@gui Paint With Brush : fx_paint_with_brush, fx_paint_with_brush_preview(0)
36812#@gui : Predefined style = choice("Default","Felt Spots","Colored Edges","Circles","Dreamy","Fuzzy",
36813#@gui : "Whirls","Smooth")
36814#@gui : previous_style = value(-1)
36815#@gui : sep = separator()
36816#@gui : note = note("<span color="#EE5500"><small><b>Global Settings:</b></small></span>")
36817#@gui : Painting Order = choice(1,"Random","Coarse to Fine","Fine to Coarse")
36818#@gui : Number of Iterations = int(16,1,128)
36819#@gui : Precision (%) = float(30,0,100)
36820#@gui : Details (%) = float(100,0,100)
36821#@gui : Background (%) = float(100,0,100)
36822#@gui : Sharpness (%) = float(10,0,100)
36823#@gui : Anisotropy (%) = float(80,0,100)
36824#@gui : Smoothness = float(0.5,0,8)
36825#@gui : Coherence = float(3,0,16)
36826#@gui : Twist Angle (°) = int(45,-1,360)
36827#@gui : note = note{"<small>(<b>-1</b> means '<i>Random Angle</i>').</small>"}
36828#@gui : Twist strength (%) = int(0,-1,100)
36829#@gui : note = note{"<small>(<b>-1</b> means '<i>Angle Shift</i>').</small>"}
36830#@gui : Init Canvas = choice(6,"Black","Gray","White","Self","Blur","Kuwahara","Vector Painting")
36831#@gui : sep = separator()
36832#@gui : note = note("<span color="#EE5500"><small><b>Brush for Details:</b></small></span>")
36833#@gui : Brush Diameter (px) = float(2,0,100)
36834#@gui : Stroke Length (px) = float(10,0,100)
36835#@gui : Hue Randomness (%) = float(0,0,100)
36836#@gui : Saturation Randomness (%) = float(0,0,100)
36837#@gui : Value Randomness (%) = float(0,0,100)
36838#@gui : Opacity (%) = float(60,0,100)
36839#@gui : sep = separator()
36840#@gui : note = note("<span color="#EE5500"><small><b>Brush for Background:</b></small></span>")
36841#@gui : Brush Diameter (px) = float(20,0,100)
36842#@gui : Stroke Length (px) = float(1,0,100)
36843#@gui : Hue Randomness (%) = float(0,0,100)
36844#@gui : Saturation Randomness (%) = float(0,0,100)
36845#@gui : Value Randomness (%) = float(0,0,100)
36846#@gui : Opacity (%) = float(30,0,100)
36847#@gui : sep = separator()
36848#@gui : note = note("<span color="#EE5500"><small><b>Brush Dynamics:</b></small></span>")
36849#@gui : Brush Diameter = float(15,0,255)
36850#@gui : Stroke Length = float(15,0,255)
36851#@gui : Hue Randomness = float(15,0,255)
36852#@gui : Saturation Randomness = float(15,0,255)
36853#@gui : Value Randomness = float(15,0,255)
36854#@gui : Opacity = float(15,0,255)
36855#@gui : Spatial Step = float(1,0,3)
36856#@gui : Angular Step (°) = float(45,0,90)
36857#@gui : sep = separator()
36858#@gui : Preview Progression While Running = _bool(0)
36859#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
36860#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
36861#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
36862#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
36863#@gui : sep = separator()
36864#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.\nLatest Update: <i>2021/08/30</i>.</small>")
36865fx_paint_with_brush :
36866  predefined_style,previous_style,\
36867  painting_order,nb_iterations,precision,details_threshold,background_threshold,\
36868  sharpness,anisotropy,smoothness,coherence,twist_angle,twist_strength,init_canvas,\
36869  brush_diameter_details,stroke_length_details,\
36870  hue_randomness_details,saturation_randomness_details,value_randomness_details,opacity_details,\
36871  brush_diameter_background,stroke_length_background,\
36872  hue_randomness_background,saturation_randomness_background,value_randomness_background,opacity_background,\
36873  brush_diameter_dynamics,stroke_length_dynamics,\
36874  hue_randomness_dynamics,saturation_randomness_dynamics,value_randomness_dynamics,opacity_dynamics,\
36875  spatial_step,angular_step,preview_progression,preview_type,preview_splitx,preview_splity=${1-38}
36876
36877  # Define parameters for predefined styles.
36878  style0=1,16,50,100,100,10,80,0.5,3,0,0,6,2,10,0,0,0,60,20,1,0,0,0,30,15,15,15,15,15,15,1,45
36879  style1=1,16,4,100,100,1,30,0.5,3,0,0,2,3,1,0,0,0,80,10,1,0,0,0,30,8,15,15,15,15,15,1,45
36880  style2=2,16,30,100,20,10,100,0.5,3,45,0,4,4,10,80,0,0,60,20,1,0,0,0,30,10,15,15,15,15,15,1,45
36881  style3=1,16,6.6,100,100,0,0,1.7,3,0,0,2,3,1,0,0,50,80,30,1,0,0,50,20,15,15,15,15,15,15,1,45
36882  style4=1,16,50,100,80,5,80,0.5,1.5,45,0,4,1,20,0,0,0,10,5,1,0,0,0,30,15,15,15,15,15,15,1,45
36883  style5=0,16,10,100,100,10,80,0.5,3,135,50,6,1,20,0,0,0,90,3,10,0,0,0,30,0,15,15,15,15,15,1,45
36884  style6=1,16,30,100,100,0,95,0.5,8,-1,-1,2,2,30,0,0,0,100,20,1,0,0,0,30,0,0,0,0,0,0,1,45
36885  style7=1,16,50,100,100,10,80,0.5,3,0,0,6,1,10,0,0,0,6,20,1,0,0,0,100,17.34,15,15,15,15,20,1,45
36886
36887  if $predefined_style!=$previous_style" && "$previous_style>=0
36888    painting_order,nb_iterations,precision,details_threshold,background_threshold,\
36889    sharpness,anisotropy,smoothness,coherence,twist_angle,twist_strength,init_canvas,\
36890    brush_diameter_details,stroke_length_details,\
36891    hue_randomness_details,saturation_randomness_details,value_randomness_details,opacity_details,\
36892    brush_diameter_background,stroke_length_background,\
36893    hue_randomness_background,saturation_randomness_background,value_randomness_background,opacity_background,\
36894    brush_diameter_dynamics,stroke_length_dynamics,\
36895    hue_randomness_dynamics,saturation_randomness_dynamics,value_randomness_dynamics,opacity_dynamics,\
36896    spatial_step,angular_step=${style$predefined_style}
36897  fi
36898
36899  # Process images.
36900  repeat $! l[$>] to_color
36901
36902    # Determine painting geometry.
36903    +diffusiontensors {[$sharpness,$anisotropy]%},$smoothness,$coherence
36904    if $twist_strength>0 # Attract to twist angle
36905      if $twist_angle>=0
36906        f. "begin(Id = eye(2));
36907            T = [ i0,i1,i1,i2 ];
36908            eig = eig(T);
36909            const ang = $twist_angle°;
36910            U = lerp(eig[2,2],[ cos(ang),sin(ang) ],$twist_strength%);
36911            U/=max(1e-8,norm(U));
36912            UUt = mul(U,U,2);
36913            T = eig[0]*UUt + eig[1]*(Id - UUt);
36914            [ T[0],T[1],T[3] ]"
36915      else
36916        100%,100%,1,2 rand. -1,1 b. $coherence orientation.
36917        f.. "begin(Id = eye(2));
36918             T = [ i0,i1,i1,i2 ];
36919             eig = eig(T);
36920             U = lerp(eig[2,2],I(#-1),$twist_strength%);
36921             UUt = mul(U,U,2);
36922             T = eig[0]*UUt + eig[1]*(Id - UUt);
36923             [ T[0],T[1],T[3] ]"
36924        rm.
36925      fi
36926    elif $twist_strength<0 # Shift with twist angle
36927      if $twist_angle>=0
36928        f. "R = rot($twist_angle°);
36929            Rt = transpose(R,2);
36930            T = mul(mul(Rt,[ i0,i1,i1,i2 ],2),R,2);
36931            [ T[0],T[1],T[3] ]"
36932      else
36933        100%,100%,1,2 rand. -1,1 b. $coherence orientation.
36934        f.. "ang = atan2(i(#-1,x,y,0,1),i(#-1,x,y,0,0));
36935            R = rot(ang);
36936            Rt = transpose(R,2);
36937            T = mul(mul(Rt,[ i0,i1,i1,i2 ],2),R,2);
36938            [ T[0],T[1],T[3] ]"
36939        rm.
36940      fi
36941    fi
36942
36943    # Compute contour map and sequence of brush strokes.
36944    +gradient_norm.. 100%,100%,1,2,[x,y] a[-2,-1] c r. {wh},1,1,3,-1 sort. +,x
36945    r. {max(0.01,$precision)}%,1,1,3 z. {(100-$background_threshold)*$details_threshold%}%,$details_threshold%
36946    if $painting_order==0 100% rand. 0,100 rv[-2,-1] a[-2,-1] c sort. +,x channels. 1,100%
36947    elif $painting_order==2 mirror. x
36948    fi
36949
36950    # Initialize canvas.
36951    arg0 $init_canvas,"+f[0] 0","+f[0] 128","+f[0] 255","[0]","+b[0] 1%","+kuwahara[0] 10","+fx_vector_painting[0] 9"
36952    run ${}
36953
36954    # Paint brush strokes.
36955    ind=0
36956    repeat inf,t
36957      progress {$ind*100/w#2}
36958
36959      100%,100%,1,{s#0+1}
36960      nb_strokes={max(1,w#2/$nb_iterations)}
36961      $nb_strokes,1,1,1,:${-math_lib}"
36962        begin(color_random = vector(#s#0));
36963        lambda(tau) = min(1,edge/(1e-8+tau));
36964
36965        const brush_diameter_background = $brush_diameter_background;
36966        const brush_diameter_details = $brush_diameter_details;
36967        const brush_diameter_dynamics = $brush_diameter_dynamics;
36968        const stroke_length_background = $stroke_length_background;
36969        const stroke_length_details = $stroke_length_details;
36970        const stroke_length_dynamics = $stroke_length_dynamics;
36971        const opacity_background = 255*$opacity_background%;
36972        const opacity_details = 255*$opacity_details%;
36973        const opacity_dynamics = $opacity_dynamics;
36974        const hue_randomness_background = $hue_randomness_background%;
36975        const hue_randomness_details = $hue_randomness_details%;
36976        const hue_randomness_dynamics = $hue_randomness_dynamics;
36977        const saturation_randomness_background = $saturation_randomness_background%;
36978        const saturation_randomness_details = $saturation_randomness_details%;
36979        const saturation_randomness_dynamics = $saturation_randomness_dynamics;
36980        const value_randomness_background = $value_randomness_background%;
36981        const value_randomness_details = $value_randomness_details%;
36982        const value_randomness_dynamics = $value_randomness_dynamics;
36983        const spatial_step = max($spatial_step,0.1);
36984        const angular_step = max($angular_step,1);
36985
36986        stroke = $ind + x;
36987        stroke<w#2?(
36988          P = I[#2,stroke];
36989          edge = P[0];
36990          X0 = P[1,2];
36991          brush_size = max(0.5,lerp(brush_diameter_background,brush_diameter_details,
36992                                    lambda(brush_diameter_dynamics)));
36993          stroke_length = lerp(stroke_length_background,stroke_length_details,
36994                               lambda(stroke_length_dynamics));
36995          hue_randomness = lerp(hue_randomness_background,hue_randomness_details,
36996                                lambda(hue_randomness_dynamics));
36997          saturation_randomness = lerp(saturation_randomness_background,saturation_randomness_details,
36998                                       lambda(saturation_randomness_dynamics));
36999          value_randomness = lerp(value_randomness_background,value_randomness_details,
37000                                  lambda(value_randomness_dynamics));
37001          opacity = lerp(opacity_background,opacity_details,
37002                         lambda(opacity_dynamics));
37003
37004          val = I(#0,X0);
37005          HSV = rgb2hsv(val[0,3]);
37006          HSV[0] = (HSV[0] + 180*hue_randomness*0.35*g)%360;
37007          HSV[1] = cut(HSV[1] + saturation_randomness*0.35*abs(g),0,1);
37008          HSV[2] = cut(HSV[2] + value_randomness*0.35*g,0,1);
37009          RGB = hsv2rgb(HSV);
37010          copy(val,RGB,3);
37011          color = [ val,opacity ];
37012
37013          for (a = 0, a<360, a+=angular_step,
37014            X = X0;
37015            U = [ cos(a°), sin(a°) ];
37016            for (l = 0, l<stroke_length, l+=spatial_step,
37017              C = I(#1,X,1);
37018              T = [ C[0],C[1],C[1],C[2] ];
37019              eig = eig(T);
37020              U = T*U;
37021              ellipse(#-1,X[0],X[1],brush_size,brush_size*(1 - eig[0] + eig[1]),atan2(U[1],U[0]),1,color);
37022              X+=spatial_step*U;
37023              U/=max(1e-9,norm(U));
37024            );
37025          );
37026        );" rm.
37027
37028      sh. 100% j[3] ..,0,0,0,0,1,.,255 rm[-2,-1]
37029
37030      if !0$_is_preview" && "$preview_progression
37031        if !{*} w. ${"fitscreen ."},0 else w. -1,-1,0 fi
37032        if !{*} break fi
37033      fi
37034
37035      ind+=$nb_strokes
37036      if $ind>=w#2 break fi
37037
37038    done
37039    k.
37040  endl done
37041
37042  # Set style parameters if new style has been selected.
37043  if $predefined_style!=$previous_style
37044    u "{"$predefined_style"}{"$predefined_style"}"\
37045    "{"$painting_order"}{"$nb_iterations"}{"$precision"}{"$details_threshold"}{"$background_threshold"}"\
37046    "{"$sharpness"}{"$anisotropy"}{"$smoothness"}{"$coherence"}{"$twist_angle"}{"$twist_strength"}{"$init_canvas"}"\
37047    "{"$brush_diameter_details"}{"$stroke_length_details"}"\
37048    "{"$hue_randomness_details"}{"$saturation_randomness_details"}{"$value_randomness_details"}{"$opacity_details"}"\
37049    "{"$brush_diameter_background"}{"$stroke_length_background"}"\
37050    "{"$hue_randomness_background"}{"$saturation_randomness_background"}{"$value_randomness_background"}"\
37051    "{"$opacity_background"}"\
37052    "{"$brush_diameter_dynamics"}{"$stroke_length_dynamics"}"\
37053    "{"$hue_randomness_dynamics"}{"$saturation_randomness_dynamics"}{"$value_randomness_dynamics"}"\
37054    "{"$opacity_dynamics"}"\
37055    "{"$spatial_step"}{"$angular_step"}{"$preview_progression"}{"$preview_type"}{"$preview_splitx,$preview_splity"}"
37056  fi
37057
37058fx_paint_with_brush_preview :
37059  _is_preview=1
37060  gui_split_preview "fx_paint_with_brush $*",${-3--1}
37061
37062#@gui Painting : fx_painting, fx_painting_preview(0)+
37063#@gui : Abstraction = int(5,1,10)
37064#@gui : Details Scale = float(2.5,0,5)
37065#@gui : Color = float(1.5,0,4)
37066#@gui : Smoothness = float(50,0,1000)
37067#@gui : Sharpen Shades = bool(1)
37068#@gui : sep = separator()
37069#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37070#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37071#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37072#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37073#@gui : sep = separator()
37074#@gui : note = note("<small>Authors: <i>Lyle Kroll</i>, <i>Angelo Lama</i> and <i>David Tschumperlé</i>.
37075#@gui : \nLatest Update: <i>2011/28/02</i>.</small>")
37076fx_painting : skip ${4=0},${5=0}
37077  repeat $! l[$>]
37078    to_colormode {max(3,s)} split_opacity rv
37079    repeat $1 fx_normalize_local. 10,6,5,20,1,11 done
37080    fx_smooth_anisotropic. {100*$2},0.2,1,$2,{2*$2},0.8,90,2,0,1,1,2,1,16
37081    fx_mix_lab. 1,0,0,$3,0,0.5,$3,0,0.5,0,2,0
37082    if $5 fx_segment_watershed. 10,1,0 fi
37083    smooth. $4,0,1,1,1
37084    rv a c
37085  endl done
37086
37087fx_painting_preview :
37088  gui_split_preview "fx_painting $*",${-3--1}
37089
37090#@gui Pen Drawing : fx_pen_drawing, fx_pen_drawing_preview(0)+
37091#@gui : Amplitude = float(10,0,30)
37092#@gui : sep = separator()
37093#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37094#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37095#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37096#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37097#@gui : sep = separator()
37098#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
37099fx_pen_drawing :
37100  drawing $1
37101
37102fx_pen_drawing_preview :
37103  gui_split_preview "fx_pen_drawing $*",${-3--1}
37104
37105#@gui Polygonize [Delaunay] : fx_polygonize_delaunay, fx_polygonize_delaunay_preview(0)
37106#@gui : Density (%) = float(40,0,100)
37107#@gui : Edges = float(5,0,100)
37108#@gui : Boundaries (%) = float(75,0,100)
37109#@gui : Smoothness = float(0.5,0,8)
37110#@gui : Filling = choice(3,"Black","White","Random","Average","Linear")
37111#@gui : Outline (%) = float(50,0,100)
37112#@gui : Outline Color = color(0,0,0,255)
37113#@gui : Anti-Aliasing = bool(1)
37114#@gui : sep = separator()
37115#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37116#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37117#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37118#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37119#@gui : sep = separator()
37120#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/06/05</i>.</small>")
37121fx_polygonize_delaunay :
37122  repeat $! l[$>] to_rgba
37123    wh={[w,h]} if $11 r 150%,150%,1,100%,3 fi
37124
37125    # Compute Delaunay triangulation.
37126    mM={[im,iM]} b $4 n $mM
37127    +to_rgb. gradient_norm. ge. $2
37128    f. "!x || !y || x==w-1 || y==h-1?(u^0.25<$3%):(u^0.25<$1%?i:0)"
37129    {is+1},1,1,2 f.. ">begin(p = 0); i?(I[#-1,++p] = [x,y];p):0"
37130    delaunay.. 0
37131
37132    # Render filling.
37133    if $5==4 # Linear
37134      +f[0] 0 f... "*i?(
37135        p = I(#1); P0 = I[#2,p[0]]; P1 = I[#2,p[1]]; P2 = I[#2,p[2]];
37136        W = solve([P0[0],P1[0],P2[0],P0[1],P1[1],P2[1],1,1,1],[x,y,1]);
37137        I(#-1) = cut(W[0]*I(#0,P0) + W[1]*I(#0,P1) + W[2]*I(#0,P2),0,255);
37138      );I"
37139    elif $5==3 # Average
37140      +f[0] 0 f... "*i?(
37141        p = I(#1); P0 = I[#2,p[0]]; P1 = I[#2,p[1]]; P2 = I[#2,p[2]];
37142        I(#-1) = (I(#0,P0) + I(#0,P1) + I(#0,P2))/3;
37143      );I"
37144    elif $5==2 # Random
37145      +norm[1] label_fg. 0 {iM+1},1,1,3 rand. 0,255 to_rgba. point. 0 map.. . rm.
37146    else # Black or white
37147      +norm[1] !=. 0 *. 255 channels. -3,0
37148      if $5 sh. 0,2 f. 255 rm. fi
37149    fi
37150    rm[0,2]
37151
37152    # Render outlines.
37153    if $6
37154      norm[0] f[0] "i!=j(1) || i!=j(0,1)" # thinning[0] 1
37155      +fc. ${7-10} j.. .,0,0,0,0,{$6%},... rm.
37156    fi
37157
37158    k.
37159    if $11 r $wh,1,100%,2 fi
37160
37161  endl done
37162
37163fx_polygonize_delaunay_preview :
37164  gui_split_preview "fx_polygonize_delaunay $*",${-3--1}
37165
37166#@gui Polygonize [Energy] : fx_polygonize, fx_polygonize_preview(0)
37167#@gui : Amplitude = int(300,0,2000)
37168#@gui : Smoothness = float(10,0,100)
37169#@gui : Minimal Area = float(10,0,100)
37170#@gui : X-Resolution = float(10,1,256)
37171#@gui : Y-Resolution = float(10,1,256)
37172#@gui : Outline Color = color(0,0,0,255)
37173#@gui : sep = separator()
37174#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37175#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37176#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37177#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37178#@gui : sep = separator()
37179#@gui : url = link("Click here for a detailed description of this filter.",\
37180# "http://www.gimpchat.com/viewtopic.php?f=28&t=9174")
37181#@gui : sep = separator()
37182#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/02/12</i>.</small>")
37183fx_polygonize :
37184  polygonize $1,$2,{$3^2},$4,$5
37185  if $9 repeat $! l[$>]  # has outline.
37186    +norm g. xy,1 !=[-2,-1] 0 -|[-2,-1] r. 100%,100%,1,4
37187    replace_color. 0,0,1,1,1,1,$6,$7,$8,$9
37188    blend alpha
37189  endl done fi
37190
37191fx_polygonize_preview :
37192  gui_split_preview "fx_polygonize $*",${-3--1}
37193
37194#@gui Poster Edges : fx_poster_edges, fx_poster_edges_preview(0)
37195#@gui : Image Smoothness = float(20,0,100)
37196#@gui : Edge Threshold = float(60,0,100)
37197#@gui : Edge Shade = float(5,0,30)
37198#@gui : Edge Thickness = float(0,0,5)
37199#@gui : Edge Antialiasing = float(10,0,100)
37200#@gui : Posterization Level = int(0,0,15)
37201#@gui : Posterization Antialiasing = float(0,0,100)
37202#@gui : sep = separator()
37203#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37204#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37205#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37206#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37207#@gui : sep = separator()
37208#@gui : url = link("Click here for a detailed description of this filter.",\
37209# "http://www.davidrevoy.com/article147/gmic-new-filter-poster-edges")
37210#@gui : sep = separator()
37211#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>David Revoy</i>.
37212#@gui :       Latest Update: <i>2012/30/11</i>.</small>")
37213fx_poster_edges :
37214  if $1 bilateral 10,$1 fi
37215  poster_edges ${2-7}
37216
37217fx_poster_edges_preview :
37218  gui_split_preview "fx_poster_edges $*",${-3--1}
37219
37220#@gui Posterize : fx_posterize, fx_posterize_preview(0)
37221#@gui : Smoothness = float(150,0,800)
37222#@gui : Edges (%) = float(30,0,100)
37223#@gui : Paint = float(1,0,10)
37224#@gui : Colors = int(12,2,32)
37225#@gui : Minimal Area = int(0,0,64)
37226#@gui : Outline (%) = float(0,0,100)
37227#@gui : Normalize Colors = bool(0)
37228#@gui : sep = separator()
37229#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37230#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37231#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37232#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37233#@gui : sep = separator()
37234#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/25/10</i>.</small>")
37235fx_posterize :
37236  repeat $! l[$>] split_opacity l[0]
37237    . amp=$1 do smooth. {min(50,$amp)},{$2%},1,$3,{2*$3} amp-=50 while $amp>0
37238    bilateral.. .,5,10 rm.
37239    srgb2rgb +colormap $4 rgb2srgb
37240    index.. .,0,0
37241    if $5
37242      +area.. 0,0 <. $5 inpaint... .,0,3 rm.
37243    fi
37244    map.. . rm.
37245    if $6
37246      +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]
37247    fi
37248    if $7 n 0,255 fi
37249  endl a c endl done
37250
37251fx_posterize_preview :
37252  gui_split_preview "fx_posterize $*",${-3--1}
37253
37254#@gui Quadtree Variations : fx_quadtree, fx_quadtree_preview(1)
37255#@gui : Mode = choice("Squares","Sierpinksi Design","Ellipse Painting")
37256#@gui : Precision = int(1024,2,4096)
37257#@gui : Homogeneity = float(0.5,0,2)
37258#@gui : Outline = int(0,0,4)
37259#@gui : sep = separator()
37260#@gui : note = note{"<small><b>For 'Ellipse painting' only:</b></small>"}
37261#@gui : Primary Radius = float(3,0,5)
37262#@gui : Secondary Radius = float(1.5,0,5)
37263#@gui : Anisotropy = float(1,0,4)
37264#@gui : Only Leafs = bool(1)
37265#@gui : sep = separator()
37266#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37267#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37268#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37269#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37270#@gui : sep = separator()
37271#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/15/06</i>.</small>")
37272fx_quadtree :
37273  mode,precision,homogeneity,outline,radius1,radius2,anisotropy,only_leafs=${1-8}
37274  m "_qt : sh 3 u {w>1&&h>1?iv*(w*h)^"$homogeneity":-0.1} k[0]" # Function used to split quads
37275  repeat $! l[$>]
37276    to_rgb
37277
37278    # Decompose image with quadtree.
37279    keep_only_leafs={$mode!=2" || "$only_leafs}
37280    +norm. a c
37281    _qt ({d=max(w,h);[0,0,d-1,d-1]},0,${})
37282    repeat $precision
37283      +columns. 100% n={yM} rm.
37284      x0,y0,x1,y1,level={crop(0,$n,5,1)}
37285      xc={round(($x1+$x0)/2)} yc={round(($y1+$y0)/2)}
37286      xc1,yc1={[$xc,$yc]-1}
37287      level1={$level+1}
37288      B0=$x0,$y0,$xc1,$yc1 +crop[0] $B0,3 _qt. B0.=,$level1,${} rm.
37289      B1=$xc,$y0,$x1,$yc1 +crop[0] $B1,3 _qt. B1.=,$level1,${} rm.
37290      B2=$x0,$yc,$xc1,$y1 +crop[0] $B2,3 _qt. B2.=,$level1,${} rm.
37291      B3=$xc,$yc,$x1,$y1 +crop[0] $B3,3 _qt. B3.=,$level1,${} rm.
37292      if $keep_only_leafs
37293        sh. $n,$n,0,0 f. $B0 rm.
37294        ($B1;$B2;$B3) a[-2,-1] y # Insert new leafs
37295      else
37296        =. -1,5,$n # Mark as not terminal leaf anymore
37297        ($B0;$B1;$B2;$B3) a[-2,-1] y # Insert new leafs
37298      else
37299      fi
37300    done
37301    shift. 2,0,0,0,2 sort. +,y levelmax={i(0,h-1)} shift. -2,0,0,0,2 # Sort by quadtree level order
37302
37303    # Estimate data average in each quad.
37304    channels[0] 0,2 +structuretensors[0] a[0,-1] c
37305    r. {w+6},100%,1,1,0
37306    repeat h
37307      x0,y0,x1,y1={crop(0,$>,4,1)}
37308      +crop[0] $x0,$y0,$x1,$y1,3 r. 1,1,1,100%,2 y. x
37309      j.. .,{-2,w-6},$> rm.
37310    done
37311
37312    # Synthetize image.
37313    permute. cyzx
37314    channels[0] 0,3 f[0] 0
37315
37316    if $mode==0 # Squares
37317      f. ">
37318        rectangle(ind,P0,P1,opacity,color) = (
37319          _P0 = P0;
37320          _P2 = P1;
37321          _P1 = [ _P2[0], _P0[1] ];
37322          _P3 = [ _P0[0], _P2[1] ];
37323          polygon(#ind,4,_P0,_P1,_P2,_P3,opacity,color);
37324        );
37325        begin(colo = [ 0,0,0,255 ]);
37326        P = I;
37327        Xpp = P[0,2];
37328        Xnn = P[2,2];
37329        col = [ P[6,3],255 ];
37330        if ("$outline"<=0,
37331          rectangle(#0,Xpp,Xnn,1,col);
37332        _(else),
37333          rectangle(#0,Xpp,Xnn,1,colo);
37334          rectangle(#0,Xpp + "$outline",Xnn - "$outline",1,col);
37335        );
37336        I"
37337
37338    elif $mode==1 # Sierpinski design
37339      f. ">
37340        P = I;
37341        Xpp = P[0,2];
37342        Xnn = P[2,2];
37343        Xnp = [ Xnn[0],Xpp[1] ];
37344        Xpn = [ Xpp[0],Xnn[1] ];
37345        col1 = [ P[6,3],255 ];
37346        col2 = [ P[6,3],64 ];
37347        T = [ P[9],P[10],P[10],P[11] ];
37348        eig = eig(T);
37349        angle = atan2(eig[5],eig[4])*180/pi;
37350        if ((angle>=0 && angle<90) || angle<-90,
37351          polygon(#0,3,Xpp,Xnp,Xnn,1,col1);
37352          polygon(#0,3,Xpp,Xnn,Xpn,1,col2);
37353        _(else),
37354          polygon(#0,3,Xnp,Xnn,Xpn,1,col1);
37355          polygon(#0,3,Xnp,Xpp,Xpn,1,col2)
37356        );
37357        I"
37358
37359    else # Ellipse painting
37360      f. ">
37361        begin(colo = [ 0,0,0,255 ]);
37362        P = I;
37363        Xpp = P[0,2];
37364        Xnn = P[2,2];
37365        Xcc = (Xpp + Xnn)/2;
37366        R = (Xnn[0] - Xpp[0])/2;
37367        col = [ P[6,3],255 ];
37368        r = "$radius2"*R;
37369        R*="$radius1";
37370        T = [ P[9],P[10],P[10],P[11] ];
37371        eig = eig(T);
37372        anisotropy = (1 + eig[1])/(1 + eig[0]);
37373        r*=anisotropy^"$anisotropy";
37374        angle = atan2(eig[5],eig[4])*180/pi;
37375        if ("$outline">0, ellipse(#0,Xcc,R,r,angle°,1,colo));
37376        ellipse(#0,Xcc,R - "$outline",r - "$outline",angle°,1,col);
37377        I"
37378      if !$outline sh[0] 100% if !im solidify[0] 10% fi fi
37379    fi
37380    k[0]
37381  endl done
37382  um _qt
37383
37384fx_quadtree_preview :
37385  gui_split_preview "fx_quadtree $*",${-3--1}
37386
37387#@gui Rodilius : fx_rodilius, fx_rodilius_preview(1)
37388#@gui : Amplitude = float(10,0,30)
37389#@gui : Thickness = float(10,0,100)
37390#@gui : Sharpness = float(300,0,1000)
37391#@gui : Orientations = int(5,2,36)
37392#@gui : Offset = float(30,0,180)
37393#@gui : Smoothness = int(0,0,5)
37394#@gui : Color Mode = choice(1,"Darker","Lighter")
37395#@gui : sep = separator()
37396#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
37397#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
37398#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
37399#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
37400#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
37401#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
37402#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
37403#@gui : Value Action = choice("None","Cut","Normalize")
37404#@gui : sep = separator()
37405#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37406#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37407#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37408#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37409#@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=RC07VUpzwGc")
37410#@gui : sep = separator()
37411#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Rod/GimpChat</i>.
37412#@gui :       Latest Update: <i>2013/05/03</i>.</small>")
37413fx_rodilius :
37414  ac "rodilius ${1-5,7} repeat $6 smooth 10,0,1,1,1,0.8,45 sharpen 30 done c 0,255",$8,$9
37415
37416fx_rodilius_preview :
37417  gui_split_preview "fx_rodilius $*",${-3--1}
37418
37419#@gui Shapeism : fx_shapeism, fx_shapeism_preview(0)+
37420#@gui : Shape = choice(2,"Squares","Triangles","Circles","Diamond","Hexagon","Octagon","Stars","Custom")
37421#@gui : Branches = int(7,3,16)
37422#@gui : Thickness = float(0.38,0,1)
37423#@gui : Angle = float(0,0,360)
37424#@gui : note = note("<small><b>Note:</b> Parameters <i>Branches</i>, <i>Thickness</i> and <i>Angle</i> are
37425#@gui : used only for <i>Custom</i> shapes.</small>")
37426#@gui : Antialiasing = bool(1)
37427#@gui : sep = separator()
37428#@gui : Scales = int(5,1,16)
37429#@gui : Maximal Size = int(32,1,256)
37430#@gui : Minimal Size = int(8,1,256)
37431#@gui : Allow Angle = choice(3,"0 deg.","180 deg.","90 deg.","Any")
37432#@gui : Spacing = int(1,-5,5)
37433#@gui : Precision = int(5,1,10)
37434#@gui : Edges = float(0.5,0,2)
37435#@gui : Smoothness = float(1,0,10)
37436#@gui : Background = color(0,0,0,255)
37437#@gui : sep = separator()
37438#@gui : url = link("Click here for a detailed description of this filter.",\
37439# "http://gimpchat.com/viewtopic.php?f=28&t=7500&sid=5b483979826903b8f8fc8fdaf1767dae")
37440#@gui : sep = separator()
37441#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/11/06</i>.</small>")
37442fx_shapeism :
37443  repeat $! l[$>]
37444    to_rgb
37445    +gradient_norm b. $13% ^. $12 quantize. $6,0,0
37446    100%,100%,1,2
37447    repeat $6
37448
37449      # Create map of possible locations.
37450      +channels[2] 100% +>[1] $> !=.. 0 -|[-2,-1] a[2,-1] c
37451
37452      # Create shape at given scale.
37453      size={if($6<=1,$7,$7+($8-$7)*$>/($6-1))}
37454      if $size<1 break fi
37455      if $5 {2*$size},{2*$size} _fx_shapeism$1. ${2-4} r2dy. $size
37456      else $size,$size _fx_shapeism$1. ${2-4}
37457      fi
37458      +!=. 0 expand_xy[-2,-1] 1,0 n[-2,-1] 0,1
37459      if $10<1 dilate. 3 fi
37460      . a[-3--1] c
37461
37462      # Pack sprites for given scale
37463      rprogress "pack_sprites[-2,-1] 1,100,$9,$10,$11",{$>*100/$6},{($>+1)*100/$6}
37464      channels. 0,1
37465
37466    done
37467
37468    rprogress 97
37469    rm[1]
37470    channels. 0 +!=. 0 blend[0,-1] shapeaverage0 *[1] 255 a c
37471    i[0] 100%,100%,1,4 fc[0] $14,$15,$16,$17
37472    blend alpha
37473    rprogress 100
37474  endl done
37475
37476fx_shapeism_preview :
37477  gui_print_preview ""
37478  50%,50% _fx_shapeism$1. ${2-4} frame. 1,1,0 >=. 50% n. 0,255 r. 100%,100%,1,4
37479  ri. [0],0,0,0.5,0.5 -|
37480
37481_fx_shapeism0 : # Square
37482  f 255 skip $*
37483
37484_fx_shapeism1 : # Triangle
37485  polygon 3,50%,0,0,100%,100%,100%,1,1 skip $*
37486
37487_fx_shapeism2 : # Circle
37488  shape_circle {w} rm.. skip $*
37489
37490_fx_shapeism3 : # Diamond
37491  shape_diamond {w} rm.. skip $*
37492
37493_fx_shapeism4 : # Hexagon
37494  star3d 3,1 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $*
37495
37496_fx_shapeism5 : # Octogon
37497  star3d 4,1 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $*
37498
37499_fx_shapeism6 : # Star
37500  star3d 5 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $*
37501
37502_fx_shapeism7 : # Custom
37503  star3d $1,$2 *3d. {0,min(w,h)/2} r3d. 0,0,1,$3 j3d[0] .,50%,50%,0,1,2,0 k[0]
37504
37505#@gui Sharp Abstract : fx_sharp_abstract, fx_sharp_abstract_preview(0)
37506#@gui : Spatial Scale = float(4,0,32)
37507#@gui : Value Scale = float(10,0,16)
37508#@gui : Precision = float(0.5,0,2)
37509#@gui : sep = separator()
37510#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
37511#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
37512#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
37513#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
37514#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
37515#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
37516#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
37517#@gui : sep = separator()
37518#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37519#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37520#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37521#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37522#@gui : sep = separator()
37523#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/09</i>.</small>")
37524fx_sharp_abstract :
37525  ac "rolling_guidance ${1-3}",$4
37526
37527fx_sharp_abstract_preview :
37528  gui_split_preview "fx_sharp_abstract $*",${-3--1}
37529
37530#@gui Sketch : fx_sketchbw, fx_sketchbw_preview(0)
37531#@gui : Number of Orientations = int(3,1,16)
37532#@gui : Starting Angle = float(45,0,180)
37533#@gui : Angle Range = float(180,0,180)
37534#@gui : Stroke Length = float(30,0,1000)
37535#@gui : Contour Threshold = float(1.75,0,10)
37536#@gui : Opacity = float(0.02,0,0.3)
37537#@gui : Background Intensity = float(0.5,0,2)
37538#@gui : Density = float(0.75,0,5)
37539#@gui : Sharpness = float(0.1,0,1)
37540#@gui : Anisotropy = float(0.7,0,1)
37541#@gui : Smoothness = float(3,0,10)
37542#@gui : Coherence = float(6,0,10)
37543#@gui : Boost Stroke = bool(0)
37544#@gui : Curved Stroke = bool(1)
37545#@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white",
37546#@gui : "White on transparent black","Color on white")
37547#@gui : Random Seed = int(0,0,65535)
37548#@gui : sep = separator()
37549#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37550#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37551#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37552#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37553#@gui : sep = separator()
37554#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/05/11</i>.</small>")
37555fx_sketchbw : skip ${16=0}
37556  srand $16
37557  if $15==4 repeat $! l[$>] +sketchbw ${1-14} blend hardlight endl done return fi
37558  sketchbw ${1-14}
37559  if $15&1 negate fi
37560  if $15==2 r 100%,100%,1,4 repeat $! sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. done
37561  elif $15==3 r 100%,100%,1,4 repeat $! sh[$>] 3 *. 2 c. 0,255 rm. done
37562  fi
37563
37564fx_sketchbw_preview :
37565  gui_split_preview "fx_sketchbw $*",${-3--1}
37566
37567#@gui Smooth Abstract : fx_smooth_abstract, fx_smooth_abstract_preview(0)
37568#@gui : Smoothness (%) = float(75,0,100)
37569#@gui : Regularization = choice(0,"Isotropic","Delaunay-Guided","Edge-Oriented")
37570#@gui : Regularization Iterations = int(20,0,100)
37571#@gui : Geometry = float(1,0,5)
37572#@gui : Details = float(30,0,50)
37573#@gui : sep = separator()
37574#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37575#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37576#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37577#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37578#@gui : sep = separator()
37579#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/06/04</i>.</small>")
37580fx_smooth_abstract :
37581  repeat $! l[$>] split_opacity l[0]
37582    srgb2rgb
37583    mM={[im,iM]} +b $4 n. $mM gradient_norm. <=. {50-$5}
37584    inpaint_pde[0] [1],$1%,$2,$3 rm.
37585    rgb2srgb c 0,255
37586  endl a c endl done
37587
37588fx_smooth_abstract_preview :
37589  gui_split_preview "fx_smooth_abstract $*",${-3--1}
37590
37591#@gui Stylize : fx_stylize,fx_stylize_preview
37592#@gui : Style = choice{0,"Custom Style (Top Layer)","Custom Style (Bottom Layer)",
37593#@gui : "Braque: Landscape near Antwerp",
37594#@gui : "Braque: Le Viaduc &agrave; l'Estaque",
37595#@gui : "Braque: Little Bay at La Ciotat",
37596#@gui : "Braque: The Mandola",
37597#@gui : "Christine Garner: Black Colour Pencil",
37598#@gui : "Christine Garner: Colour Pencil Sepia",
37599#@gui : "Christine Garner: Dark Coloured Pencil",
37600#@gui : "Christine Garner: Pencil",
37601#@gui : "Christine Garner: Sketching Pastel",
37602#@gui : "Christine Garner: Willow Charcoal",
37603#@gui : "Delaunay: Windows Open Simultaneously",
37604#@gui : "Delaunay: Portrait de Metzinger",
37605#@gui : "Hokusai: The Great Wave",
37606#@gui : "Kandinsky: Squares with Concentric Circles",
37607#@gui : "Kandinsky: Yellow-Red-Blue",
37608#@gui : "Klee: Death and Fire",
37609#@gui : "Klee: In the Style of Kairouan",
37610#@gui : "Klee: Oriental Pleasure Garden Anagoria",
37611#@gui : "Klee: Polyphony 2",
37612#@gui : "Klee: Red waistcoat",
37613#@gui : "Klimt: The Kiss",
37614#@gui : "Mondrian: Composition in Red-Yellow-Blue",
37615#@gui : "Mondrian: Evening; Red Tree",
37616#@gui : "Mondrian: Gray Tree",
37617#@gui : "Monet: San Giorgio Maggiore at Dusk",
37618#@gui : "Monet: Water-Lily Pond",
37619#@gui : "Monet: Wheatstacks - End of Summer",
37620#@gui : "Munch: The Scream",
37621#@gui : "Picabia: Udnie",
37622#@gui : "Picasso: Les Demoiselles d'Avignon",
37623#@gui : "Picasso: Seated Woman",
37624#@gui : "Picasso: The Reservoir - Horta de Ebro",
37625#@gui : "Pollock: Convergence",
37626#@gui : "Pollock: Summertime Number 9A",
37627#@gui : "Van Gogh: Almond Blossom",
37628#@gui : "Van Gogh: Irises",
37629#@gui : "Van Gogh: The Starry Night",
37630#@gui : "Van Gogh: Wheat Field with Crows"}
37631#@gui : Scale Style to Fit Target Resolution = choice(5,"No rescaling","10%","20%","30%","50%","75%","100%","150%",
37632#@gui : "200%","250%","300%")
37633#@gui : Style Variations = choice("None","All XY-flips","All 90&deg; rotations","All 45&deg; rotations")
37634#@gui : Preview Progression While Running = bool(1)
37635#@gui : sep = separator(), note = note{"<small><b><span color="blue">Style/Target Parameters:</span></b></small>"}
37636#@gui : Fidelity to Target (Finest) = float(0.5,0,5)
37637#@gui : Fidelity to Target (Coarsest) = float(2,0,5)
37638#@gui : Fidelity Smoothness (Finest) = float(3,0,5)
37639#@gui : Fidelity Smoothness (Coarsest) = float(0.5,0,5)
37640#@gui : Fidelity Chromaticity = float(0.1,0,1)
37641#@gui : sep = separator(), note = note{"<small><b><span color="blue">Image Matching Parameters:</span></b></small>"}
37642#@gui : Match Colors With = choice(3,"Nothing","Gamma Balance","Histogram Transfer","PCA transfer")
37643#@gui : Colorspace = choice{3,"sRGB","Linear RGB","YCbCr","YCbCr (Luma/Chroma),"YCbCr (Luma Only)",
37644#@gui : "YCbCr (Chroma Only)","Lab","Lab (Luma/Chroma)","Lab (Luma Only)","Lab (Chroma Only)"}
37645#@gui : Keep Color Channels = choice{"All","Luminance Only (YCbCr)","Luminance Only (Lab)","Chrominances Only (CbCr)",
37646#@gui : "Chrominances Only (ab)"}
37647#@gui : Smoothness = float(0.7,0,5)
37648#@gui : Also Match Gradients = float(1,0,5)
37649#@gui : sep = separator(), note = note{"<small><b><span color="blue">Advanced Parameters:</span></b></small>"}
37650#@gui : Init. Type = choice("Best Match","Identity","Randomized")
37651#@gui : Init. Resolution = choice(1,"8px","16px","32px","64px","128px","256px")
37652#@gui : Init. With High Gradients Only = float(0,0,100)
37653#@gui : Patch Size for Analysis = int(5,2,16)
37654#@gui : Patch Size for Synthesis = int(5,2,16)
37655#@gui : Patch Size for Synthesis (Final) = int(7,2,16)
37656#@gui : Number of Matches (Finest) = int(1,0,10)
37657#@gui : Number of Matches (Coarsest) = int(30,0,200)
37658#@gui : Penalize Patch Repetitions = int(2,0,300)
37659#@gui : Matching Precision (Smaller is Faster) = float(2,0,10)
37660#@gui : Scale Factor = float(1.85,1.1,4)
37661#@gui : Skip Finest Scales = int(0,0,3)
37662#@gui : sep = separator()
37663#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/03/23</i>.</small>")
37664fx_stylize :
37665  init_resolution={arg(1+$16,8,16,32,64,128,256)}
37666
37667  # Define image matching function
37668  colorspace=${"arg 1+$11,all,lrgb,ycbcr,ycbcr,ycbcr_y,ycbcr_cbcr,lab,lab,lab_l,lab_ab"}
37669  match_colors="s c,-3 mv[1] 3 b[-2,-1] 1% negate[-2,-1] n[-2,-1] 0,1"
37670  luma,chroma=
37671  if $11==3" || "$11==7 # Chroma/Luma conversion for YCbCr or Lab
37672    luma=_${"arg 1+($11==7),y,l"}
37673    chroma=_${"arg 1+($11==7),cbcr,ab"}
37674  fi
37675  if $10==1
37676    match_colors=$match_colors" ac[0,1] \"balance_gamma ,\","$colorspace$luma
37677    if narg($chroma) match_colors=$match_colors" ac[0,1] \"balance_gamma ,\","$colorspace$chroma fi
37678  elif $10==2
37679    match_colors=$match_colors" transfer_histogram[0] [1],256,"$colorspace$luma
37680    if narg($chroma) match_colors=$match_colors" transfer_histogram[0] [1],256,"$colorspace$chroma fi
37681  elif $10==3
37682    match_colors=$match_colors" transfer_pca[0] [1],"$colorspace$luma" c[0] 0,255"
37683    if narg($chroma) match_colors=$match_colors" transfer_pca[0] [1],"$colorspace$chroma" c[0] 0,255" fi
37684  fi
37685  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]"
37686  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]"
37687  elif $12==3 match_colors=$match_colors" rgb2ycbcr[0,1] sh[0,1] 0 f[-2,-1] 128 rm[-2,-1] ycbcr2rgb[0,1]"
37688  elif $12==4 match_colors=$match_colors" srgb2lab[0,1] sh[0,1] 0 f[-2,-1] 50 rm[-2,-1] lab2srgb[0,1]"
37689  fi
37690  match_colors=$match_colors\
37691              " 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"
37692
37693  patch_penalization=$23
37694
37695  # Insert style image at the end of the list.
37696  if $1<2 # Custom style (top or bottom layer)
37697    if $!<2 error "At least two layers are required in this mode." fi
37698    ind_first={$1==0?1:0} ind_style={$1==0?0:-1} N={$!-1} sh[$ind_style]
37699  else
37700    ind_first=0 N=$! ind_style= _fx_stylize {$1-2}
37701  fi
37702
37703  # Process layers.
37704  is_window=0
37705  repeat $N l[{$ind_first+$>},-1] nm={0,n}
37706    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
37707    if $3==1 . +mirror. x +mirror[-2,-1] y a[-4--1] z
37708    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
37709    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
37710    fi
37711    if $4 wsiz=${"fitscreen "{0,[w,h]}} w[0] $wsiz is_window={*} fi
37712    stylize[0] .,${5-9},$15,$init_resolution,${17-22},$patch_penalization,${24-26},$match_colors
37713    k[0,1] nm[0] $nm
37714    if $is_window" && "!{*} break fi
37715  endl done
37716  rm. # Remove style image
37717  if 0$_output_mode
37718    if narg($ind_style) rm[$ind_style] fi # Do not duplicate style layer if output mode -> new layers
37719    if $is_window" && "!{*} rm fi # In case user closed the window, does not output new layers.
37720  fi
37721
37722fx_stylize_preview :
37723  if $1<2 # Custom style (top or bottom layer)
37724    if $!<2 gui_warning_preview "At least two layers are required when specifying a custom style." return fi
37725    ind_first={$1==0?1:0} ind_style={$1==0?0:-1} N={$!-1} sh[$ind_style]
37726  else
37727    ind_first=0 N=$! ind_style= _fx_stylize {$1-2}
37728  fi
37729  repeat $N l[{$ind_first+$>},-1]
37730    gui_no_preview[0] ,
37731    +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
37732  endl done rm.
37733  if narg($ind_style) rm[$ind_style] fi
37734
37735_fx_stylize : # Download pre-defined style image
37736  if isnum($1)
37737    name=${"arg 1+$1",\
37738           "landscapenearantwerp,leviaducalestaque,littlebayatlaciotat,themandola",\
37739            "blackcolourpencil,colourpencilsepia,darkcolouredpencil,pencil,sketchingpastel,willowcharcoal",\
37740            "windowsopensimultaneously,portraitdemetzinger,greatwave,squareswithconcentriccircles,yellowredblue",\
37741            "deathandfire,inthestyleofkairouan,orientalpleasuregardenanagoria,polyphony2,redwaistcoat,thekiss",\
37742            "compositionredyellowblue,redtree,graytree,sangiorgiomaggioreatdusk,waterlilypond,wheatstacksendofsummer",\
37743            "scream,udnie,lesdemoisellesdavignon,seatedwoman,reservoirhortadeebro,convergence,summertime9a",\
37744            "almondblossom,irises,starrynight,wheatfieldwithcrows"}
37745  else name="$1"
37746  fi
37747  input_cached img/style_$name.png
37748
37749#@gui Vector Painting : fx_vector_painting, fx_vector_painting_preview(1)
37750#@gui : Details = float(9,0,10)
37751#@gui : sep = separator()
37752#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37753#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37754#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37755#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37756#@gui : sep = separator()
37757#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.\nLatest Update: <i>2015/25/08</i>.</small>")
37758fx_vector_painting :
37759  repeat $! l[$>]
37760    +luminance b. {10-$1}%,1,1
37761    f. "dmax = -1; nmax = 0;
37762        for (n = 0, ++n<=8,
37763          p = arg(n,-1,0,1,-1,1,-1,0,1);
37764          q = arg(n,-1,-1,-1,0,0,1,1,1);
37765          d = (j(p,q,0,0,0,1) - i)^2;
37766          d>dmax?(dmax = d; nmax = n):nmax;
37767        )"
37768    blend shapeaverage
37769  endl done
37770
37771fx_vector_painting_preview :
37772  gui_split_preview "fx_vector_painting $*",${-3--1}
37773
37774#@gui Warhol : warhol, warhol(1)
37775#@gui : X-Tiles = int(3,1,10)
37776#@gui : Y-Tiles = int(3,1,10)
37777#@gui : Smoothness = float(2,0,10)
37778#@gui : Color = float(40,0,60)
37779#@gui : sep = separator()
37780#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
37781
37782#@gui Whirl Drawing : fx_draw_whirl, fx_draw_whirl_preview(0)
37783#@gui : Amplitude = float(20,0,100)
37784#@gui : sep = separator()
37785#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37786#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37787#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37788#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37789#@gui : sep = separator()
37790#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
37791fx_draw_whirl :
37792  repeat $! l[$>] split_opacity draw_whirl[0] $* a c endl done
37793
37794fx_draw_whirl_preview :
37795  gui_split_preview "fx_draw_whirl $*",${-3--1}
37796
37797#@gui ____<b>Black & White</b>
37798#-------------------------------
37799
37800#@gui Black & White : fx_blackandwhite, fx_blackandwhite_preview(1)+
37801#@gui : Red Level = float(0.299,0,1)
37802#@gui : Red Smoothness = float(0,0,10)
37803#@gui : Green Level = float(0.587,0,1)
37804#@gui : Green Smoothness = float(0,0,10)
37805#@gui : Blue Level = float(0.114,0,1)
37806#@gui : Blue Smoothness = float(0,0,10)
37807#@gui : sep = separator()
37808#@gui : Brightness (%) = float(0,-100,100)
37809#@gui : Contrast (%) = float(0,-100,100)
37810#@gui : Gamma (%) = float(0,-100,100)
37811#@gui : Hue (%) = float(0,-100,100)
37812#@gui : Saturation (%) = float(0,-100,100)
37813#@gui : sep = separator()
37814#@gui : Grain (Shadows) = float(0,0,200)
37815#@gui : Grain (Midtones) = float(0,0,200)
37816#@gui : Grain (Highlights) = float(0,0,200)
37817#@gui : Grain Tone Fading = float(2,0,10)
37818#@gui : Grain Scale = float(0,0,3)
37819#@gui : Grain Type = choice("Gaussian","Uniform","Salt and Pepper","Poisson")
37820#@gui : sep = separator()
37821#@gui : Local Contrast = float(0,0,60)
37822#@gui : Radius = int(16,1,512)
37823#@gui : Contrast Smoothness = float(4,0,10)
37824#@gui : sep = separator()
37825#@gui : Pseudo-Gray Dithering = int(0,0,5)
37826#@gui : Use Maximum Tones = bool(false)
37827#@gui : sep = separator()
37828#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37829#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37830#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37831#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37832#@gui : sep = separator()
37833#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/20/02</i>.</small>")
37834fx_blackandwhite :
37835  repeat $!
37836    l. split_opacity rv to_rgb. s. c      # Isolate opacity
37837    *... $1 b... $2%                      # Red level + smoothness
37838    *.. $3 b.. $4%                        # Green level + smoothness
37839    *. $5 b. $6%                          # Blue level + smoothness
37840    +[-3--1] /. {$1+$3+$5} c. 0,255       # (R,G,B) -> B&W
37841    adjust_colors ${7-11},0,255
37842    if $12||$13||$14
37843      100%,100% [-1]x2                          # Create noise for shadows, midtones and highlights.
37844      noise... 100,$17 b... $16% n... -$12,$12  # Scaled grain on shadows.
37845      noise.. 100,$17 b.. $16% n.. -$13,$13     # Scaled grain on midtones.
37846      noise. 100,$17 b. $16% n. -$14,$14        # Scaled grain on highlights.
37847      +tones[-4] 3 b[-3--1] $15%                # Get smoothed tones.
37848      *[-6,-3] *[-4,-2] *[-2,-1]                # Get noisy tones.
37849      +[-4--1] c. 0,255                         # Compose them with the B&W image.
37850    fi
37851    rv a c endl mv. 0 done                      # Re-compose opacity and loop to next image.
37852  if $18 normalize_local $18,$19,$20,2%,1,0,255 fi
37853  if $22 n 0,255 fi
37854  if $21 to_pseudogray $21,1 fi
37855
37856fx_blackandwhite_preview :
37857  gui_split_preview "fx_blackandwhite $*",${-3--1}
37858
37859#@gui B&W Stencil : fx_stencilbw, fx_stencilbw_preview(0)
37860#@gui : Threshold = float(10,0,30)
37861#@gui : Smoothness = float(10,0,30)
37862#@gui : Hue = float(0,0,360)
37863#@gui : Saturation = float(0,0,1)
37864#@gui : sep = separator()
37865#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37866#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37867#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37868#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37869#@gui : sep = separator()
37870#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
37871fx_stencilbw :
37872  stencilbw $1,$2
37873  if $3||$4 repeat $! l[$>] split_opacity
37874    /[0] 255 i[0] 100%,100%,1,1,$4 i[0] 100%,100%,1,1,$3 a[0-2] c hsv2rgb[0]
37875  a c endl done fi
37876
37877fx_stencilbw_preview :
37878  gui_split_preview "fx_stencilbw $*",${-3--1}
37879
37880#@gui Charcoal : fx_charcoal, fx_charcoal_preview(0)
37881#@gui : Granularity = int(65,0,800)
37882#@gui : Lowlights Crossover Point = int(70,0,255)
37883#@gui : Highlights Crossover Point = int(170,0,255)
37884#@gui : Boost Contrast = bool(0)
37885#@gui : Resize Image for Optimum Effect = bool(1)
37886#@gui : Add Chalk Highlights = bool(0)
37887#@gui : Minimal Highlights = int(50,0,255)
37888#@gui : Maximal Highlights = int(70,0,255)
37889#@gui : Background Color = color(255,255,255)
37890#@gui : Foreground Color = color(0,0,0)
37891#@gui : Invert Background / Foreground = bool(0)
37892#@gui : sep = separator()
37893#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
37894#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
37895#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
37896#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
37897#@gui : sep = separator()
37898#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/17/03</i>.</small>")
37899#@gui : note = note("<small>Inspired from the Charcoal script by <i>micomicon</i> :</small>")
37900#@gui : url = link("http://registry.gimp.org/node/25078")
37901fx_charcoal :
37902  repeat $! l[$>] split_opacity l[0]
37903    compose_channels max
37904    w={w} h={h}
37905    if $5 r. 150%,150%,1,1,6 fi
37906    if $4 equalize. n. 0,255 fi
37907    sharpen {$1*3} cut 0,255
37908    if $6 +ir $7,$8 fi  # Add highlights layer if required.
37909    ir[0] $2,$3
37910    if !$15 ==[0] 0 fi
37911    -|
37912    +*[0] $10 +*[0] $11 *[0] $9
37913    a[-3--1] c replace_color 0,0,0,0,0,$12,$13,$14
37914    r $w,$h,1,100%,2
37915  endl a c endl done
37916
37917fx_charcoal_preview :
37918  gui_split_preview "fx_charcoal $*",${-3--1}
37919
37920#@gui Colorize [Interactive] : fx_colorize_interactive, fx_colorize_interactive_preview
37921#@gui : Input Type = _choice("B&W Photograph","Lineart")
37922#@gui : Output Type = _choice{"Colorized Image (1 Layer)","Colors Only (1 Layer)","Image + Colors (2 Layers)",
37923#@gui : "Image + Colors (Multi-Layers)"}
37924#@gui : View Resolution = _choice{1,"Small (Faster)","Medium","High (Slower)","Very High (Even Slower)"}
37925#@gui : 1st Additional Palette (.gpl) = _filein()
37926#@gui : 2nd Additional Palette (.gpl) = _filein()
37927#@gui : Image to Grab Color from (.png) = _filein()
37928#@gui : sep = separator()
37929#@gui : note = note{"<small><b>Description:</b>\n
37930#@gui : This filter allows to quickly colorize a B&W image or lineart.
37931#@gui : Click on the <i>Apply</i> or <i>OK</i> buttons below to open the G'MIC interactive window and
37932#@gui : start adding color control points. When you're done, exit the interactive window: your colored
37933#@gui : result will be transferred back to the host software.\n\n
37934#@gui : If you are not satisfied with the result, <i>Undo it (CTRL+Z)</i>, and click on <i>Apply</i> once
37935#@gui : again to modify your control points defined previously.
37936#@gui : To clear all control points, click on the <i>Reset</i> button above.
37937#@gui : </small>"}
37938#@gui : Clear Control Points = button(0.5)
37939#@gui : Last Image Size = value(0,0)
37940#@gui : Control Points = value(-1)
37941#@gui : sep = separator()
37942#@gui : note = note{"<small><b>Interactions:</b>\n
37943#@gui : Use the following actions in the interactive window to manage your colorization :\n\n
37944#@gui : - <b>Left mouse button</b> creates a new color control point (or move an existing one).\n
37945#@gui : - <b>Right mouse button</b> or key <b>X</b> over a control point deletes it.\n
37946#@gui : - <b>Right mouse button</b> or key <b>P</b> anywhere else picks a color from the image.\n
37947#@gui : - <b>Mouse wheel</b>, or keys <b>CTRL+arrows up/down</b> zoom view in/out.\n
37948#@gui : - <b>CTRL+mouse wheel</b>, <b>SHIFT+wheel</b> or arrow keys move image in zoomed view.\n
37949#@gui : - Key <b>SPACE</b> updates the extrapolated color field.\n
37950#@gui : - Key <b>TAB</b> toggles markers view modes.\n
37951#@gui : - Key <b>BACKSPACE</b> deletes the last control point added.\n
37952#@gui : - Key <b>PAGE UP</b> increases image contrast.\n
37953#@gui : - Key <b>PAGE DOWN</b> decreases image contrast.\n
37954#@gui : - Key <b>R</b> enters/exits color replace mode.\n
37955#@gui : - Keys <b>CTRL+D</b> increase window size.\n
37956#@gui : - Keys <b>CTRL+C</b> decrease window size.\n
37957#@gui : - Keys <b>CTRL+R</b> resets window size.\n
37958#@gui : - Keys <b>ESC</b>, <b>Q</b> or <b>ENTER</b> exit the interactive window.
37959#@gui : </small>"}
37960#@gui : sep = separator()
37961#@gui : note = note("<small>You can find more information on how to use this filter here :</small>")
37962#@gui : url = link("David Revoy's G'MIC Colorization Page",
37963#@gui : "http://www.davidrevoy.com/article240/gmic-line-art-colorization")
37964#@gui : sep = separator()
37965#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/12/07</i>.</small>")
37966fx_colorize_interactive : skip "${4=},${5=},${6=}"
37967  N=$! nm={n}
37968  resolution={arg(1+$3,512,1024,2048,0)}
37969  nm "[G"{`39`}"MIC] Colorize"
37970  if [$9][0]==-1" || "[$8]!=[w,h] _gui_control_points= else _gui_control_points=$9 fi
37971  N=$!
37972
37973  arg_palette1=0 l[]
37974    0 nm. "$4" ext={x} rm
37975    if same(['$ext'],'gpl',-1,0)
37976      input_gpl "$4" arg_palette1=1 fi
37977  onfail rm endl
37978  if $arg_palette1 arg_palette1=[{$!-1}] fi
37979
37980  arg_palette2=0 l[]
37981    0 nm. "$5" ext={x} rm
37982    if same(['$ext'],'gpl',-1,0)
37983      input_gpl "$5" arg_palette2=1 fi
37984  onfail rm endl
37985  if $arg_palette2 arg_palette2=[{$!-1}] fi
37986
37987  arg_grabber=0 l[]
37988    0 nm. "$6" ext={x} rm
37989    i "$6" to_rgb arg_grabber=1
37990  onfail rm endl
37991  if $arg_grabber arg_grabber=[{$!-1}] fi
37992
37993  repeat $N
37994    status=${x_colorize[$>]\ $1,$resolution,$2,$arg_palette1,$arg_palette2,$arg_grabber}
37995  done
37996  k[0-{$N-1}]
37997
37998  if $2==1 repeat $! l[$<] # Output : colors only (1 layer).
37999      channels {s-3},{s-1}
38000    endl done
38001  elif $2>=2 repeat $! l[$<] # Output : Lineart + Colors (2 layers).
38002      +channels {s-3},{s-1} channels.. 0,{0,s-4}
38003    endl done
38004    if $2>=3 split_colors[1] 0,256,8 fi # Split colors into layers.
38005  fi
38006  nm $nm
38007  if !narg($status) status=-1 fi
38008  u \{$1\}\{$2\}\{$3\}\{"$4"\}\{"$5"\}\{"$6"\}\{0\}\{{w},{h}\}\{$status\}
38009
38010fx_colorize_interactive_preview : skip "${4=},${5=},${6=}"
38011  if $7  # Clear control points.
38012    gui_print_preview "No preview\n  available",,"(Control points cleared)"
38013    u \{$1\}\{$2\}\{$3\}\{"$4"\}\{"$5"\}\{"$6"\}\{0\}\{{w},{h}\}\{-1\}
38014  else gui_no_preview ,
38015  fi
38016
38017#@gui Colorize [Photographs] : fx_recolorize, fx_recolorize_preview(1)
38018#@gui : Smoothness = int(2,0,6)
38019#@gui : Anisotropy = float(0.2,0,1)
38020#@gui : Output Mode = choice("Merge Brightness / Colors","Split Brightness / Colors")
38021#@gui : sep = separator()
38022#@gui : note = note{"<small><b>Note:</b> This filter needs two layers to work properly.
38023#@gui : The bottom layer must be a B&W image, while the top layer contains color patches that will
38024#@gui : be extrapolated in a smart way (edge-directed) to fill the entire image. At the end,
38025#@gui : you get a completely recolored image.</small>"}
38026#@gui : sep = separator()
38027#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/16/01</i>.</small>")
38028fx_recolorize :
38029  repeat int($!/2)
38030    if $3 s=$>,{$>+1} else s={2*$>},{2*$>+1} fi
38031    l[$s] rv[0,1]
38032      channels[0] 0 to_rgb.. # Convert to pure gray.
38033      to_rgba. split_opacity. !=. 0  # Retrieve mask of color patches.
38034      srgb2rgb[-3,-2] rgb2lab8[-3,-2] channels... 0 channels.. 1,2 # Now, list is [0]=lightness / [1]=chroma / [2]=mask.
38035      +.. 1 *.. . +gradient_norm... *. -1 watershed... . rm. -.. 1 # Get first estimate for the color interpolation.
38036      +diffusiontensors... $2,1,0.5,0.5 ==.. 0 *. .. rm..
38037      smooth.. .,{$1*80},0.8,60 rm.
38038      a[-2,-1] c lab82rgb. rgb2srgb.
38039      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
38040    endl
38041  done
38042
38043fx_recolorize_preview :
38044  fx_recolorize $* a x
38045
38046#@gui Colorize [with Colormap] : fx_bwrecolorize, fx_bwrecolorize_preview
38047#@gui : Brightness (%) = float(0,-100,100)
38048#@gui : Contrast (%) = float(0,-100,100)
38049#@gui : Gamma (%) = float(0,-100,100)
38050#@gui : Normalize Input = bool(0)
38051#@gui : sep = separator()
38052#@gui : Gradient Preset = choice("User-Defined","Black to White","White to Black","Sepia","Solarize")
38053#@gui : Interpolation Type = choice(1,"Nearest","Linear","Cubic","Lanczos")
38054#@gui : Preserve Initial Brightness = bool(0)
38055#@gui : sep = separator()
38056#@gui : note = note("<small><u>User-defined gradient :</u></small>")
38057#@gui : Number of Tones = int(5,2,8)
38058#@gui : 1st Tone = color(0,0,0,255)
38059#@gui : 2nd Tone = color(43,25,55,255)
38060#@gui : 3rd Tone = color(158,137,189,255)
38061#@gui : 4th Tone = color(224,191,228,255)
38062#@gui : 5th Tone = color(255,255,255,255)
38063#@gui : 6th Tone = color(255,255,255,255)
38064#@gui : 7th Tone = color(255,255,255,255)
38065#@gui : 8th Tone = color(255,255,255,255)
38066#@gui : sep = separator()
38067#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38068#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38069#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38070#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38071#@gui : sep = separator()
38072#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
38073fx_bwrecolorize :
38074  remove_opacity
38075  if $4 n 0,255 fi
38076  if $5==0   # User-defined gradient
38077    (${9--2}) r. 4,$8,1,1,-1 permute. yzcx
38078  elif $5==1 # Black to white
38079    (0,255^0,255^0,255^255,255)
38080  elif $5==2 # White to black
38081    (255,0^255,0^255,0^255,255)
38082  elif $5==3 # Sepia
38083    (0,44,115,143,196,244^0,20,84,119,184,235^0,5,44,73,144,200^255,255,255,255,255,255)
38084  else       # Solarize
38085    (0,359^1,1^1,1^255,255) r. 256,1,1,4,3 sh. 0,2 hsv2rgb. rm.
38086  fi
38087  if $6==0 r. 256,1,1,4,1
38088  elif $6==1 r. 256,1,1,4,3
38089  elif $6==2 r. 256,1,1,4,5 c. 0,255
38090  else r. 256,1,1,4,6
38091  fi
38092  if $7==1 sh. 0,2 rgb2hsv. sh. 2 f. x/w hsv2rgb.. rm[-2,-1] fi
38093  l[^-1] luminance adjust_colors ${1-3} endl map[^-1] . rm.
38094
38095fx_bwrecolorize_preview :
38096  gui_split_preview "fx_bwrecolorize ${^0}",${-3--1}
38097
38098#@gui Colorize Lineart [Auto-Fill] : fx_autofill_lineart, fx_autofill_lineart_preview(0)
38099#@gui : Contour Threshold (%) = float(90,0,100)
38100#@gui : Contour Normalization = bool(1)
38101#@gui : Minimal Region Area = int(8,0,256)
38102#@gui : Tolerance to Gaps = int(0,0,10)
38103#@gui : Preview Type = choice("Lineart + Colors","Colors Only")
38104#@gui : sep = separator()
38105#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/12/11</i>.</small>")
38106fx_autofill_lineart :
38107  repeat $! l[$>] nm=${-gui_layer_name}
38108
38109    # Format input lineart to expected format.
38110    is_alpha={s==2||s==4}
38111    if $is_alpha sh 100% is_alpha={im<128&&iM>128} rm. fi # Check is alpha-channel contains lineart
38112    if $is_alpha +channels 100% negate.
38113    else +norm fi
38114    n. 0,255
38115    l.
38116
38117      # Start multi-scale filling.
38118      repeat 1+$4
38119        fact={2^-$<}
38120        nw={0,max(1,round(w*$fact))}
38121        nh={0,max(1,round(h*$fact))}
38122        +r[0] $nw,$nh,1,1,2 if $2 normalize_local. , fi +>=. {min(99.5,$1)}%
38123
38124        if $colors
38125          scale2x[colors] r[colors] .,1 *[colors,-1] rv[-2,-1]
38126        fi
38127
38128        label_fg. 0,1 b.. 0.8 watershed. .. rm.. nm. colors
38129      done
38130
38131      # Inpaint regions that are too small.
38132      +area. 0,1 label_maxarea={"P=[xM,yM];i(#-2,P)-1"}
38133      if $3>1
38134        >. {$3*sqrt($3)} *[-2,-1]
38135        b.. 0.8 watershed. ..
38136      else
38137        rm.
38138      fi
38139      rm..
38140
38141      # Colorize regions.
38142      N={iM}
38143      if $N
38144        -. 1
38145        srand 0 {iM+1},1,1,3,">[j(-1) + u(135,225),u(0,0.7),u(0.4,0.9)]"
38146        hsv2rgb. round. point. $label_maxarea,0,0,1,255
38147        map.. . rm.
38148      fi
38149    endl
38150
38151    # Format output layers.
38152    if !$is_alpha gui_set_layer_mode.. multiply fi
38153    gui_set_layer_name. $nm" [colors]"
38154  endl done
38155
38156fx_autofill_lineart_preview :
38157  repeat $! l[$>]
38158    fx_autofill_lineart ${1-4}
38159    if $5 k. else rv blend multiply fi
38160  endl done
38161
38162#@gui Colorize Lineart [Smart Coloring] : fx_colorize_lineart_smart, fx_colorize_lineart_smart_preview(0)
38163#@gui : Colorize Mode = choice("Generate Random-Colors Layer","Extrapolate Color Spots on Transparent Top Layer",
38164#@gui : "Auto-Clean Bottom Color Layer")
38165#@gui : sep = separator()
38166#@gui : note = note{"<small><b>Global geometry parameters:</b></small>"}
38167#@gui : Contour Detection (%) = float(95,0,100)
38168#@gui : Discard Contour Guides = bool(0)
38169#@gui : note = note{"<small>Add strokes with a saturated color having value 255 (e.g. pure red) on your lineart
38170#@gui : allows to guide the colorization algorithm with virtual contours.</small>"}
38171#@gui : Output Region Delimiters = _bool(0)
38172#@gui : sep = separator()
38173#@gui : note = note{"<small><b>For <i>Random colors</i> mode only:</b></small>"}
38174#@gui : Make Hue Depends on Region Size = float(1,0,1)
38175#@gui : Maximal Color Saturation = int(24,0,255)
38176#@gui : Minimal Color Intensity = int(200,0,255)
38177#@gui : sep = separator()
38178#@gui : note = note{"<small><b>For <i>color spots</i> mode only:</b></small>"}
38179#@gui : Color Shading (%) = int(0,0,100)
38180#@gui : sep = separator()
38181#@gui : note = note{"<small><b>Connection parameters:</b></small>"}
38182#@gui : End Point Rate (%) = float(75,0,100)
38183#@gui : End Point Connectivity = int(2,1,5)
38184#@gui : Spline Max Length (px) = float(60,0,256)
38185#@gui : Segment Max Length (px) = float(20,0,256)
38186#@gui : Spline Max Angle (deg) = float(90,0,180)
38187#@gui : Spline Roundness = float(1,0,2)
38188#@gui : Minimal Region Area = float(10,0,100)
38189#@gui : Allow Self Intersections = bool(1)
38190#@gui : sep = separator()
38191#@gui : Preview Type = choice(0,"Colored geometry","Colored regions","Colored lineart")
38192#@gui : sep = separator()
38193#@gui : note = note("<small>Authors: <i>David Tschumperlé</i>, <i>Sébastien Fourey</i> and
38194#@gui : <i>David Revoy</i>.      Latest Update: <i>2018/11/09</i>.</small>")
38195fx_colorize_lineart_smart :
38196  _fx_colorize_lineart_smart $*,-1 round
38197
38198fx_colorize_lineart_smart_preview :
38199  if $1==1" && "$!<2 gui_warning_preview "A top layer with color spots is missing, for this colorization mode." return
38200  elif $1==2" && "$!<2 gui_warning_preview "A bottom color layer is missing, for this colorization mode." return
38201  fi
38202  _fx_colorize_lineart_smart ${1-3},0,${5--1}
38203
38204_fx_colorize_lineart_smart :
38205  if $1==1" && "$!<2 error "A top layer with color strokes is missing, for this colorization mode."
38206  elif $1==2" && "$!<2 error "A bottom color layer is missing, for this colorization mode."
38207  fi
38208  min_color_area={$15^2}
38209
38210  repeat $1?1:$! inds=${arg\ 1+!!$1,$<,0--1} l[$inds]
38211
38212    ind_lineart={$1==1?1:0}
38213    nm=${gui_layer_name[$ind_lineart]} nm[$ind_lineart] lineart
38214
38215    if $!>1 ind_colors={$1==1?0:1} nmc={$ind_colors,n} nm[$ind_colors] colors fi
38216    if $!>=3 rm[2--1] fi # Delete old color and added contour layers if any
38217
38218    # Constrain input lineart to be a binary, single channel image.
38219    [lineart]
38220    is_alpha={s==2||s==4}
38221    if $is_alpha sh. 100% is_alpha={iM-im>64} rm. fi
38222    if $is_alpha channels. 100%
38223    else luminance. negate. fi
38224    >. {255*(1-$2%)}
38225    nm. strokes
38226
38227    # Retrieve closed binary shape.
38228    _keep_keycoords={$-1==0}
38229    [strokes] close_binary. ${9-14},$min_color_area,$16 nm. new_strokes
38230
38231    if $-1==0 # Render estimated geometry
38232      +negate[strokes] *. 255 to_rgb.
38233      +-[new_strokes] [strokes] dilate. 2
38234      100%,100%,1,3,[0,128,255] j... .,0,0,0,0,1,.. rm[-2,-1]
38235      if narg($keycoords) f[keycoords] "ellipse(#-1,(I)[0,2],3,3,0,1,[255,0,0]);I" rm[keycoords] fi
38236      nm. geometry
38237    fi
38238
38239    # Label color regions and inpaint strokes.
38240    if $1==1 # Mode: Color spots-guided colorization
38241      to_rgba[$colors] [colors],[colors]
38242      f[colors] "i(#-1) = A<255?0:norm(R,G+0.3,B+0.6);I"
38243      label_fg. 0 {1+iM},1,1,{colors,s+1}
38244      f.. ">I[#-1,i]+=[ I(#"$colors"),1 ];I"
38245      s. c,{-s+1} /[-2,-1]
38246      +l[new_strokes] * -1 + 1 +b 1% b.. 1 min endl
38247      watershed... .,0 rm. map.. . rm.
38248
38249      if !$8 rm[strokes] # No color shading
38250      else # Shade colors
38251        j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1
38252        eq[new_strokes] 0 label_fg[new_strokes] 0,0
38253        if $min_color_area +area_fg[new_strokes] 0,0 >. $min_color_area *[new_strokes,-1] fi
38254        watershed[new_strokes] [strokes],0 rm[strokes]
38255        srgb2rgb. guided. [new_strokes],{1+$8/5},0 rgb2srgb.
38256      fi
38257      rm[new_strokes]
38258      nm. new_colors
38259
38260    elif $1==2 # Mode: Clean color layer
38261      j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1
38262      eq[new_strokes] 0 label_fg[new_strokes] 0,0
38263      if $min_color_area +area_fg[new_strokes] 0,0 xy_bg={[xM,yM]} >. $min_color_area *[new_strokes,-1] fi
38264      watershed[new_strokes] [strokes],0 rm[strokes]
38265      to_color[colors] sh[colors] 0,2 rgb2hsv8. rm. blend[colors,new_strokes] shapemedian sh[colors] 0,2 hsv82rgb. rm.
38266      nm[colors] new_colors
38267
38268    else # Mode: Random colorization
38269      j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1
38270      eq[new_strokes] 0 label_fg[new_strokes] 0,0
38271      if $min_color_area +area_fg[new_strokes] 0,0 xy_bg={[xM,yM]} >. $min_color_area *[new_strokes,-1] fi
38272      watershed[new_strokes] [strokes],0 rm[strokes]
38273      label[new_strokes] 0,0
38274      +histogram[new_strokes] {new_strokes,[iM+1,0,iM]} equalize. 1024 n. 0,240
38275      ind_bg={xM}
38276      channels. 0,2 srand 0
38277      f. "[i,u(1,max(3,$6))/255,u(min(252,$7),255)/255]"
38278      . sh. 0 rand. 0,360 rm.
38279      hsi2rgb[-2,-1] *.. $5 *. {1-$5} +[-2,-1] round.
38280      point. $ind_bg,0,0,1,255 point. 0,0,0,1,255
38281
38282      map[new_strokes] . rm.
38283      nm[new_strokes] new_colors
38284    fi
38285
38286    # Final rendering
38287    ind_lineart=$lineart
38288    ind_colors=$new_colors
38289
38290    if $4 # Output region delimiters
38291      100%,100%,1,1,"const boundary = 1;
38292                     J(#"$new_colors",1)!=I(#"$new_colors") || J(#"$new_colors",0,1)!=I(#"$new_colors")"
38293      *. 255 channels. -3,0 sh. 0,2 fc. 255,0,0 rm.
38294      gui_set_layer_name. $nm" [region delimiters]"
38295      mv. {$lineart+1}
38296    fi
38297    if !narg($nmc) gui_set_layer_name[$ind_colors] $nm" [colors]" fi
38298    gui_set_layer_name[$ind_lineart] $nm
38299
38300    if $-1==-1 # Rendering : Lineart and color layers
38301      if !$is_alpha gui_set_layer_mode[$ind_lineart] multiply fi
38302      if $3 l[{$1==1?1:0}] # Discard contour guides
38303        if $is_alpha
38304          100%,100%,1,3,255 blend. [0],alpha,1 rgb2hsv. channels. 2 *. 255 negate.
38305          channels.. 0,{0,s-2} s={0,s} luminance[0] to_colormode[0] $s a c
38306        else
38307          s={s} to_rgb rgb2hsv channels 2 * 255 to_colormode $s
38308        fi
38309      endl fi
38310
38311    elif $-1==0 # Preview : Colored geometry
38312      if $is_alpha channels[$ind_lineart] 100% negate[$ind_lineart] fi to_rgb[$ind_lineart]
38313      n[$ind_lineart] 180,255
38314      blend[$ind_lineart,$ind_colors] multiply
38315      +select_color[geometry] 0,255,255,255 ==. 0 j[$ind_lineart] [geometry],0,0,0,0,1,. k[$ind_lineart]
38316    elif $-1==1 # Preview : Colored regions
38317      k[$ind_colors]
38318    else # Preview : Colored lineart
38319      if $is_alpha channels[$ind_lineart] 100% negate[$ind_lineart] fi to_rgb[$ind_lineart]
38320      if $3 l[$ind_lineart] s={s} to_rgb rgb2hsv channels 2 * 255 to_colormode $s endl fi
38321      blend[$ind_lineart,$ind_colors] multiply k[$ind_lineart]
38322    fi
38323
38324  endl done
38325
38326#@gui Colorize Lineart [Propagation] : fx_colorize_lineart, fx_colorize_lineart_preview(1)
38327#@gui : note = note("<b>Layers ordering:</b>")
38328#@gui : Input Layers = choice{0,"Color Spots + Lineart","Lineart + Color Spots",
38329#@gui : "Color Spots + Extrapolated Colors + Lineart","Lineart + Color Spots + Extrapolated Colors"}
38330#@gui : Output Layers = _choice{1,"Single (Merged)","Extrapolated Colors + Lineart",
38331#@gui : "Lineart + Extrapolated Colors","Color Spots + Extrapolated Colors + Lineart",
38332#@gui : "Lineart + Color Spots + Extrapolated Colors"}
38333#@gui : Extrapolate Colors As = choice("One Layer","Two Layers","Three Layers","Four Layers","Five Layers",
38334#@gui : "Six Layers","Seven Layers","Eight Layers","Nine Layers","Ten Layers","One Layer per Single Color",
38335#@gui : "One Layer per Single Region")
38336#@gui : sep = separator()
38337#@gui : Smoothness = float(0.05,0,1)
38338#@gui : sep = separator()
38339#@gui : note = note{"<small><b>Note:</b> You probably need to select <i>All</i> for the <i>Input layers</i> option
38340#@gui : on the left.\n
38341#@gui : <i>Color Spots</i> = your layer with color indications.\n
38342#@gui : <i>Lineart</i> = your layer with line-art (B&W or transparent).\n
38343#@gui : <i>Extrapolated Colors</i> = the G'MIC generated layer with flat colors.\n\n
38344#@gui : <b>Warnings:</b>
38345#@gui : \n  - Do not rely too much on the preview, it is probably not accurate !
38346#@gui : \n  - Activate option <i>Extrapolate color as one layer per single color/region</i> only if you have
38347#@gui : <i>a lot</i> of available memory !
38348#@gui : </small>"}
38349#@gui : sep = separator()
38350#@gui : url = link("Click here for a detailed description of this filter.",\
38351# "http://www.gimpchat.com/viewtopic.php?f=28&t=7567")
38352#@gui : sep = separator()
38353#@gui : note = note("<small>Authors: <i>David Tschumperlé</i>, <i>Timothée Giet</i> and <i>David Revoy</i>.
38354#@gui :       Latest Update: <i>2013/19/06</i>.</small>")
38355fx_colorize_lineart :
38356  if $!<2 return fi
38357  if $1<2 selection=0,1 else selection=0,1,2 fi
38358  l[$selection]
38359
38360    # Format input layers.
38361    if $1==0            # Color strokes + drawing
38362    elif $1==1 rv       # Drawing + color strokes
38363    elif $1==2 rm[1]    # Color strokes + extrapolated colors + drawing
38364    elif $1==3 rm[2] rv # Drawing + color strokes + extrapolated colors.
38365    fi
38366
38367    # Here we have only 'color strokes + drawing' -> process.
38368    +to_rgba[0] split_opacity. +.. 1 !=. 0 *[-2,-1]   # Map of color labels to spread.
38369    +norm[1] n. 0,1 +histogram. 2,0,1
38370    if i(0)>i(1) *.. -1 +.. 1 fi rm.          # Determine color model of the drawing.
38371    b. $4% watershed.. . rm.                         # Priority map.
38372    -. 1
38373    # Here we have 'color strokes + drawing + extrapolated colors'.
38374
38375    # Format output layers.
38376    if $2==0 rm[0] rv blend[0,1] multiply ind=-1
38377    elif $2==1 rm[0] rv ind=0
38378    elif $2==2 rm[0] ind=1
38379    elif $2==3 rv[1,2] ind=1
38380    elif $2==4 rv[0,1] ind=2
38381    fi
38382
38383    # Separate extrapolated colors as multiple layers.
38384    if $3" && "$ind>=0 l[$ind]
38385      +mix_channels (65536,256,1)
38386      if $3==10 do  # Split by colors.
38387        iM={1,iM}
38388        if $iM>=0
38389          +==[1] $iM area={is} replace[1] $iM,-1
38390          +r. 100%,100%,1,3 *. [0]
38391          rv[-2,-1] *. 255 a[-2,-1] c nm. $area
38392        fi
38393      while $iM>=0 else  # Split by disconnected regions.
38394        label.
38395        if $3<10 %. {$3+1} fi
38396        repeat iM+1 +==[1] $< area={is} +r. 100%,100%,1,[0] *. [0] rv[-2,-1] *. 255 a[-2,-1] c nm. $area done
38397      fi
38398      rm[0,1]
38399      sort_list +,n
38400    endl fi
38401
38402  endl
38403
38404fx_colorize_lineart_preview :
38405  fx_colorize_lineart $1,0,$3,$4
38406
38407#@gui Dithering : fx_ditheredbw, fx_ditheredbw_preview(0)
38408#@gui : Brightness (%) = float(0,-100,100)
38409#@gui : Contrast (%) = float(0,-100,100)
38410#@gui : Gamma (%) = float(0,-100,100)
38411#@gui : Hue = float(0,0,360)
38412#@gui : Saturation (%) = float(0,0,100)
38413#@gui : Smoothness = float(0,0,10)
38414#@gui : sep = separator()
38415#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38416#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38417#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38418#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38419#@gui : sep = separator()
38420#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
38421fx_ditheredbw :
38422  repeat $! l[$>] split_opacity l[0]
38423    luminance  adjust_colors ${1-3} b $6
38424    ditheredbw
38425    if $4" || "$5 / 255 i[0] 100%,100%,1,2 fc[0] $4,{$5%} a c hsv2rgb fi
38426  endl a c endl done
38427
38428fx_ditheredbw_preview :
38429  gui_split_preview "fx_ditheredbw $*",${-3--1}
38430
38431#@gui Engrave : fx_engrave, fx_engrave_preview(0)
38432#@gui : note = note("<small><b>Black & White foreground:</b></small>")
38433#@gui : Radius = float(0.5,0,2)
38434#@gui : Density = float(50,0,200)
38435#@gui : Edges = float(0,0,10)
38436#@gui : Coherence = float(8,0,40)
38437#@gui : Threshold (%) = float(40,0,100)
38438#@gui : Minimal Area = int(0,-256,256)
38439#@gui : Flat Regions Removal = float(0,0,10)
38440#@gui : sep = separator()
38441#@gui : note = note("<small><b>Color background:</b></small>")
38442#@gui : Add Color Background = bool()
38443#@gui : Quantization = float(10,0,40)
38444#@gui : Shading = int(1,0,5)
38445#@gui : Hue = float(0,-180,180)
38446#@gui : Saturation (%) = float(0,-100,100)
38447#@gui : Lightness (%) = float(0,-100,100)
38448#@gui : sep = separator()
38449#@gui : Anti-Aliasing = choice(1,"Disabled","x1.5","x2","x3")
38450#@gui : sep = separator()
38451#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38452#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38453#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38454#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38455#@gui : sep = separator()
38456#@gui : note = note("<small>Authors: <i>Lyle Kroll</i> and <i>David Tschumperlé</i>.
38457#@gui :       Latest Update: <i>03/13/2015</i>.</small>")
38458fx_engrave :
38459  f={arg(1+$14,1,1.5,2,3)}
38460  r={$f*(0.2+$1)}
38461  repeat $! l[$<]
38462    nm=${-gui_layer_name} pos=${-gui_layer_pos}
38463    if $8 [0] fi  # Keep copy for color background.
38464    l[0] split_opacity l[0]
38465      wh={w},{h}
38466      norm
38467      if $14 r {100*$f}%,{100*$f}%,1,1,3 fi
38468      if $7>0 [0] fi  # Keep copy for flat regions removal.
38469      l[0]
38470        amount={(0.5+$2)^2}
38471        repeat 5 b $r unsharp $r,{1+$2} c 0,255 done
38472        smooth 100,0.1,1,{$f*$3},{$f*$4}
38473        >= {100-$5}%
38474      endl
38475      if $7>0 # Flat region removal.
38476        gradient_norm[1] b[1] $3 <[1] $7 max[0,1]
38477      fi
38478      if $6<0 area_fg 0,0 > {$f*$6*$6}
38479      elif $6>0 == 0 area_fg 0,0 > {$f*$6*$6} == 0
38480      fi
38481      * 255
38482      if $14 r $wh,1,1,2 fi
38483    endl a c endl
38484
38485    # Process color background.
38486    if $!>1
38487      l[1] split_opacity l[0]
38488        f={arg(1+$14,1,1.5,2,3)}
38489        if $14 r {100*$f}%,{100*$f}%,1,100%,3 fi
38490        b {$f*$9} segment_watershed 5
38491        if $14 r $wh,1,100%,2 fi
38492        repeat $10 guided 10,{$10*80} done
38493        rgb2hsv s c +... $11 +.. {$12%} +. $13% a c hsv2rgb
38494      endl a c endl
38495      nm[0] mode(darken),name($nm),pos($pos)
38496      nm[1] name($nm" [colors]"),pos($pos)
38497    fi
38498
38499 endl done
38500
38501fx_engrave_preview :
38502  repeat $! l[$<]
38503    gui_split_preview "nm foo fx_engrave $* gui_merge_layers",${-3--1}
38504  endl done
38505
38506#@gui Freaky B&W : fx_freaky_bw, fx_freaky_bw_preview
38507#@gui : Strength (%) = float(90,0,100)
38508#@gui : Oddness (%) = float(20,0,100)
38509#@gui : Brightness (%) = float(0,-100,100)
38510#@gui : Contrast (%) = float(0,-100,100)
38511#@gui : Gamma (%) = float(0,-100,100)
38512#@gui : sep = separator()
38513#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38514#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38515#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38516#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38517#@gui : sep = separator()
38518#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/30/09</i>.</small>")
38519fx_freaky_bw :
38520  repeat $! l[$>] split_opacity l[0]
38521    to_rgb
38522
38523    # Estimate gradient field of B&W result.
38524    +expand_xy 1,0 channels. 0,4
38525    f. ">if (c!=4,i,
38526          Rx = i(x+1,y,0,0) - i(x,y,0,0);
38527          Ry = i(x,y+1,0,0) - i(x,y,0,0);
38528          Rn = Rx^2 + Ry^2;
38529          Gx = i(x+1,y,0,1) - i(x,y,0,1);
38530          Gy = i(x,y+1,0,1) - i(x,y,0,1);
38531          Gn = Gx^2 + Gy^2;
38532          Bx = i(x+1,y,0,2) - i(x,y,0,2);
38533          By = i(x,y+1,0,2) - i(x,y,0,2);
38534          Bn = Bx^2 + By^2;
38535          n = 1e-5 + max(Rn,Gn,Bn)^"{$2%}";
38536          val = 0;
38537         if (Rn>=Gn && Rn>=Bn,
38538           i(x,y,0,3) = Rx/n; val=Ry/n,
38539         if (Gn>=Rn && Gn>=Bn,
38540           i(x,y,0,3) = Gx/n; val=Gy/n,
38541           i(x,y,0,3) = Bx/n; val=By/n));
38542         val
38543        )"
38544    channels. 3,4
38545    luminance[0] ia={0,ia}
38546
38547    # Estimate laplacian of final image and invert it.
38548    s. c
38549    f.. "i - i(x-1,y,0,0)"
38550    f. "i - i(x,y-1,0,0)"
38551    +[-2,-1]
38552    ilaplacian. 0
38553    shrink_xy. 1 +. $ia n. 0,255
38554
38555    # Merge result with original color image.
38556    j[0] [1],0,0,0,0,{$1%} rm.
38557    adjust_colors ${3-5}
38558  endl a c endl done
38559
38560fx_freaky_bw_preview :
38561  gui_split_preview "fx_freaky_bw $*",${-3--1}
38562
38563#@gui Ink Wash : fx_ink_wash, fx_ink_wash(0)
38564#@gui : note = note("Ink wash controls")
38565#@gui : Size = float(0.14,0,4)
38566#@gui : Amplitude = float(23,0,200)
38567#@gui : sep = separator()
38568#@gui : note = note("Check if you wish visual control on this step")
38569#@gui : Skip All Other Steps = bool(false)
38570#@gui : note = note ("UNcheck to reactivate the other controls")
38571#@gui : sep = separator()
38572#@gui : Smoother Sharpness = float(0.5,0,2)
38573#@gui : Smoother Edge Protection = float(0.54,0,1)
38574#@gui : Smoother Softness = float(2.25,0,10)
38575#@gui : sep = separator()
38576#@gui : Stretch Contrast = choice("None","Automatic","Automatic & Contrast Mask","Manual Controls")
38577#@gui : note = note ("To activate the sliders below chose 'Manual Controls'")
38578#@gui : sep = separator()
38579#@gui : LN Amplitude = float(2,0,60)
38580#@gui : LN Size = float(6,0,64)
38581#@gui : LN Neightborhood-Smoothness = float(5,0,40)
38582#@gui : LN Average-Smoothness = float(20,0,40)
38583#@gui : sep = separator()
38584#@gui : note = note("<small>Author: <i>PhotoComiX</i>.
38585#@gui :       Latest Update: <i>2011/05/04</i>.</small>")
38586#@gui : url = link(0,"Forum thread about the filter discussion","http://gimpchat.com/viewtopic.php?f=10&t=914")
38587fx_ink_wash :
38588  repeat $! l[$>] split_opacity l[0]
38589    fx_pencilbw. $1,$2,0,0,0
38590    if $3==1 continue
38591    elif $3==0 fx_smooth_anisotropic. 60,$4,$5,$6,1.1,0.8,30,2,0,1,1,0,1,16
38592    fi
38593    if $7==1 normalize_local. 2,6,5,24,1,0,255
38594    elif $7==2 normalize_local. 2,6,5,24,1,0,255 fx_contrast_swm 2,0,0.512
38595    elif $7==3 fx_normalize_local. $8,$9,$10,$11,1,3,0
38596    fi
38597  endl a c endl done
38598
38599#@gui Pencil : fx_pencilbw, fx_pencilbw_preview(0)
38600#@gui : Size = float(0.3,0,5)
38601#@gui : Amplitude = float(60,0,200)
38602#@gui : Hue = float(0,0,360)
38603#@gui : Saturation = float(0,0,1)
38604#@gui : sep = separator()
38605#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38606#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38607#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38608#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38609#@gui : sep = separator()
38610#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/05/03</i>.</small>")
38611fx_pencilbw :
38612  pencilbw $1,$2
38613  if $3" || "$4 repeat $! l[$>] split_opacity
38614    /[0] 255 i[0] 100%,100%,1,1,$4 i[0] 100%,100%,1,1,$3 a[0-2] c hsv2rgb[0]
38615  a c endl done fi
38616
38617fx_pencilbw_preview :
38618  gui_split_preview "fx_pencilbw $*",${-3--1}
38619
38620#@gui Pencil Portrait : fx_pencil_portraitbw, fx_pencil_portraitbw_preview(0)
38621#@gui : Stroke Length = float(30,0,500)
38622#@gui : Stroke Angle = float(120,0,180)
38623#@gui : Contour Threshold = float(1,0,10)
38624#@gui : Opacity = float(0.5,0,1)
38625#@gui : Color = color(144,79,21)
38626#@gui : sep = separator()
38627#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38628#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38629#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38630#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38631#@gui : sep = separator()
38632#@gui : note = note("<small>Authors: <i>Jamac4k</i> and <i>David Tschumperlé</i>.
38633#@gui :       Latest Update: <i>2015/29/06</i>.</small>")
38634fx_pencil_portraitbw :
38635  repeat $! l[$>] split_opacity l[0]
38636    +b 2%
38637    +blend divide rm.. luminance.
38638    fx_ink_wash.. 0,167,0,0.5,0.54,2.25,0,2,6,5,20
38639    +fx_hardsketchbw. 80,32,1.89,0.21,31.46,0,0
38640    +fx_sketchbw.. 1,$2,180,$1,$3,0.03,0,0.6,0.1,0.6,0.25,1,0,1,0
38641    blend[0,1] darken
38642    blend[0,1] multiply,0.5
38643    blend[0,1] lighten,$4
38644    normalize_local ,
38645    to_rgb +fc ${5-7} blend softlight
38646  endl a c endl done
38647
38648fx_pencil_portraitbw_preview :
38649  gui_split_preview "fx_pencil_portraitbw $*",${-3--1}
38650
38651#@gui Stamp : fx_stamp, fx_stamp_preview(0)
38652#@gui : Auto-Threshold = bool(1)
38653#@gui : Threshold = int(50,0,100)
38654#@gui : Smoothness = float(0,0,10)
38655#@gui : Sharpening = float(0,0,30)
38656#@gui : Grain = float(0,0,100)
38657#@gui : Negative = bool()
38658#@gui : Anti-Aliasing = bool(1)
38659#@gui : sep = separator()
38660#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38661#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38662#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38663#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38664#@gui : sep = separator()
38665#@gui : note = note("<small>Authors: <i>Antaron</i>, <i>Mahvin</i> and <i>David Tschumperlé</i>.
38666#@gui :      Latest Update: <i>2015/16/03</i>.</small>")
38667fx_stamp :
38668  repeat $! l[$>] split_opacity l[0]
38669    wh={w},{h}
38670    norm
38671    if $7 r 150%,150%,1,1,3 fi
38672    noise $5
38673    if $1 otsu 256 else >= $2% fi
38674    b {if($7,1.5,1)*$3},0 sharpen $4 n 0,255
38675    apply_curve 1,0,0,101,33,170,229,255,255
38676    if $7 r $wh,1,1,2 fi
38677    if $6 negate fi
38678  endl a c endl done
38679
38680fx_stamp_preview :
38681  gui_split_preview "fx_stamp $*",${-3--1}
38682
38683
38684#@gui ____<b>Colors</b>
38685#----------------------
38686
38687#@gui Abstraction : fx_color_abstraction, fx_color_abstraction_preview(0)
38688#@gui : Smoothness = float(1,0,10)
38689#@gui : Levels = int(10,2,100)
38690#@gui : Contrast = float(0.2,0.01,1)
38691#@gui : sep = separator()
38692#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38693#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38694#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38695#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38696#@gui : sep = separator()
38697#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/19/10</i>.</small>")
38698fx_color_abstraction :
38699  repeat $! l[$>] split_opacity l[0] to_rgb
38700    b $1 s c quantize $2,1,0 area 0 ^ $3 n 0,255
38701  endl a c endl done
38702
38703fx_color_abstraction_preview :
38704  gui_split_preview "fx_color_abstraction $*",${-3--1}
38705
38706#@gui Apply External CLUT : fx_apply_haldclut, fx_apply_haldclut_preview(1)+
38707#@gui : Specify HaldCLUT As = choice(2,"Top Layer","Bottom Layer","Filename")
38708#@gui : HaldCLUT Filename = filein()
38709#@gui : note = note("<small><b>Note:</b> Do not forget to set the <i>Input layers</i> option if you select
38710#@gui : <i>Top layer</i> or <i>Bottom layer</i>.</small>")
38711#@gui : sep = separator()
38712#@gui : Strength (%) = float(100,0,100)
38713#@gui : Brightness (%) = float(0,-100,100)
38714#@gui : Contrast (%) = float(0,-100,100)
38715#@gui : Gamma (%) = float(0,-100,100)
38716#@gui : Hue (%) = float(0,-100,100)
38717#@gui : Saturation (%) = float(0,-100,100)
38718#@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both")
38719#@gui : sep = separator()
38720#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38721#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38722#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38723#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38724#@gui : sep = separator()
38725#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/02/08</i>.</small>")
38726fx_apply_haldclut : skip "${2=}"
38727  mode=$1
38728  filename="$2"
38729  strength,brightness,contrast,gamma,hue,saturation,normalize=${3-9}
38730
38731  if $mode<2 # CLUT as a layer
38732    if $!<2 gui_warning_preview "Input layer with HaldCLUT is missing" return fi
38733    ind_clut={$mode?$!-1:0}
38734  else # CLUT as a file
38735    l
38736      0 nm. "$2" ext={x} rm.
38737      if lowercase(['$ext'])=='cube' input_cube "$2"
38738      else i "$2"
38739      fi
38740      ind_clut={$!-1}
38741    onfail gui_warning_preview "Specified HaldCLUT filename not found" return
38742    endl
38743  fi
38744  if {$ind_clut,iM>512} /[$ind_clut] 255 fi # Possibly a 16bits HaldCLUT.
38745
38746  if $normalize==1" || "$normalize==3 # Pre-normalization
38747    repeat $! if $>!=$ind_clut l[$>] split_opacity balance_gamma[0] , a c endl fi done
38748  fi
38749  repeat $! if $>!=$ind_clut +map_clut[$>] [$ind_clut] j[$>] .,0,0,0,0,{$strength%} rm. fi done rm[$ind_clut]
38750  adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
38751  if $normalize==2" || "$normalize==3 # Post-normalization
38752     repeat $! l[$>] split_opacity n[0] 0,255 a c endl done
38753  fi
38754
38755fx_apply_haldclut_preview : skip "${2=}"
38756  if $1<2 gui_warning_preview "No preview available in this mode" return fi
38757  gui_split_preview "fx_apply_haldclut $1,\"$2\",${3--2}",${-3--1}
38758
38759#@gui Basic Adjustments : fx_adjust_colors, fx_adjust_colors_preview
38760#@gui : Brightness (%) = float(0,-100,100)
38761#@gui : Contrast (%) = float(0,-100,100)
38762#@gui : Gamma (%) = float(0,-100,100)
38763#@gui : Hue (%) = float(0,-100,100)
38764#@gui : Saturation (%) = float(0,-100,100)
38765#@gui : sep = separator()
38766#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38767#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38768#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38769#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38770#@gui : sep = separator()
38771#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/16/06</i>.</small>")
38772fx_adjust_colors :
38773  adjust_colors ${1-5},0,255
38774
38775fx_adjust_colors_preview :
38776  gui_split_preview "fx_adjust_colors $*",${-3--1}
38777
38778#@gui Boost Chromaticity : fx_boost_chroma, fx_boost_chroma_preview(1)
38779#@gui : Amplitude (%) = float(50,0,100)
38780#@gui : Color Space = choice{"YCbCr (Distinct)","YCbCr (Mixed)","Lab (Distinct)","Lab (Mixed)"}
38781#@gui : sep = separator()
38782#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38783#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38784#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38785#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38786#@gui : sep = separator()
38787#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/19/07</i>.</small>")
38788fx_boost_chroma :
38789  repeat $! l[$>] split_opacity l[0]
38790    +to_rgb
38791    if $2>=2
38792      srgb2rgb rgb2lab.
38793      if $2==2 sh. 1 sh.. 2 equalize[-2,-1] rm[-2,-1]
38794      else sh. 1,2 equalize. rm.
38795      fi
38796      lab2rgb. rgb2srgb
38797    else
38798      rgb2ycbcr.
38799      if $2==0 sh. 1 sh.. 2 equalize[-2,-1] rm[-2,-1]
38800      else sh. 1,2 equalize. rm.
38801      fi
38802      ycbcr2rgb.
38803    fi
38804    j.. .,0,0,0,0,{$1%} rm.
38805  endl a c endl done
38806
38807fx_boost_chroma_preview :
38808  gui_split_preview "fx_boost_chroma $*",${-3--1}
38809
38810#@gui Boost-Fade : fx_boost_fade, fx_boost_fade_preview
38811#@gui : Amplitude = float(5,0,10)
38812#@gui : Chromaticity From = choice("YCbCr","Lab")
38813#@gui : sep = separator()
38814#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38815#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38816#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38817#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38818#@gui : sep = separator()
38819#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/11/26</i>.</small>")
38820fx_boost_fade :
38821  repeat $! l[$>]
38822    100%,100%,1,3 rand. 0,1 b. {10-10*($1/10)^0.5} n. 0,255
38823    to_colormode 0 a z
38824    ac "s z transfer_histogram.. . rm.",${"arg 1+$2,ycbcr_cbcr,lab_ab"}
38825  endl done
38826
38827fx_boost_fade_preview :
38828  gui_split_preview "fx_boost_fade $1,$2",${-3--1}
38829
38830#@gui Channel Processing : fx_channel_processing, fx_channel_processing_preview(1)
38831#@gui : Brightness (%) = float(0,-100,100)
38832#@gui : Contrast (%) = float(0,-100,100)
38833#@gui : Gamma (%) = float(0,-100,100)
38834#@gui : Smoothness = float(0,0,10)
38835#@gui : Value Action = choice("None","Cut","Cut & Normalize","Normalize","Threshold")
38836#@gui : Low Value = float(0,0,100)
38837#@gui : High Value = float(100,0,100)
38838#@gui : Quantization = int(256,1,256)
38839#@gui : Equalization = bool(0)
38840#@gui : Negation = bool(0)
38841#@gui : sep = separator()
38842#@gui : Tones Range = choice("All tones","Shadows","Mid-Tones","Highlights")
38843#@gui : Tones Smoothness = float(2,0,10)
38844#@gui : sep = separator()
38845#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
38846#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
38847#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
38848#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
38849#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
38850#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
38851#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
38852#@gui : sep = separator()
38853#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38854#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38855#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38856#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38857#@gui : sep = separator()
38858#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
38859_fx_channel_processing :
38860  adjust_colors ${1-3} b. $4%
38861  if $5==1 c. $6%,$7%
38862  elif $5==2 c. $6%,$7% n. 0,255
38863  elif $5==3 n. $6%,$7%
38864  elif $5==4 ir. $6%,$7% *. 255
38865  fi
38866  if $8!=256 quantize. $8,1,0 fi
38867  if $9 equalize. fi
38868  if $10 negate. fi
38869
38870fx_channel_processing :
38871  repeat $! l. split_opacity rv to_rgb.
38872    fx_start_mix $11,$12
38873    ac. "_fx_channel_processing $1,$2,$3,$4,$5,$6,$7,$8,$9,$10",$13,1
38874    fx_end_mix $11
38875  if $!!=3 rv a c fi endl mv. 0 done
38876
38877fx_channel_processing_preview :
38878  gui_split_preview "fx_channel_processing $*",${-3--1}
38879
38880#@gui Channels to Layers : fx_channels2layers, fx_channels2layers_preview
38881#@gui : Colorspace = choice("RGB","CMY","HSV")
38882#@gui : sep = separator()
38883#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/15/07</i>.</small>")
38884fx_channels2layers :
38885  repeat $! l[$<] nm=${-gui_layer_name} to_rgb
38886    if $1==0 # RGB
38887      s[0] c
38888      r[0] 100%,100%,1,3,0,0,0,0,0,0 nm[0] name($nm" "[red]),mode(add)
38889      r[1] 100%,100%,1,3,0,0,0,0,0,0.5 nm[1] name($nm" "[green]),mode(add)
38890      r[2] 100%,100%,1,3,0,0,0,0,0,1 nm[2] name($nm" "[blue]),mode(add)
38891    elif $1==1 # CMY
38892      rgb2cmy[0] -[0] 255 s[0] c
38893      r[0] 100%,100%,1,3,0,0,0,0,0,0 nm[0] name($nm" "[cyan]),mode(difference)
38894      r[1] 100%,100%,1,3,0,0,0,0,0,0.5 nm[1] name($nm" "[magenta]),mode(difference)
38895      r[2] 100%,100%,1,3,0,0,0,0,0,1 nm[2] name($nm" "[yellow]),mode(difference)
38896      +[0-2] 255
38897      i[0] 100%,100%,1,3,255 nm[0] name($nm" "[base]),mode(difference)
38898    else # HSV
38899      rgb2hsv[0] s[0] c,-2
38900      r[0] 100%,100%,1,3,0,0 sh[0] 2 f. 1 rm. nm[0] name($nm" "[color]),mode(normal)
38901      r[1] 100%,100%,1,3,0,0,0,0,0,1 nm[1] name($nm" "[value]),mode(value)
38902      hsv2rgb[0,1] rv[0,1]
38903    fi
38904  endl done
38905
38906fx_channels2layers_preview :
38907  repeat $! l[$>]
38908    fx_channels2layers $*
38909    repeat $! l[$>] to "#"{1+$>},1,1,43,7,1,255 endl done
38910    frame 1,1,0 frame 3,3,255 append_tiles ,
38911  endl done
38912
38913#@gui Color Balance : fx_balance_gamma, fx_balance_gamma_preview
38914#@gui : Neutral Color = color(128,128,128)
38915#@gui : Stretch Colors = bool(1)
38916#@gui : sep = separator()
38917#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38918#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38919#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38920#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38921#@gui : sep = separator()
38922#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/01/07</i>.</small>")
38923fx_balance_gamma :
38924  repeat $! l[$>] split_opacity
38925    if $!>1 +!=. 0 *[0,-1] fi
38926    l[0]
38927      balance_gamma ${1-3}
38928      if $4 n 0,255 fi
38929    endl
38930    a c endl
38931  done
38932
38933fx_balance_gamma_preview :
38934  gui_split_preview "fx_balance_gamma $*",${-3--1}
38935
38936#@gui Color Blindness : colorblind, fx_colorblind_preview
38937#@gui : Blindness Type = choice("Protanopia","Protanomaly","Deuteranopia","Deuteranomaly","Tritanopia",
38938#@gui : "Tritanomaly","Achromatopsia","Achromatomaly")
38939#@gui : sep = separator()
38940#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
38941#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
38942#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
38943#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
38944#@gui : sep = separator()
38945#@gui : note = note{"<small><b>Note:</b>
38946#@gui : This filter simulates different types of colorblindness vision.
38947#@gui : </small>"}
38948#@gui : sep = separator()
38949#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/04</i>.</small>")
38950fx_colorblind_preview :
38951  gui_split_preview "colorblind $*",${-3--1}
38952
38953#@gui Color Presets : fx_color_presets, fx_color_presets_preview(1)+
38954#@gui : LUTs Pack = choice{19,"Abigail Gonzalez (21)","Alex Jordan (81)","Berat (10)","Cinematic (8)",
38955#@gui : "Cinematic Travel (29)","Creative Pack (33)","Eric Ellerbrock (14)","FilterGrade Cinematic (8)",
38956#@gui : "InAvision (15)","J.T. Semple (14)","Kyler Holland (10)","Lutify.Me (7)","Michael Ezra (2)","Moviz (48)",
38957#@gui : "Ohad Peretz (7)","Olivio Sarikas (19)","ON1 Photography (90)","PictureFX (25)","Pixelmator (45)",
38958#@gui : "PIXLS.US (31)","Purple11 (12)","RocketStock (35)","Shamoon Abbasi (25)","SmallHD Movie Look (7)",
38959#@gui : "Youssef Hossam (5)","Others (69)"}
38960
38961##### Abigail Gonzales
38962#@gui : Preset = choice{1,"All [Collage]","None",
38963#@gui : "Blade Runner","Blue House","Blue Ice","Caribe","Cinema","Cinema 2","Cinema 3","Cinema 4","Cinema 5",
38964#@gui : "Cinema Noir","Cinematic for Flog","Day4Nite","Eterna for Flog","Filmic","Fuji HDR",
38965#@gui : "Golden Gate","Matrix","Monochrome 1","Monochrome 2","Old West","Science Fiction"}_0
38966
38967##### Alex Jordan
38968#@gui : Preset = choice{1,"All [Collage]","None",
38969#@gui : "Action Magenta 01","Action Red 01","Adventure 1453","Aggressive Highlights Recovery 5",
38970#@gui : "Bleech Bypass Green","Bleech Bypass Yellow 01","Blue Dark","Blue Shadows 01","Bright Green 01",
38971#@gui : "Brownish","Colorful 0209","Conflict 01","Contrast with Highlights Protection","Contrasty Afternoon",
38972#@gui : "Contrasty Green","Cross Process CP 130","Cross Process CP 14","Cross Process CP 15",
38973#@gui : "Cross Process CP 16","Cross Process CP 18","Cross Process CP 3","Cross Process CP 4",
38974#@gui : "Cross Process CP 6","Dark Green 02","Dark Green 1","Dark Place 01","Dream 1","Dream 85",
38975#@gui : "Faded Retro 01","Faded Retro 02","Film 0987","Film 9879","Film Highlight Contrast","Flat 30",
38976#@gui : "Green 2025","Green Action","Green Afternoon","Green Conflict","Green Day 01","Green Day 02",
38977#@gui : "Green G09","Green Indoor","Green Light","Harsh Day","Harsh Sunset","Highlights Protection",
38978#@gui : "Indoor Blue","Low Contrast Blue","Low Key 01","Magenta Day","Magenta Day 01","Magenta Dream",
38979#@gui : "Memories","Moonlight 01","Mostly Blue","Muted 01","Night 01","Only Red","Only Red and Blue",
38980#@gui : "Operation Yellow","Orange Dark 4","Orange Dark 7","Orange Dark Look","Orange Underexposed",
38981#@gui : "Protect Highlights 01","Red Afternoon 01","Red Day 01","Red Dream 01","Retro Brown 01",
38982#@gui : "Retro Magenta 01","Retro Yellow 01","Saturated Blue","Smart Contrast","Subtle Blue",
38983#@gui : "Subtle Green","Yellow 55B","Yellow Film 01"}_0
38984
38985##### Berat
38986#@gui : Preset = choice{1,"All [Collage]","None",
38987#@gui : "Brown BM","Cine Blue","Cine BM4k","Golden Time","Green and Orange","Monochrome","Sevsuz","Sunlight Love",
38988#@gui : "Western","Western Lut 2"}_0
38989
38990##### Cinematic
38991#@gui : Preset = choice{1,"All [Collage]","None",
38992#@gui : "Deep","Dimension","Enchanted","Flavin","Frosted","Shine","Ultra Water","Wipe"}_0
38993
38994##### Cinematic Travel
38995#@gui : Preset = choice{1,"All [Collage]","None",
38996#@gui : "Blue Cold Fade","Bright Teal Orange","Bright Warm","Clear Teal Fade","Cold Clear Blue","Cold Clear Blue 1",
38997#@gui : "Deep Blue","Deep Dark Warm","Deep High Contrast","Deep Teal Fade","Deep Warm Fade","Faded Green",
38998#@gui : "Greenish Contrasty","Greenish Fade","Greenish Fade 1","Hard Teal Orange","Neutral Teal Orange",
38999#@gui : "Neutral Warm Fade","Smooth Clear","Smooth Green Orange","Smooth Teal Orange","Teal Fade","Very Warm Greenish",
39000#@gui : "Warm Dark Contrasty","Warm Fade","Warm Fade 1","Warm Neutral","Warm Sunset Red","Warm Teal"}_0
39001
39002##### Creative Pack
39003#@gui : Preset = choice{1,"All [Collage]","None",
39004#@gui : "Anime","Bleach Bypass 1","Bleach Bypass 2","Bleach Bypass 3","Bleach Bypass 4","Candle Light",
39005#@gui : "Color Negative","Crisp Warm","Crip Winter","Drop Blues","Edgy Ember","Fall Colors","Foggy Night",
39006#@gui : "Futuristic Bleak 1","Futuristic Bleak 2","Futuristic Bleak 3","Futuristic Bleak 4","Horror Blue",
39007#@gui : "Late Sunset","Moonlight","Night From Day","Red Blue Yellow","Smokey","Soft Warming","Teal Magenta Gold",
39008#@gui : "Teal Orange","Teal Orange 1","Teal Orange 2","Teal Orange 3","Tension Green 1","Tension Green 2",
39009#@gui : "Tension Green 3","Tension Green 4"}_0
39010
39011##### Eric Ellerbrock
39012#@gui : Preset = choice{1,"All [Collage]","None",
39013#@gui : "Avalanche","Black Star","Helios","Hydracore","Hypnosis","Killstreak","Nemesis","Night Blade 4",
39014#@gui : "Paladin","Seringe 4","Serpent","Terra 4","Victory","Yellowstone"}_0
39015
39016##### FilterGrade Cinematic
39017#@gui : Preset = choice{1,"All [Collage]","None",
39018#@gui : "Cine Basic","Cine Bright","Cine Cold","Cine Drama","Cine Teal Orange 1","Cine Teal Orange 2",
39019#@gui : "Cine Vibrant","Cine Warm"}_0
39020
39021##### InAvision
39022#@gui : Preset = choice{1,"All [Collage]","None",
39023#@gui : "7Drk21","BC Darkum","Brown Mobster","Cold Ice","Dark Man X","Film GB-19","Formula B","Gremerta",
39024#@gui : "Hitman","J. Wick 21","London Nights","Louetta","Nightlife","VFB 21","Vintage Mob"}_0
39025
39026##### J.T. Semple
39027#@gui : Preset = choice{1,"All [Collage]","None",
39028#@gui : "Bright Green","Crisp Romance","Crushin","Frosted Beach Picnic","Just Peachy","Late Afternoon Wanderlust",
39029#@gui : "Lush Green Summer","Magenta Coffee","Minimalist Caffeination","Mystic Purple Sunset","Nostalgia Honey",
39030#@gui : "Spring Morning","Toasted Garden","Winter Lighthouse"}_0
39031
39032##### Kyler Holland
39033#@gui : Preset = choice{1,"All [Collage]","None",
39034#@gui : "KH 1","KH 2","KH 3","KH 4","KH 5","KH 6","KH 7","KH 8","KH 9","KH 10"}_0
39035
39036##### Lutify.Me
39037#@gui : Preset = choice{1,"All [Collage]","None",
39038#@gui : "Hackmanite","Herderite","Heulandite","Hiddenite","Hilutite","Howlite","Hypersthene"}_0
39039
39040##### Michael Ezra
39041#@gui : Preset = choice{1,"All [Collage]","None",
39042#@gui : "Deep Skin Tones 2","Deep Skin Tones 3"}_0
39043
39044##### Moviz
39045#@gui : Preset = choice{1,"All [Collage]","None",
39046#@gui : "Moviz 1","Moviz 2","Moviz 3","Moviz 4","Moviz 5","Moviz 6","Moviz 7","Moviz 8","Moviz 9","Moviz 10",
39047#@gui : "Moviz 11","Moviz 12","Moviz 13","Moviz 14","Moviz 15","Moviz 16","Moviz 17","Moviz 18","Moviz 19","Moviz 20",
39048#@gui : "Moviz 21","Moviz 22","Moviz 23","Moviz 24","Moviz 25","Moviz 26","Moviz 27","Moviz 28","Moviz 29","Moviz 30",
39049#@gui : "Moviz 31","Moviz 32","Moviz 33","Moviz 34","Moviz 35","Moviz 36","Moviz 37","Moviz 38","Moviz 39","Moviz 40",
39050#@gui : "Moviz 41","Moviz 42","Moviz 43","Moviz 44","Moviz 45","Moviz 46","Moviz 47","Moviz 48"}_0
39051
39052##### Ohad Peretz
39053#@gui : Preset = choice{1,"All [Collage]","None",
39054#@gui : "Cold Simplicity 2","D and O 1","Retro Summer 3","Subtle Yellow","Teal Moonlight","True Colors 8",
39055#@gui : "Vintage Warmth 1"}_0
39056
39057#### Olivio Sarikas
39058#@gui : Preset = choice{1,"All [Collage]","None",
39059#@gui : "Analog Film 1","Atomic Pink","Beach Aqua Orange","Beach Faded Analog","BW but Yellow","City Dust",
39060#@gui : "Dark Orange Teal","Day to Night King's Blue","DuoTone Blue Red","Faded Pink-ish","Flat Blue Moon",
39061#@gui : "Honey Light","Infrared - Dust Pink","Neutral Pump","Shade King's Ink","Sunset Aqua Orange",
39062#@gui : "Sunset Intense Violet Blue","Sunset Violet Mood","Violet Taste"}_0
39063
39064##### ON1 Photography
39065#@gui : Preset = choice{1,"All [Collage]","None",
39066#@gui : "2-Strip Process","Aqua","Aqua and Orange Dark","Berlin Sky","Blues",
39067#@gui : "Black & White-1","Black & White-2","Black & White-3","Black & White-4","Black & White-5",
39068#@gui : "Black & White-6","Black & White-7","Black & White-8","Black & White-9","Black & White-10","Chrome 01",
39069#@gui : "Cinematic-1","Cinematic-2","Cinematic-3","Cinematic-4","Cinematic-5","Cinematic-6","Cinematic-7",
39070#@gui : "Cinematic-8","Cinematic-9","Cinematic-10","Classic Teal and Orange","Earth Tone Boost","Fade to Green",
39071#@gui : "Film Print 01","Film Print 02","French Comedy","Green Blues","Green Yellow","Landscape-1","Landscape-2",
39072#@gui : "Landscape-3","Landscape-4","Landscape-5","Landscape-6","Landscape-7","Landscape-8","Landscape-9",
39073#@gui : "Landscape-10","Lifestyle & Commercial-1","Lifestyle & Commercial-2","Lifestyle & Commercial-3",
39074#@gui : "Lifestyle & Commercial-4","Lifestyle & Commercial-5","Lifestyle & Commercial-6","Lifestyle & Commercial-7",
39075#@gui : "Lifestyle & Commercial-8","Lifestyle & Commercial-9","Lifestyle & Commercial-10","Moody-1","Moody-2",
39076#@gui : "Moody-3","Moody-4","Moody-5","Moody-6","Moody-7","Moody-8","Moody-9","Moody-10","Nature & Wildlife-1",
39077#@gui : "Nature & Wildlife-2","Nature & Wildlife-3","Nature & Wildlife-4","Nature & Wildlife-5","Nature & Wildlife-6",
39078#@gui : "Nature & Wildlife-7","Nature & Wildlife-8","Nature & Wildlife-9","Nature & Wildlife-10","Oranges","Portrait-1",
39079#@gui : "Portrait-2","Portrait-3","Portrait-4","Portrait-5","Portrait-6","Portrait-7","Portrait-8","Portrait-9",
39080#@gui : "Portrait10","Purple","Reds","Reds Oranges Yellows","Studio Skin Tone Shaper","Vintage Chrome"}_0
39081
39082##### Picture FX
39083#@gui : Preset = choice{1,"All [Collage]","None",
39084#@gui : "AnalogFX - Anno 1870 Color","AnalogFX - Old Style I","AnalogFX - Old Style II","AnalogFX - Old Style III",
39085#@gui : "AnalogFX - Sepia Color","AnalogFX - Soft Sepia I","AnalogFX - Soft Sepia II",
39086#@gui : "PictureFX - Faux Infrared B&W1","PictureFX - Faux Infrared Color P2","PictureFX - Faux Infrared Color P3",
39087#@gui : "PictureFX - Faux Infrared R0a","PictureFX - Faux Infrared R0b","PictureFX - Faux Infrared YP1",
39088#@gui : "GoldFX - Bright Spring Breeze","GoldFX - Bright Summer Heat","GoldFX - Hot Summer Heat",
39089#@gui : "GoldFX - Perfect Sunset 01min","GoldFX - Perfect Sunset 05min","GoldFX - Perfect Sunset 10min",
39090#@gui : "GoldFX - Spring Breeze","GoldFX - Summer Heat",
39091#@gui : "TechnicalFX - Backlight Filter","ZilverFX - B&W Solarization","ZilverFX - InfraRed",
39092#@gui : "ZilverFX - Vintage B&W"}_0
39093
39094##### Pixelmator
39095#@gui : Preset = choice{1,"All [Collage]","None",
39096#@gui : "Black & White 01","Black & White 02","Black & White 03","Black & White 04","Black & White 05",\
39097# "Black & White 06",
39098#@gui : "Cinematic 01","Cinematic 02","Cinematic 03","Cinematic 04","Cinematic 05","Cinematic 06","Cinematic 07",
39099#@gui : "Classic Films 01","Classic Films 02","Classic Films 03","Classic Films 04","Classic Films 05",
39100#@gui : "Landscape 01","Landscape 02","Landscape 03","Landscape 04","Landscape 05",
39101#@gui : "Modern Films 01","Modern Films 02","Modern Films 03","Modern Films 04","Modern Films 05","Modern Films 06",\
39102# "Modern Films 07",
39103#@gui : "Night 01","Night 02","Night 03","Night 04","Night 05",
39104#@gui : "Urban 01","Urban 02","Urban 03","Urban 04","Urban 05",
39105#@gui : "Vintage 01","Vintage 02","Vintage 03","Vintage 04","Vintage 05"}_0
39106
39107##### PIXLS.US
39108#@gui : Preset = choice{1,"All [Collage]","None",
39109#@gui : "Amstragram","Amstragram+","Autumn","Cinematic Lady Bird","Cinematic Mexico","Dark Blues in Sunlight",
39110#@gui : "Delicatessen","Expired 69","Faded Look","Faded Print","Hypressen","Magenta Yellow","Metropolis",
39111#@gui : "Modern Film","Newspaper","Night Spy","Progressen","Prussian Blue","Seventies Magazine","Street",
39112#@gui : "Sweet Bubblegum","Sweet Gelatto","Taiga","Tarraco","Unknown","Uzbek Bukhara","Uzbek Marriage",
39113#@gui : "Uzbek Samarcande","Velvetia","Warm Vintage","Whiter Whites"}_2
39114
39115##### Purple11
39116#@gui : Preset = choice{1,"All [Collage]","None",
39117#@gui : "Going for a Walk","Good Morning","Nah","Once Upon a Time","Passing By","Serenity",
39118#@gui : "Smooth Sailing","Undeniable","Undeniable 2","Urban Cowboy","We'll See","You Can Do It"}_0
39119
39120##### RocketStock
39121#@gui : Preset = choice{1,"All [Collage]","None",
39122#@gui : "Arabica 12","Ava 614","Azrael 93","Bourbon 64","Byers 11","Chemical 168","Clayton 33","Clouseau 54",
39123#@gui : "Cobi 3","Contrail 35","Cubicle 99","Django 25","Domingo 145","Faded 47","Folger 50","Fusion 88",
39124#@gui : "Hyla 68","Korben 214","Lenox 340","Lucky 64","McKinnon 75","Milo 5","Neon 770","Paladin 1875","Pasadena 21",
39125#@gui : "Pitaya 15","Reeve 38","Remy 24","Sprocket 231","Teigen 28","Trent 18","Tweed 71","Vireo 37","Zed 32",
39126#@gui : "Zeke 39"}_0
39127
39128##### Shamoon Abbasi
39129#@gui : Preset = choice{1,"All [Collage]","None",
39130#@gui : "City 7","Coffee 44","Date 39","Day for Night","Denoise Simple 40","Desert Gold 37","Directions 23",
39131#@gui : "Drop Green Tint 14","Elegance 38","Golden Night Softner 43","Golden Sony 37","Green 15","Happyness 133",
39132#@gui : "HLG 1","Industrial 33","Morning 6","Morroco 16","Night King 141","Rest 33","Shadow King 39","Spy 29",
39133#@gui : "Thriller 2","Turkiest 42","Vintage 163","Wooden Gold 20"}_0
39134
39135##### SmallHD Movie Look
39136#@gui : Preset = choice{1,"All [Collage]","None",
39137#@gui : "Apocalypse This Very Moment","B-Boyz 2","Bob Ford","Life Giving Tree","Moonrise","Saving Private Damon",
39138#@gui : "The Matrices"}_0
39139
39140##### Youssef Hossam
39141#@gui : Preset = choice{1,"All [Collage]","None",
39142#@gui : "Cinematic Forest","City","Darkness","Hallowen Dark","Sea"}_0
39143
39144##### Others
39145#@gui : Preset = choice{1,"All [Collage]","None",
39146#@gui : "60's","60's (faded)","60's (faded alt)","Alien green","Black & White","Bleach bypass","Blue mono",
39147#@gui : "Cinematic-01","Cinematic-02","Cinematic-03",
39148#@gui : "Color (rich)","Faded","Faded (alt)","Faded (analog)","Faded (extreme)","Faded (vivid)","Expired (fade)",
39149#@gui : "Expired (polaroid)","Extreme","Fade","Faux infrared","Golden","Golden (bright)","Golden (fade)",
39150#@gui : "Golden (mono)","Golden (vibrant)","Green mono","Hong Kong","Instant-C","K-Tone Vintage Kodachrome",
39151#@gui : "Light (blown)","Lomo","Mono tinted","Muted fade",
39152#@gui : "Mute shift","Natural (vivid)","Nostalgic","Orange tone","Pink fade","Purple","Retro","Rotate (muted)",
39153#@gui : "Rotate (vibrant)","Rotated","Rotated (crush)","Smooth crome-ish","Smooth fade","Soft fade","Solarize color",
39154#@gui : "Solarized color2","Summer","Summer (alt)","Sunny","Sunny (alt)","Sunny (warm)","Sunny (rich)","Super warm",
39155#@gui : "Super warm (rich)","Sutro FX","Vibrant","Vibrant (alien)","Vibrant (contrast)","Vibrant (crome-ish)",
39156#@gui : "Vintage","Vintage (alt)","Vintage (brighter)","Warm","Warm (highlight)","Warm (yellow)"}_0
39157
39158#@gui : Thumbnail Size = int(512,0,1024)_1
39159#@gui : sep = separator()
39160#@gui : Strength (%) = float(100,0,100)
39161#@gui : Brightness (%) = float(0,-100,100)
39162#@gui : Contrast (%) = float(0,-100,100)
39163#@gui : Gamma (%) = float(0,-100,100)
39164#@gui : Hue (%) = float(0,-100,100)
39165#@gui : Saturation (%) = float(0,-100,100)
39166#@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both")
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 = value(0)_2+
39173#@gui : sep = separator()
39174#@gui : note = note("<b>Note:</b> The color LUTs proposed in this category comes from:\n")
39175
39176#@gui : sep = value(0)_0+
39177#@gui : note = note{"<center><img src="data:image/png;base64,\
39178# iVBORw0KGgoAAAANSUhEUgAAABoAAAAgCAMAAAA7dZg3AAADAFBMVEUAAAAGBgYwHyUFBAQMDAwuHSMQDg8+LTMICAg2JStDMTfmu5gKBgfs6evgs5Di\
39179# popINz06KS/tzrnnuarUmGxSQEVHOTE4JyNHIxggEBMVCg4NBwj29fXmx8Pu0rzoyrPlyLHoxKzgtKTdr5/pvpzjuY7ep4ritIjbooXgpYTgtYPcn4PA\
39180# lYPZnIHYmn7NkHS6iXS7gGevc1mOVUhVRUiWW0GHSjM4Jy0+LyscGB4TCQkoBwk2Cgju7e7z1NDuzcjnx8bqzMXn0b/lxbvotaPit5PlsZLYrYvnqouY\
39181# jInmrYXjq3/cnH/NkH/iqHzTl3zfpHnanHneona+kXCignDGimvHhWvVi2Z+Z2bMkWW3el6ldl3RflzAgli4bFadXlRrXVNvVlOoak+vY09hU0tYRkuh\
39182# W0NOPUNZSUB+Tz5URDp2RzVcQDVqPi5yOy5WNihHLyAdFxcrChNCGA4aCQphCAjw6unry9HyzMrrxL/wxbfuyrLIr6/iqqWspqXnvaTtwqDnt5/mtpjl\
39183# r5jbqpbfrY/ftYvFm4e5mYWWf4HYoYDEi3uPg3nGm3jYjXTEkXHOi3GFdXDDjm/ThW+6e2+vfWzSlGnakWmzdWiZc2fOi2PGe2HCh1uoaVt2Y1iZa1fB\
39184# dlZ4VFOIXlFbSlCUWEyHWEhmS0aST0WBRDmPSzhvQjJaPCl3QChmNii6EiiiFyY0ISGQEB+bCxswHBlcDxh3CRIgDBEzEQ7g3Nzc3Nzu4tbevcPw1L7S\
39185# yL3TrqzUq5mamJnCp5XCp5TZpJDroonQlYXon4S+hoOJgYPqnX3clHzAlHqBdHbboHTBgnTYnXG+f3FxZm+6j216YGnLhWfUg2WlcmSzcGSWa2N3ZmPE\
39186# iV3IeliJXFW2e1HKdFGmVVGSYEyGU0toTUugaEqsYUpwVEqhYEh5V0Z7U0bIYUOQYUN/KUNIPkKGTD9vQzyJQjppPDhWOTWZMC7AGC0qJyhfLyRdLyA/\
39187# IBgKBxgwJhcvIRcsEhdFKRSCDRRICw5SCAxjV0wNAAADGElEQVQoz23NBUxbYRAH8O9JX1+FlrrhMIa7O8PGcN1wBgx3xtzdBXd3GQ5zd3d3d3d/Lcmy\
39188# LLvkkrv/L5cD9nIJqbF8MguGYZIgORkmwZbkjZ5TklpWgmlrvvJjrWC+FQmGE9YkUGGYRTYza0yaMgnMSxVZ8QdYG80oMCzg95JIMIVFpVLlCfKMPV4j\
39189# gGFLEkyEFBJJamQyVd7ME2yx35FqRaFQWNKdSiZbUiyJQT6JNwnIKRy6/jL9TUtN8Iq9e1YYm9Q21NdPvsLj8SYDua1HJq7za7iopqKswlZ119TW2jd3\
39190# 55yV5xoJGjfxyQVmxC62Enscez5jkdtCzkxFJ6dtepeB/azImwFaZ1W2KykvXLreJDxoCcdZcfp0R06B9CpybFCQipIqw+NYxO1m8zg/jouiotPcOiDn\
39191# rH1Qe+1aZSVVLcMDh8+UW8SFaKrrFdemGIE5b33Ga2hoqLKV3Zeu8lle1J6oubg4SyjM0wPPvt3Zr0Fzmz97Ns1jlWFoUVzoIjXT7C+f7PSAnWhA352h\
39192# 47ZgAc1D08/QUH/xLE5zRuf3vAIwbJPtPWECgzZmLJOppau7xMVxhpppZkZ38HIgpqcH+MtoPJPpfdTVwcFR3byrK2XzNCBBOku4XsukpO29odDV2WGm\
39193# +kMLi2AFBYAiwmsV/jq0MTq+3POF61xdZqipJ7a/uLubIMg2MbLUl7FM5wQ3QFfXIISpv9rcIoslP4+gqd3RpYFeXr5cAwMDfZ+Tp8ti0l7nWL9/DlCc\
39194# PtIWdSrQPzAsJHTDeqOy6hjTjF4Ms7YGGHGWZ14Rxi2JqqqLvx9/KcY0LTsHQzFMSnRb4b0bYVejqioro2+1ZnZ8HBajECQGOIqi4qnCpybG4eHR8a2v\
39195# 2jI/iDAUgXARQHAcsxHTR9IiVhsZmzR1ZAls6CiOQLk9ACAQKpFAuERQbmRc3ZSSboNDOA5Bg+8AABAKAQhCEIz34NHj5BxIVrl9mwgiYqKBtFAMQmRC\
39196# /9UvJYAgyKjIFpnYDfX3/In+NsQ2/0ff4L8w+sAufyhX9F+S/My3/Wz9Gwm+5rUIT8ACAAAAAElFTkSuQmCC"/>  \
39197# <a href="https://www.abigailgonzalez.com/">Abigail Gonzalez - FreshLUTs</a></center>"}
39198
39199#@gui : sep = value(0)_0+
39200#@gui : note = note{"<center><img src="data:image/png;base64,\
39201# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAADAFBMVEXU1NTV0cfT0MXNyb6fmo7V0cvX08i2trza1tDX083UzsHQx7YSDxHW1NTb19PM\
39202# ysjKxb68uKqdnJ1MRUjO0dnKzdna2NfBws3BwcnTz8fOycHIwLXEu6+hn6BkWVdBPD8uKCvU1+DX1NHT0dHIydDZ1c3PzMu9vsm9vcPQzcLJxsK1tcG4\
39203# uMDTzL3Iw73OyLvLw7nNwK+sqau+tqmZl595dntoZWZsWlpVUVNVTUxIQ0M6NDclJCchICQgHh8dHB4FBAXW2+TRz8/Y1MnKyMXNxr3JwrjOxbe2tLOs\
39204# rLLIwKyjpKzFuqq7tai5s6SNi4+emI2clIiLgH1zcHt8dHJxbXJ4Yl1ZS0hQR0gtIiUYFxoNCwzh4+bQzs24ucXV0MTRzMSws8TIwrrTyrmurrjMxLXF\
39205# vrSxsLTLw7KoqbKlpKien6jHvKePjpmZmJaWkpCIhYySjYuOiIaZjYWIgYOCfHungHiTfXB3cXB2aGKDa1xnUlFUREh3T0MzLzM5LzA6JigoHR8ZFRfP\
39206# 0t3f3dvCx9jT0tbEx9TT09G7vs/Fxs24vM2jqsbKxru7ubaxra60r6eop6ehoKa5qZqVk5qblZWlmpPGto6dloqjlIaAf4OcgHqWe3mBd3h3bWuMcmqD\
39207# cGZqYWBaV1uDYVN4X1NfTk5tVUo6OT5ZQD1MPDpANDNILCvm6u3i5urR1N3W1tbKzNPU0M6wts7LyMvDwcPSy7+xsru+urWqqK/AtqyamqzOrqq8rqm7\
39208# pqjayafDvKW3rqG7vJ+zqp+upp/Ir5ivpJetoZewlZSJipSymI24kYuvi4iTin6RhXqvg3iScnFtaG+YfG5hYWqXcWRuZWR9ZmGMb2BhXFuKYFlzXVho\
39209# W1RaVVJhTEJOQ0JIPj9nQz3s7+7Dy925wdq+x9nBvb24srbYyq63s66rq63Qv6i9qKTZxqKqn6LFup3Lu5esr5aej5bjx4yDgoyqnoeig4a7qYCfjoCb\
39210# i3qkiHh5amagcWN/XmNKS1mEXExqRkNuRDpq42GmAAAD9klEQVQ4y0XRZVRTYRzH8YfpHBtzzAUbsQIcsEA21w0ijXSjlHR3p510d5fd3d3d3d3ddxw9\
39211# ft/+Pud/zz0PSElJwaWeOJGZ6eXV5Ok5/n+enp5NXl5ewMnJafm+g/v37dkzw93dfdLfpkK5u8+AAhtYLPutU7eaxBg7LFs27l9ToBwdo6Pj4oC9vr69\
39212# iYmxzfRxVlYwWEy6b0aVoY4ODAZDobjcKD09wGIy9bW7kRVMB87Ku3xdcj0dOWGCDoTCUFwI2M+cyYpxgHZDOBJJ6aBKZTKp9WTktGmQCUONAbtI6IB2\
39213# 1zWtz/wqkcnwj3V1J0+eBp0Jm6IH9O3sIh1sjLQ7Zm3l9mOdVwvxVZh/AmUFAbNIh+nhcKQpxsB0/fZjp84XyBowkNAC6DNA38zMOtIoHA7tc9fuPHw8\
39214# 51LBtR2Y8Og4Fxc3N7cpcAgstF5mZGhqYDA31G5nW2th4dWL4RiHR7sO8FxckkyQWoDFjoH5hIeHPxz3P93lv1nX5vnbp4de73WBABNoAdxg/nqSBb/9\
39215# 4umiInE/hb85apfe+727eW5wMBMALJYZSjAnW8ya7SH+XiSn+XngKpebJO1O4r2CgNlEgGWFhhJJi8nkWQJqEQ3vc0ZIiXV64hQdx0uaqgPAxInWTMZK\
39216# 4mJzS/NZbB+xWPguTeCMQCQkxBgbG48zHANzCSuJa9ZYkjikbf5ZyYIt0D47gWINC4PeTAuwd+bYziOYW3LI5hHJiRwEYgsC4VpNWY6CggFIrF4xhzhv\
39217# 1WIOmWy5yZVjsYDEdmW78nHVXC5340YIrF7BWDBvCXRhQ32qd3osAvqbxNnOfBxfz9HR0QaA+bYM0oJFi+YQLCy98096p1NiN8UnxnNicdW1tZ+aDwFg\
39218# e/d+xKIlEbYEEqIjv6E+D+fs7Jp2hF2Do3xuPlhQAJg72OuWLl1HtCVa1Hg/qEwVCeLjhXSVpKYOFxXFG+gDz3Jaty29t4TBILJ9qb77M0QeAgFNHRxc\
39219# l5z6srGxbwAcPdv2JuJICmMVOZsaIM/PvixMSxsMRquTPYS1Hxu/nAWtnW0vqlr4CXmBtJISekC3RCQS9SiUNJ8WYW7u0QM80OmdleEbKJUGyvuHRuSB\
39220# eLG/f++AQlE6iu++MZTb2wHEed1U/K1bN/tzTn37WUyT3i4u/j2sVKvKy+hB5ddunAGBFy5QAwKaL13JOX+l+HZQRdnoaBm9RKGoGBkevtmVKwM+Pnh8\
39221# aUB+V2lhX++voSANGh2iQSuVwWjqycymBj8V+JHooZS2l44Eo4PKystDNJqQkBB0j192VkZWtm+7Ag0GW+okSppcTqfTg1TqCnSFWqVSlwxKzp3z8xP1\
39222# aDR/AFuiV5UIiV63AAAAAElFTkSuQmCC"/>  \
39223# <a href="https://freshluts.com/users/1">Alex Jordan - FreshLUTs</a></center>"}
39224
39225#@gui : sep = value(0)_0+
39226#@gui : note = note{"<center><a href="https://freshluts.com/users/10185">Berat - FreshLUTs</a></center>"}
39227
39228#@gui : sep = value(0)_0+
39229#@gui : note = note{"<center><a href="http://fixthephoto.com/free-cinematic-luts">Free Cinematic LUTs</a></center>"}
39230
39231#@gui : sep = value(0)_0+
39232#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADkAAAAgCAMAAACfHyDkAAAC9FBMVEUUI\
39233# TIXITMfITUcITQfIzYhITYZIDNBMHbKPi/HKE3HLkPGKn9BL3PIMj/JODXJOjLHJ1TKRC3HJXxrOIfIJnZFLm/HKFzHK0fKPDDHJ23HJ2XINDzJNTnKQ\
39234# S7FLoJpPXYmJUgiJEEeIjrJNzcrJDTKSS7KQy1xOot1QIdiNIRWMH1KMHlFL3UuJlAmIjbNXTWKR5GFRpB8QY52PYtmNYbFMoRdMoHHJ357QHpPL3pdO\
39235# HZQMHUpJ08cIz7MXDzNYTdKJjbKQC7ENYbHLYBfNH1TMHpaNHlVNHZKMHJGLnJCLGrHKGrHKGHFT1YrIju+NzfNVDXKRDUzKTVaLDLLTDCRSZKBRJCCR\
39236# InGVYZuPYR+QYJyP37IJXrIJ3NQMnE8LW2FR2nIT2bHKklCI0MqJkHOWDfIO4FnOYBYMH9qPHyXSXpjOnnBJ3Z2QXJbM3FML2WUTGMzKl7BKFktKlg0K\
39237# Fg2KFJkJVJALU/KSU68KE7IM0szKkouJUeLJUXIQELMUDsZIzmuVDifUDiXLzh+QzeNSTZLMTaZSZJ8Q4vJToe+NIbKSIOyMX7BKX2kTHqsLnrLSXdvP\
39238# nZUM3BvNm9gOGyNSWtWNGtLLWuFMGc4LGeUK2evKWdyNWS7UGBXM2A/Kl/JPV5IMVt3JFmqVVSjMUy9WEbLVkO3LkJsJEE1JEDJPz08Ijk1IzZjOTU+L\
39239# TWGMTPMUTLJQDB/NjCgOS+/QC6kSI+HRYqZR4e4QIe1UoasT4G8MYB1PHqGOnfFUnWSOnWaMHTILnDJO2/KRm22KWynS2qlLWqeS2hnMmfIL2ClJ16EM\
39240# VlOL1m4VlO3LFKvJlLJQE2zWExfN0vMVUq/LUZ6JkTEXUDLST2uND2VTjnDXji4VzhvQDZaNzVpODS1QC6sPYefP4KfP4FzNni2Jne6UnOkR3GqNHBqM\
39241# XCuT2WIJGBlKlyxUlmMQFjHK1aIJVWdS1StTU+RM05VJE5zP0WCJEJYI0BTIzzFWTSmNzHHTC7FQC5jTTRlAAAEvUlEQVQ4y6XTdVRTcRTAcd7e3htjU\
39242# 8E5kBkLN2ADBgiyMWCEdJdSSiMCEkqISimK3d1SAlIidnd3d3d3/uP9bXAQD/qHfv/hnnv4vPvgbBqUzqN2EtGh/5A4jncCCaEwKKjx1s2JN4PUyu/8t\
39243# WsNQSpS+3HCpFIpSKiDCWosvXV74qQJ4687ODiYmY1vRHD0inMwTwInPRlz2czsa8MvkqAGlTYgceMGGLPEgPUhISEi0ffbCMoXOqwXie4AXCWvMBOJR\
39244# BtAoqSlEyeMVyqViYmJASBEOn36GBgY6KjaQFBHb5ZXBahG6YLQGGWIjs7GYLXkn69SFhVdCQwMbG7uqYlycuoDIb8xmDouIiJTKYLRkTi5ObIiUcfAo\
39245# EVIaJAkbur1du26fv26orqhekKaKPAtwoTI0LRzAU5OzU3C0aERMZdDnJx+BFMoIE3Z7HeFvaDevXv3gwcg73/Vv5UHU1dZRGYqAzU17zj6RYbOrQiAZ\
39246# YsQSYGXJ/tVoVYXFPDqyvcnVsx/+mBPoOr6N+E4IyNuVRGMTUSZRWTMWtj7w0mQdibGnosq9bRUnWAyaTQTYxvr/lsK0Xn/YGLRlORM+GO6+tcnwDNOF\
39247# cG2RKiWTGMbj6On4/Sgi0fVUsIa/KwQvX0JnAxPO3UF5iYiY0pyzIV1MDpS2qSra9SKMXQ6PS6ucrWpHY+/WmyY4tsF8nfE041SMy/AeLUuPzmc+zIPx\
39248# hIKDmmQfJDDXaOWjNKmQ5+kJPzPxJaH8rSgEjzfMGmu71kYL1EyLFMX+o7V0qp2xNUSH2lsM9zaPX2ZrjY0pgYncS9xhO/YOHh5R8G8cMWRvLF6etX1C\
39249# awk7so82BZIWyUpiAYpdks/7OOjq6s76jPOk1gd8qVDBdIcVmrayrMw1lAyWAo4SaePgZOtkuR5SqzFVm7zl3M4Pj4+Z+KXSnbGrkH3v1DmJSmOxL7W1\
39250# j5Tn+/iwl2JtgVEq8Qgno21mMVym39Q39vbm3M83eVx7DE4X4DnWLmlxa6BF6khFlspUtD2dB3eJlHZErGVIUuxa8TQofr6+k9m7T7A4XBG1QlsXdxnH\
39251# fDmcI778cRu3Ef74aUuEaQ6tRSstrZiGYan7hnRAxo6Z84+fX3vN5QcF3f57v0w5uKLrd1T0HZ5PNkmIUQXg7Q04u7tjhoyBPzBeL6tq2L7dHjScsJUY\
39252# ssNGwZjOd4u1ZTvBrK/0dzZMplMrZ9TsofbysMGdh8yIlfgJYlKmcqA0Y/sKKF8F5CDp8hnMFTJHvrZ29q6b5vOkMmW+cHJnWHDGLKZ5STWllqiclggL\
39253# UJ37RgIMRjlWLarhxx+nTHzA+lpEzVrKiwP12KdSCzDEKS5+f0BfaHZtXy2R9T26TAuo5raeCjChvXtOzOX7FQKFln2tzAfNHmr84ABO3LhQ8metw3GG\
39254# fH2Xh5s7lQY9xJYJxLiJyE5aNNWZ+clhL2JJ9sjYprzjBdUUxM22/3etGmz47GOsr2E8MHmcHTT3SVUjZEmbPRVZWdjxUxPJg1GLx72R4mNS0avu2UVV\
39255# cM+mkmDou0wDR5NNS7lY3+UkKBswYKyBNgWL6XRmNEj7WHHZ6LRrhj7q2zPPivLDhyKl5XFK4afv8l/7iccu3t+6IPmuwAAAABJRU5ErkJggg=="/>&n\
39256# bsp; <a href="https://911templates.com/30travel_presets">30 Cinematic Travel Color</a></center>"}
39257
39258#@gui : sep = value(0)_0+
39259#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAgCAMAAABAUVr7AAAC91BMVEUh\
39260# ISH9DA39+QAhJCkpKSElJSElJDMxMCJFQCE4NyEIXKcmPyIqNSH80gUTJrp8HLEmJ04fOEgzJ0EiLDUhMi8hIib8ahT1phACygX5xwQFsdYbJKUjLj8gN\
39261# TlfIiw2KyJfWB5oWx38gBhCSdiIG7tyGqsUH6TzEJnsEY49J00dRUr8Nzr31DA+JTAu5i8hKC78xyEzRh/3tBj3rBcArwYFvwRr0gEq1vEBgtEBo8wDcL\
39262# 4TJKxsGqPnEoPlGn/kE3cZVF9HK0ovJjYsLS4xIitTJicqIiYhSiL5mCE+PiFgOSFIRSBWUB/6vB78jBuEcBkHtQj5ywOT3gAAxOAAu9sueMsCZ7UUIbA\
39263# EU50yN5cdO1dWJUFKJTexKS4nMCr68icvKiNBLSJIMSEiWSBRSSBgSx/76R1lZRyUjhf8dBZg0wj37QMBuAKk4gF81wEBqcyLPskCecj4Q6buOZnWO5Ic\
39264# SXghJXOdJmhoKVptIUgbVz5SJT79jTd59C1HJSv9/Cjg2ya9aSZTMyK/uCEffyAfah5PWh6UGx40Uh00YhuDfRl0dRj6Uhf83QZazgTt9QGD2wAAzOadS\
39265# OKURNcCrdABw80ksMYtbbgTVLZVHa0bmKtwH6u0FqkLv6CkFpVnM5QZWZPPEocdK4YtNIMacYFjH3BPJ2ruLmkcNWFEJF01JVdTKlUfL1RaJ00HpkH1DT\
39266# 47JzzyijrlfjUfPzL16S7gzSv8fivetijNpSRz7COARSKBJCKXeSGgKCGskyBFkiD3CyBtOB8+VB39+RfU0RbGwxZMbBYMkxFxjQz82gjx1Af43gRGyQE\
39267# sseIXvN1dStM3aNCGNMYCkMQkbr4DhrhwHrh+PLfBPrNQKbAApq85IK4mZ6MJkp0OaYXxEHgpxHITZmoBtmgZTmD0TzyeFzQ62y1p3Cpm1ypixidXuiXS\
39268# 4yObYSOSUCLLhx5LVx6R7B37nhx9XxyNWBs+dhpA0RlYfRn8MxEUnw3l8gaOywaizgW15gMlxgLHnPo6AAAA/XRSTlP9/f39/f39/f39/f39/f39/f39/\
39269# 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\
39270# 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\
39271# 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39wCUWrAAAA0ZJREFUOMt103k803Ecx/GFbLNpmTW1\
39272# piHLJmoSMxlmreauoQO5ReQoldwlUpR03/fpzH3kKDfRfd/3fd/nH31+3x8P80evv5+P9+f72EHoa+i0aVNNU1NlYyCZLCiQE6A2lKAUCAC+Pj71WfOgx\
39273# vpmp/QgTsAgg0BC/JLj41FPyleeckoP5MCQkkhKiI9bHHd8CGpddXIZoKABM2JqadLyJYvjbO3szuCEyaxOLn+LmT6hTpeAACAWn0SEuYDJZCaXvQklEd\
39274# Vw4cA/FnvD1s5WLOYh8t3AYAEguUlDqCMRO6XqQM+/vnn3Plsxj8e7j+64uQEy0DGpcg8hq6Ez/P2baDbcfTyekZER9hgDORg3cx0dk1p9Z5hRVacf23z\
39275# Jhsul7QFx8GlWVuNKuVz+0XWSuc4kkwYXshrcgZHdNlwazXDPwfgE+HCdmj+/Mxe4umIzNfokIkGdXhAbi4Qh7ejy4iRfmSw99FOtQIDPwGsI6sOXXbi5\
39276# kwvC8GhhYbGPb2pgICnUvQYnlU0ujkAOjx09bvTtndds8vkSicTUlMMhh+g3CQSjIP9VeiQgc4FMmT1s2KECPl9S6iANCCCS9LK7uzU1Z46aX6WvTJYNp\
39277# 9PpDlKplEh21nM/ux6RFYNJEZutrS3186M6uvyHHDqhxdbVZvhRYSQsvAcnbYgcxsgOayvrpd5augwgZu/DctpVIiN7NGfiz2WfuLhDY5b1IiurA4+NgV\
39278# AoZqtzwiNVsCL9w/XIBFV20XYvDdzcszeuANHS0dWmgmqtzA4Boq211EtjDmZYHncfJgYHr+3o+v1nAyJp7WHORILqRLb39itzwIg8Nm7cWtLZ+eNXVJQ\
39279# iCjOt/uHY1ziif+ayp6eHx9aRUF6UQpGnAJO2Kgz7MaCZA14aV7ewWJ6eezHSq8iLtrSM/pm2IgcbgWDG+845ligG0CML6G80iNzcD6+zV+M/THTKfpvw\
39280# vEgUE1OyEJphaRmRG5Hy/BuI/r/ARF1j+11CIUt0azKqNyKiLuXllxYzKhLoFGaObBMK91rMgCy+1qW8OL12DYUKV5SMFqBdDyagnr0CABMDAn8PoMTEz\
39281# MyMjIzM4OA1ZpTBAh9iMBgVlOlYFApVCfwDpbUmiFdSYzcAAAAASUVORK5CYII="/>  <a href="https://rawpedia.rawtherapee.c\
39282# om/Film_Simulation">RawTherapee Film Simulation</a></center>"}
39283
39284#@gui : sep = value(0)_0+
39285#@gui : note = note{"<center><img src="data:image/png;base64,\
39286# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAC91BMVEX+/v78/f4nKTn+/f4nKzspLTxJQkQlJzh3odlzndQyM0EsLzwlKDUlJzD6/f6X\
39287# xvmUwfWItuuFsuh9p907OkExMjw2MzUrKS/v/f6PvPGMue6CreNuls5wXVpaT01SR0c1N0Y5OD0mKDcoKzUiJDX2/f2cwfWXu++KfXOHdW+UdWiDcWeQ\
39288# cWSEa2JxZmF9ZlxpXVhtWVRUUVJGS1A9PkdDP0BAPEA/OjwrLTsmKTosLjgiJS/R9v3B6P2byv2lyPv6+/pvmtBskstNVGRnX116ZFx1X1hGSFdaVFFh\
39289# U1BOR0xBR0xeTkovMEBFPDw+OTkxLzcpKzen1v6j0v6gzv6ozP6t0f2fy/3I7fvr7O2Ltet/q+J9p996o91rkcZpjcVlisJfhbxhdY5oeo2ejYBbX29o\
39290# bW57YVlfXFdlWlR3XFNlU0xoUEtLTkotL0hTRkQ2NUIsM0ErLkFFRj8vLjO23f6s2v6x1f7c/f3h/P274P2w3/yQtuyEr+VwmtJwj7uJna1ogaWYnZtr\
39291# gJt1hYthboN5gYBvdn2WhntaZXpda3lsd3NPXG12bGl7b2aKb2R9bGN5ZmOAaF9NUVpgV1dPV1ZUWVJTTkxXSklbSkZNRkQ1NTs7NTY0MDEoKzG32v7X\
39292# +/2exficw/iUu/D28+7Y3N/t5NyNstrH1dh0n9fZ19XEzM7u28FiisFbgbhZfLSzrKSlpqDLtpi9qJW4oY2Ulo17hYhbbIaki39jaHlMXXeulHVDVmxP\
39293# XGluZmRrYl87SV9YYF5bX1tiXlVnVU8tNkpGR0dFQUY1O0FZRz83OzhEOTfo/P7n/P3l/P3o/fzy+fr8+vKo0/Hx8e+cwezc5+ql0er17t2gx9210Nyc\
39294# udyyzNPT0dKbtc56oM6Kq83z6MyEocqDpMOGnr3izryhrq7Vx62eoKuCj5x5hpvGsZpWdJqil5KomYazloKOeXKPhHGMc2tWWmsqV2qffWZxbWZdZmGE\
39295# Z2A2QlpUXVmBaViAZlMyOlJoAiFnAAADzElEQVQ4yy2TZViaURTHL/i+sIG4OYkVIKAbDOkWN91AWsBtujZm94x1d9jdrru7u7u7u7vjw+6L/j/c++H3\
39296# O/ec5zzPBRs3boiPi4vp171v+3YjhvtNnT596uShQydNWtrTk0lgQ3x8XGxM9yntV4/w83t7KJemNJt3Pu7Yo0ePxUvgsRTEr4+L7Qd5uxHDYn55e3vz\
39297# 3I3Uk4aZ10JDo0JDBy1eAtbHxni437AHx/V6PYVB5ygJisqkO9Fjo6OjokJBLNZ9NeST91D0FArD7co/kaWQW15NGL1w4djoKNA2XbdO3XIplJaCZheb\
39298# xaqVp1btGNy794TRo8cCyLHyTr5TeXpKAd3FljLFTOZBY/HzkBDMARjv1snXt+tIHoVRcKbByjSVlwmFQv6azuMXQQV4+NCuXbsYWuCAOcdM4RVCwad5\
39299# iR9WhAV2HhcyGLQbjvEuHVfWn2U0c+RMcblIwE/c+mLdMhzO5+K4EAC5L+QdnmaeduXX19XKjWX8XVufXcZ5AeADuwCsP+QdkjKtBGm4Kc0iFfN3bbmJ\
39300# AzC4wPPjQCvvs9ImY9WlSCRHU48eFMx78ygMM7zgEwCOB/nAh8aMBodEJLSkphlL+O+3rFvWKlwAEEM+4L6RoGJJSoMq0lIPFAvmJW7bdBUTAs8BWD5o\
39301# 4ID+08Q2p0MS1KvX5zJxyUf+9hmbw+AMuMDOwIPHXJkrPqlmhUOhl0giKhVsnzEE1yYMgnjijfnFUpWmDgpBonKmNVxwD+PQ8AkEEE+8VUSqkqmobGZF\
39302# qcnCYrOk7+AWsMBdgSfTdx46TiZT0wicBllKSobVwbZUrWnlwAve/7LRSC6PnCtnczSyn+np6cdkKcIVONihLdnZeWStFv26T6Th2GSm9Myc+sRNa1dd\
39303# D/MoXjiQl52HomRtrrLWSWtUEwiEnMxtcwrnTFvlWSVYDlAUjSCTecGjqBp6k4vDURP+7k0+kjztro8XDO4lIOpQMh7VQoPmzFGpCBpC0N4f1b9n3b4U\
39304# NmTI8rW7ARFBETyKx0fSSG4H25HhzPhWcthePWvujrkLEmbO3A3weAQPg2hH0YKDqeo/p63S6sN2e2FhQsKC2SNHzgZ4LjR0eIQ0KpgX6VZqmlroarld\
39305# qagxmxULEqBA5OoQBNGS/EnwW0WSaU59AZ126kR+lmJ/TY1h33wQoCPqiBH+/v4kLhS8kbMMKuOU/fuZLMX8I/lmwx4QEEAkQoEEBYTLRfD0ZhW1slFN\
39306# z1Imb042fCnChABiXgTJPyKAFozoUHcTg5pkq7QdmPN6RtL+WUX/AdPoMUu7nvi/AAAAAElFTkSuQmCC"/>  \
39307# <a href="https://www.facebook.com/eric.ellerbrockom">Eric Ellerbrock - FreshLUTs</a></center>"}
39308
39309#@gui : sep = value(0)_0+
39310#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJsAAAAgCAMAA\
39311# AAysvoIAAAAz1BMVEUAAAD///9AQEDQ0NCFhYU+Pj4EBAT+/v6ioqLk5OTi4uL29vYKCgrr6+t7e3v7+/sGBgYnJye8vLxcXFxVVVVra2v09PQ1NTXu7u\
39312# 6srKyoqKghISHLy8u2traysrKdnZ1zc3Pd3d0cHBz4+PhgYGBFRUUrKyu/v7+YmJiIiIiBgYEVFRURERHx8fHp6emOjo5nZ2czMzMvLy/19fXGxsagoKC\
39313# UlJR4eHhSUlLV1dWEhIRKSko5OTnm5ubY2NjOzs5qampNTU3BwcFtbW0lJSWTtii3AAADCklEQVRYw+2XaXPaMBCG9w2OxWHikDRgwBAIR7hvCFfu/v/f\
39314# 1NUiDKFM6TD5QDs8H+JIWsnP7GoHIJwuZ7fj3QoXp0fBuF3S6XF5dju7HcG3uP0IrZkQhUL+NVEuFHog5ioU8EF0EwyqOkR4nAxoRbWRf/XTbw5pTNxNP\
39315# UsrKjxK0wqzc1x2gqUV4d/c7rAmR6QQ4x1RIEHMBAEeUSQYLHWIIfZGTCtTgtD5QUwQ2X4nIcf/u30SYFDxguQmCPb3uRXDQmHX7ZYnR8AzP2rilg4LWQ\
39316# nJh8OZIstxFh/4qYq5XBwoleX1boZXnwGvRUxCzMekMYt5Pk6NV27RsNDY53ZPhh038QNuiBG3BzLoEF0v5xWwiD44YVM97Lqhhbw+ds2PJkf1iOkC6ST\
39317# aLeOW1Iv2VQmqLG55MnyrG9WBDC0USqZgKaKNG2XEnOw4StU8UNi4yV6F4kG31yvh4qBbV+KergM32wfqVNOCARs3x+StDIxoAbx8dWtx4rLa7eVKeP9D\
39318# L3QPuRmGTQnJ1Gr3MwV3QI/Ana5cWlhdqXteLQJtjiUfKOv9qrLtJjMX2s2wt09jcWF+0M2TuKi4GZKfpN10t84gbPepp2/h0oXHNnMg/9XNg1pqt1Jcm\
39319# OxzC9sCHXSrmkAJKc48wNeBpqbh0ch3jZt6mbXZRS7omI9IpVK3CsPstlvfhWppt5wtfGMvJOjSRTKrz1NwK6S5bhs3PoZdoi3drW0E1LfcbE734zF9Or\
39320# A1u26J7bxlJSOPph+H9YTjDCaciqAXblZX6AkbOra4tWzbqfi8KUVH5K0T0Qx23HhW01+7NTtQPZ2uvAJcz9N/64FbdQh3QVTkF6Q0lSRwKwV/jkR0bKx\
39321# M4haLCLm/czOkdtwMy7UbfSp4Uuhe0QXj+hUK3KgLRJ13BfcnCWlgFDSKiuWqhz6z+pY1DdzmVkNXwTI0iaqWVSZBZgW2KViWYyYrJPR7jUY5QYIlx5A9\
39322# t6zU1LIKtGLA8w5ZQmFqLm8iOLb3T31HOjHObv+v2yn/dj5Vzm7H8Qt/zkuzg1TiwQAAAABJRU5ErkJggg=="/>  <a href="https://f\
39323# iltergrade.com/free-cinematic-luts-video-editing/">FilterGrade Free Cinematic LUTs Pack</a></center>"}
39324
39325#@gui : sep = value(0)_0+
39326#@gui : note = note{"<center><img src="data:image/png;base64,\
39327# iVBORw0KGgoAAAANSUhEUgAAACIAAAApCAIAAADf4mxWAAAKkklEQVRYhV1XTYxdR1b+zqmq+959P91td7fdjp3EsZOZJNaAEjEjmAi2CCFAGjQLZsVi\
39328# diPEAokFYsNqtiCEGFhPpAFpAEUIxI8gIghBEqE4E0JiO7GdxG33z3vd/d677/5U1flYvG6PmaPSlUqnqr7zW/cruXh+jSQhpIiICmOM6nqwTGQAqmoZ\
39329# oiRJEwpWQmYBeDYxCqAGKEBmwAAAutJ7MSTLFBicJTiaiApSNjrniWRmFIAEBGJnO01ESAIQkVOdnc3FAQLAzEQEgDeB88HMet6VI2dSqLHtFlE0G0GC\
39330# TpSnZlMhICmiK3tXp5y6JCLQMwsAQIiV2huZjJLsucvbX3vlyxvPvlyqvvPOWzd/9MnJrA7e5aQZ5igAZOUWV7YqQNIewzgRAwU/Bn4smkGngaRaWh/o\
39331# 9vZ2v6eSUuGVzEwGmMA9uUdEACVPvyRBPqHCWWJAAcUopk4ASaIUp73B0Lte7JqubYXmnRM4YSZJMSJzVScigJ2N0zwLAZiQjzEAiJzCqxDCRGEoB2ub\
39332# F4eD9a5uLJqqmpkZnNPH2YYYxER/fJaIKOQM+9QPgcHI09pEBhWidIHJMS6HozVfhNQem2TAU+h9Z3DeOhV5HJOcsxr11FYznFaz8HTQnJytV4KkCiAE\
39333# SfFhMBg5J03T5JxVIVBAieycO2uPswpWWU1NTn2i4GwYlCRXASOphBcz5xyZvSsG4/NJbFkvVF3wCgDiBBRR587idgYGqqpmJlGQRiPIU7zHFQEQEBG/\
39334# QgPgQlGO1xcns6ZpiqLo91eNqaKSDHYa9Cd8AgAEURgBoRBnGcog1JkaqSBJ6mq9iPhQDIbnEOt62Ybgt86vJcIEOWcz2BNCgCuwbKvIqMA77TlXhlAE\
39335# 3/M+eA3qgrrgvXfOq6qqA6DBF/1h7qqmSf1B2e+NLUMKJMtBC4o4QgmqOOcIWKIKDAYhIA50KipUwKAEM5lXuQU8xRmFFFXvQ1kvj9smPvXsJbFeSkYx\
39336# gVKMJCAkxUAlAYWtGkNFvKBQUcFKQwglGySZJRCi3gwilixDJBT9ZTWLZufPbxxPmpSSGbzvp5RWoQdgZkwEoISHd6IK9FQLFUcIIeLESWeg2spbFXgn\
39337# KRtgor4sZLactYxy8cL2wwe3Vb1naDWH4MycqQR1UUh0AwhQWO6cFN5b4VhCvXphciKWQ0/YJBqdeN9Y9CbiCBFCnNAmR7u9EltbG/PZTFUz1dq4rBdG\
39338# Z5oVaAmV0FlUQc+HHpLz6Pl1r64Y2rpfK3WwkKqqsplBXRW7wqtPmYUKmZ0LTHW1aPuDwXh8rlrUIYiBX3v1xa+/+qU0ui6aNFbLRbVc5Gpx3DWT9mh6\
39339# 0rmQm3UUIetgbbh1eaev5f7ew8n+0d7kcFZXA6+VZW+r+xw5hF5THU+my9HautMiNtkHzTk+f3XzV3/5Z6/+zG90kTTnNGWaifm4m/Zu3b93a/LFybIj\
39340# jGuDzfXLLxVr5+689+6bf/XG8bH2QxG7xtG8emdMJIog86ODRTV78YWnhblqaqW1WdB13vUXi4g0hxslOLMmSZFwqXfh0uWNy888+8X0we5sEU2KcqM3\
39341# 3Dp346uvHtz68PbdT51zJFW8V6GZgBIkV7Np23U7l7abpk6pA1TMeiFsX77uR6Ucvt1V2a+/EEZb4gqhNS1l+NMcP/fMU/vN4afLWZVyk/bvpKb66s/d\
39342# +Pe33757/7Do9eu69jlHQRBxqV0cTQ/axi7vPH00mbrgVSGA9zLcerqpln/9gzfe/Z9bt+/ONst+Ebh15crvffd7IVg53Pnh9/7y337wF+vr66/90qs3\
39343# vvLMYlaPzunLN57/6M6u82Vi8kKDqIhrFkdHh5PQKy5evPjfn30izq3+jCGE0eZTszvv3/70k7sPjm5+dM8lXTT1jRvTjdJ1Ohr0yzv37r35Xzevbl37\
39344# +Nbd7/7Rbz17bbxc1Jvb52KM5j1WlIdi6tzJcbN7OOv39Pzm8OH+pO8D0Weue+UaZG1+MrHsRr2B9EIYluNhOR6Pp/f+AbwHMy/FYKypxO7e9ESu7Lzy\
39345# 81deulquh3lCQk6EFxFVOEXXtccnJ1tbO2UxnB8dlz1HpAz0hwNBrGaTxWK5vbkBQ7IULSfjP/7TW1fPf/Dar/xa0xzGLMSxcD4eXkDxU8Xlq9h4r7LY\
39346# Z5HR82ZGJrNUNfXJfH5p5+kU25PJxPtCNGeBK88Jw3TvQU649qWnCgmUlBPVhf29+Wd3duvuZPeTj2r0Z11hZeE3lAJwnLDWCqL6SPOqzkG1KGhaLXnt\
39347# y9cXTZsRisGIKTqouJEAB3uPIO7K0xcLCcmiKAEs5vXB8cnN/x09mlUs0LRdQzIEoBMU0VynoYpdZ+aVSogZDg6PF9Vs/+hvf/g3/zo/qdt4NCiL2NSu\
39348# cJB4cHBQjobjjXHbtiEYU3LKxbJqq3S0OKqPzayrgsfMtQ9v8ivPd3KhJUl6DZ0mLyJCEe/rmJuufTS5HxOchixxY9RnRq8XECcPH00uXNgO6qpqvrk9\
39349# UtXgpE3N0aJmr0ySiCKbpJ57+63/7IfFy699s8+lGQwZSB5GY3JeRJV0DuiXvdgmXxRmJlBIaBfTw4Pj69dfqKsGUIorfBFCuP7SC58+fGd+dHgcq2nK\
39350# A8tlP9z//ODNf36/OV4+uPMeIBEZTB4AkUU8YRmE0xQbBbMpnO9I7/3+g0/qqt3eOncyXxRlH+q6yF45/O3f/Z3ndr7/xt+9GRv21StjJOYp3nqwF3O6\
39351# ffc2NFmC076KiPfeCMsk1cxM1RB8Uue8CDU2+3uPmjZubG/vHU2UKtHlvs5n8fDzz7/x7d/8/T/4zvnNdabsvF8rtK4Ws1ne3T+cL0JpqhQi62OysxIv\
39352# KkZVBcCUlGjr+e7nDyjh/Lmdw4MT8c7gveuT6U//5M++/8d/eHFnsHVlPdGyCcIgEfP5o8N5dTSfaJE1uJyz/0nqbsxmlAzSNKn4anH0MKXhqPTeT6ez\
39353# 0PMiatkVXg/2Zx9+cPdHH955eHffeUZGy7Hw5d78OGubDckgKUoWPSPVBCCWBc57f8q1AKo7OHy4t3dwfms95a6qKjjrUlS48XAQG8zgPv74sDpuxSwm\
39354# QNpvffMXvv7KtVzXdd264MUVIk6fQMHqdAKGTEFMJqqH08n+3vTS5Uuz2axtojqSKcUaaNrYsF5Gyx068Y4SoDJaC9/59q9/6xu/uDEqSUU2E9OfCJqZ\
39355# pZRMICIxw3mZz6rp0ezSzpXDvalFQzZ1yWknmlJeSpKubjKRjYzLcbn2+uv/8uev//2LLz5z+cqF1HWCrI5P5EYIQhyQ4VUsw7FnqOezhk53dnbufPwR\
39356# SOdC19Q5x6IY9PvhcNH46JlU1TufBr3e/rR+6/3/uH3/7he7E69wznVd/H/eiAhWfI60lL0WzNY0yWkYrJXH08nG2jjHzEiFu/XR/b39+fG02z1Y1BZF\
39357# LYs8ms2alHx0736wd/+zaRFKy6JSyPZoDA9VTTmvnkunTNwHiwlAEVxZ9EbjUuBi200mU6gIXJdTjFGUKt45pw5Q5JxzzE57BkJqVW/ZZetkc22oqhAx\
39358# swyG1VPs9C2mZGY2VWWOJL33q3YmmWgASJNVuYpAGHNWSuqyCwXRkGLZmaX/A0GLBDxi5LN0AAAAAElFTkSuQmCC"/>  \
39359# <a href="https://freshluts.com/users/64078">InAvision - FreshLUTs</a></center>"}
39360
39361#@gui : sep = value(0)_0+
39362#@gui : note = note{"<center><img src="data:image/png;base64,\
39363# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAC/VBMVEUEAwju6+4FBAnv7O8FBgoIDxju5+oPERcICQ8HCA0GBwwLEBkRFR/u6e05SFU6\
39364# PkYzO0YSFx8MEx0HDRYOCQ1aXmcpOEcgLToJCxFHT1lFTFUtPUwsOUUhMD0YHCcTGCEKERvt5OdrcHZdYmpUX2dSWmNKVWBQVl9EVF4/T1o9S1c3RVEz\
39365# RFEzPkomNUMlM0AqMTwdIysKDhUGCxPu6evDtLh0eH1ta25XXWVOXGVNVF1JUVtMUFo/R1ExQE0vPEkqNkIyOUEfKzgjKzYqKzIiKTIgJS0SFhwdFxsQ\
39366# DhMLCg/ZxsjIu8DIuLq+sLO5rLGwpKZ7foSThIBjaHFpaG1lZWtZW2NLWmNTWV9QVFpNTFA6Qk1MRUk3PEksPEkuNkJBPUEoLjobJTMmKC4rICUgICUV\
39367# GCI2ISAgGh4QFB3m2t3d0NLVwsPOv8O7sbnOtbLIsLG3oJ2mm5yYk5bPnZKGh4l8dnm6eXJwb3JmaG5ZYmlVWWOIY2FKWV9DT1pATFiKW1Y7RVG4YUqP\
39368# UEE4NjscKTcdKDRwODAeJjBFKSoTGyQoGh4xGhkgExQUDRDTwMTLvcK4tMGvr7zKtLSvqK63qaver6rAq6q1pqepoKGXlqC5op+zmZOVkJKrlpHKl4yB\
39369# gIXfj32JeXxtdHh0dHZwcnSmfnJobXJoa3DEeWxeY2xnYWhSXGWfa2G0dV+4bV6ybl2QZFqZX1m2Z1WBWFR5U1CRVk5GRk5lQEFWP0ExNjx3QzhbOTc2\
39370# LTFOMTBfNC0XICwdHyo1JSc+Hx04GxsWFBns4OLp3N+/ucXAt77MurzQuLjRsanFqqmfnKbQrKW9qKWqoKLCpJzZp5qfl5nFopi4m5WHi5WgkpHfl4y7\
39371# kYWch4WYhYF8fX+DfX6Jfn24gnt/fnunfHpeaneRenWqenKCcnKkeXBYZ27BhW2Xbm3Kf2yTZmZyYmRhYmN2ZGJuX2C/clxKTlqqalhYTE4+Rko9QEpv\
39372# SUaBTkU2OUFNNzY8LzJaMSoaIyUoFRRPuIDSAAAD7UlEQVQ4y0WQY3RjURSF77y+l4ekQeM0TdJYbZPO1HabqW1zbBt1O7Zt27Zt2zbW3FmZtWb/uD/u\
39373# +dY+ex8QUSoMDAxiMn0Xzu3RgUajtXN0dGxjl0MbBwcH0Li3KCfIzdmk8knv097VTvwfQyCCLcnOczeq+Bk+N0627+D6D3CEAIQgEBakyx7uru2fnt4v\
39374# 9dL5rtAEInYLO1AUmufm7MzUZKgy0udf7tEj0avjYWhDo7nS7EBJkZu7i8DIzxzIV/VPndl9StduiV6dxhyL79y5oyv0AaVCFw93qWmokck06nxuzp7R\
39375# dUq3pKTELl4JCfHtj3ToCGqlppBQaQhcE8zUqvqlzp5xduqkSRMTJyR4He80ZuxYYAmWuki1ZmdBodkwWD1gyb3ePadNm5qcnNRtwuQTCfHxoFYoFIa6\
39376# ObsLgs2GLPXgLL+nix6+efs4tXuvUxfOeHWZDmpHh4fDGIKQQrPWb2WWXq/ZWBVbHtu07tnMXnOSrriAvaWjw11gzsJgU4BGbdDrc1t4vNhDtt0jF6++\
39377# 3uVBISgNFwo9BAIBc6hRO4Q5RG9k2XitzVVVLSNXj1g8btx4YHELDWFmqf1DpAKDQZmi9sttInktv3/W7Rm5Krnz0fEgIlxq0gebhvqp56YYFviZzeyd\
39378# X3c0HzxQt667d8rpidNBvcVjdKjH8AXKnhfP9Z6lFK+YP2jzmrU79jzq1DNsyEptCpBFscXeqt6zvG/3ueozCufNeb5+zbu1H570Sc4JioqSMUB9hHjA\
39379# ALHmjs+i1ywSAWDQtvUbRsx7UUXimRYZ7ikCubmSwADvpUsXZn+KAVCxW5dtGFF3AKGI6PdicaAvSBNn5ATcUmoirFYUIAio3Hpt3rKDCEoSeGAmP00J\
39380# lvfr7z04m8W2NsbABQixj7d/1bA4gJKUiDWQr/QF7C1hkrBoOYfD8fwLMHD4ogSgCJT4rOMv8Qf1siiWNQanxzBEMAHPhsRFHqJq4AqEYOmW83VAVsmo\
39381# lFVyoEg4v3u/vLqsjEVQNhQCA9MkvoAhp3PkcjlHzkABqO77cr8nhX1DCZJCyDBNWh4fMBg1jBqc7kkXwQrf+w5rwHFPGYB9EHS7vy87E9TQ6QwnOr0t\
39382# RsC/TX0bmnCRaNcPkiQpKjpAZ8kBXC7mBAG6ApZANm3+0ozvHjZ5G4qiFFUi8S+RAC6mwLgYxuU6VVTj5a0AKd85qFcriiJoDDvI31IAuG0xblsoLLL4\
39383# 4/biONGujQ35r/KrAYJGF+QFsEsAxlVgmEKByUflR0b/Yo0qjgUVKyqK8wEqiiqQFFmBAlM4/VVjZJm1rMIWBw9JbImM4wGEwtnDC/b9AdM3HYNA1prq\
39384# AAAAAElFTkSuQmCC"/>  \
39385# <a href="https://freshluts.com/users/120">J.T. Semple - FreshLUTs</a></center>"}
39386
39387#@gui : sep = value(0)_0+
39388#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAMAAACf4xmcAAACMVBMVEULF\
39389# iILFyEOGSMJFSEOGyQOGCELGCITHykLGCELFh8JFh8NFyEQHSYSHicUISoNFyINGSULGCQWICoJFiMhKzQbJS8mMToKFSIdKDEZIy0RHSchLTgZJi8XI\
39390# iwTGyQQGiMpMzsbKDQYJTAKFiQ4Qkg0PkYlLzgeKjQNFh8yPUQfLDcXIy0RHisQHCkWGyRUXmojLzkjLTYTIC0PGyceHyTr7/jK2eyhssZIVWFDTlo3Q\
39391# 047RksvOkIuOEEtNz8pNj8nMz4eKTMaJzITGSEQFyHe6PTV4O7D1+vE1+a+0OTH1uPCzt6VsNGdrL2PlKNhfJxqd4ZocX4+SVQ6RlA3QUgtOkclMTwXJ\
39392# C8aHybl6PLT4fLQ3uzM2erR1+O5zOOvxNysvtSEps2jtcqNqsiAocecp7hohaR+jaJyfolSaodLYn6DdX1kcHtnZ3RZZG9JWG1LWWVPWWM3TGMyP01GR\
39393# EgqN0QxNEAjMT8aKTdFNzEdJSkzKicXHyHk7fbG3vbN3PTW4PHM3PDn5Oq2z+rC0Oa0yd2sxN2ivdy6yNrLy9avvs6otsmTrMmmrsCUpsB7lbd5krOMn\
39394# q/VuJmFj5lsgJlYdJSDhZF7fo2ciIxvfIuMg4Z8cYCDbndeanZCWnNiYW85UW9OVWlhYmRrWWSJdmNiWmFQU15LT1xXT1oxRFo8SldQR0snN0oyPUdTT\
39395# UQ4OkRoT0EeLD5IRD0uLTcyNTVWPzIfIzA7MC4sKCsgJCokISPHXUUCAAAD5klEQVQ4y0WSBXfbMBRGnxRbppniLJx0gSZpk6w0bFfumJmZmZmZmZmZG\
39396# X7dnqLBPZKPYl19T1IM4PMB/YcBGd0gHhCZ2kTTDJtWJfv22/2LW/ylLDQZdM02dCA45ppKq/r2+3BpN6CFGiI80Bo/lgjouFBHTZZR+7qzH/g4clITo\
39397# gzu+vVvMkBsaggNi+7s15dreqawR0Nsm9KQ83B1vUcAqA18b6h1f+/uAUDNy+g+7gNCb9/ZohKqyVgZV2Jc949kD86GdJ/nAeLjXX3x+JvnUkNiPkIMo\
39398# jJmzNBs1CCkY9hfqp5+Lm1udChzfCoxDAMrJ2XUMA47j0LfrTt5Y/Xa0acm9LYzoPlIIdlTmFFlaKihI7ale+6Evf0PzZx7YM7ARjmEe9YL5pdPM6pk1\
39399# IDHAYe4U8cO7d9/7sxhC5bvcDyZkUKg77snj3r+pglCGTZw3ohZJ4YOX3lllQNJanfv+vl6yx7ZIMAlnsbJeAMPzh95es6YB+fukRDB7WuFAj9GCAjhj\
39400# sBlAxcdPTJq+PFVs1+Cx0+laTpCdNR0DiAE1KULRgwYNm/MyoubPJfoREMIERpHJSri0rGjhw2YdezC1UWbXFtV+aWhhirIMpUrMMYcZ/mooQNGLj4/f\
39401# ulElzJbNiqgDZQjNOpIN2f3HzF/4Zjx97cx8VZggEp4OLUqRZ3ma6OHn1mycPzaXY6NcQSNCkAQzeyt65X/iz0/O2DxsiXLroeT+IufUcA1zNMqd+yBv\
39402# e3WuP2H9417z+wMIEQgNB0712wA69m4USPHTpzM+Ecl5jjAs4iKI57mQeOGDVOb8oO220zHGd6EpqoMG2PYwXUdZcKgmLN1Ui3gxH8IUCoJKB9Rc/Kgd\
39403# NO6urxDKWNiNQ+CaNQ0eeuFmKaU2Di1nB60rrHp71Jhg1+gKKap4ENpHRxT2qelTL6Mi8KFSCSSSCT4g8t+q94pK1asCQsoJvcEEO6NhMPhThxEEv743\
39404# eaS1T74bVkRYGpFCwgaGgKBMHrxNZPjVmzFYMvycwktoQVzuYaGXC4XzDVwr7NuRcKyJq75k/UnjEIw2CdY6blAJy9/eVLAsgbXFYUnwvAIfThtbX2Cs\
39405# Xw43jsS2DgpGPHXNVuWhZY4BJMY1LS1ttZks63B2q54vFicvjnvN3s1x5rEBVmYho1CS3ZKtjpb01bfmS4Wi+lXZWASje7Y7jBGJSVuYWGJMWhpaclWV\
39406# 9e019em0tPT6S5L6RVV0ltLrhS1FKzMs7Bodkp19ZAhQ9qnxWK1qVRXKh+bXnKcUpcLUI5iZUlCD/c2pQa9jo5pHR31tSjmU/l4vAwcH7gsKjHqMvob3\
39407# PCdJnRHhgoAAAAASUVORK5CYII="/>  \
39408# <a href="https://sellfy.com/p/SGnG/">Kyler Holland 10 Free CLUTs</a></center>"}
39409
39410#@gui : sep = value(0)_0+
39411#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIMAAAAWCAMAAADU6N+kAAAANlBMVEUA\
39412# AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAABH0wLDAAAAEHRSTlMAv0Dfn4CP78+vIDBgcFAQ4LAwBQAAA\
39413# opJREFUSMeVlVmy4yAMRcFMNuA47H+z/TQgMSSp6vsRhAT4IAQxoBMUill1gdCd/Z+sTagI4iG5ettQztrbmOpAV1+h4oQX2uUF6pFy3BEUqHs00r0yoD\
39414# dZe7ZduE5cImdlwzIh9RIwPQ5tMEuww5wMI2Nr7/ebJz4ehLg3+BvrLSJvzdGtARQaiRgsdml7nlcLl6xIMUy0oy4xVCIvnQ1iu1qLSQFE3cMML/mQA4T\
39415# OOc9DhtJGhkDkHnfx/qrmZgBJ0cjgpO8uwcN2ZYgTg2Xcxzzt/xl6mzilOprsnYHOwvWoJXaO9CU4qke+M0h6+0rEcCrDNHhZr+ih8b3ovdLZ8FfyuDNQ\
39416# cS+1Wem6qWcMTugJhnphyGNxlFsPUKzrOIRBvfZPMwNeq2v0zQxq00siS3mj8K1V+cYJvr522MlQJ3bGhOYFQUplcDNDkaUqMejCPM572Ttcm8+VNZ69f\
39417# /CC7Qz7TGII+oGFQXaVWrd+MWg9AoKW+n59NwYrRxFAyiC5K2PBXP2AvjPU/Cc+1P3jc+HMDKplZpAS8UYZoFXhxRAL5KJ+c2p7w0qVGVTKoJmV1OSRQR\
39418# PF0t5cTiLuqpeQV4b9CXEvKJE+Whnu8fk/ve764sCSF3sLw6RfDJ32MMLgJgbbD+iG9ET5o2BLGW6zFPtvhv0dC/pkEIMXhu5EyZejWJK9kaF9ZIirUw+\
39419# Sn60yMlTsKEOcGVJWhkZQwtAWBmXMnbjLcou1gEqtCcNDNjA0bJmB3UGsi1vDyrymnas1HTydwzGAsk/UrX164Dh2Lurkg1rGrGDj81QaqdrWM8mYHpSL\
39420# ayobMwVfAVSz+arDgwrt5wDlbgjnCaqAY0EvUwLo+bAaK/fYPzbBi89MLkUDAAAAAElFTkSuQmCC"/>  <a href="https://lutify.me\
39421# /free-luts/">Lutify.Me Free LUTs</a></center>"}
39422
39423#@gui : sep = value(0)_0+
39424#@gui : note = note{"<center><a href="https://michaelezra.com">Michael Ezra</a>\
39425# </center>"}
39426
39427#@gui : sep = value(0)_0+
39428#@gui : note = note{"<center>Moviz LUTs</center>"}
39429
39430#@gui : sep = value(0)_0+
39431#@gui : note = note{"<center><a href="https://freshluts.com/users/992">Ohad Peretz - FreshLUTs</a></center>"}
39432
39433#@gui : sep = value(0)_0+
39434#@gui : #@gui : note = note{"<center><img src="data:image/png;base64,\
39435# iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAC/VBMVEX9AEL7AUL9AUP8AED9AUH6AkL4AkP6A0L4A0H7AUT6AkD7AUD2BEP6AkT+AUX0B\
39436# UL+AUD4+Pj0BkT9AUTxBkL3A0D9+fv2BUb5Az79+/r/AEL6+fr6/Pf81uPlD0b4BEXuCUT6/f32+/j8+Pjv8O/bGEnhE0frC0T2BUL++/38/fn98/f96v\
39437# HML1XXH03oDUX+8/z99fr38fby8vH83+nk4uT50d/4us/uVn7tPWrrM2DqGVHSI1DoEUjzCEXxB0X5+f397vj88vTy7PP58vH65+z62ur84+jn6Of5x9j\
39438# a19bTztD0sce0r7HncozkPGywVGvrNmTkLmDQNFnCN1fZKVbRJ1TsEkzyDEjpDUfyBz77Aj7sCz39/f3/8vn29/X79vT39vL19PL77u/96O7t6+vs3975\
39439# ztzOz8/TyMj2sMPzqcLzpb31oLrDtbnNsrm7u7f1l7P1ka7xkqvvgqOtnZ+0m53ndJPtbY30ZIazeoWodYPmWHylb3inZ3euZnboSXTuRnG6Smm3RWDaM\
39440# mDrLlvFNVrsJ1rlIlfLKlTqH1TvGk/bIk7pGUv5A0niDUP/AUPnC0HuCEH05ens5eXb3tz6y9vtz9fk1Nbt0dT3rsvQwsbIxcXqrcLGvL3Pt7r0m7byob\
39441# XFra6xqqzOnqqtpKPtjKKolaDJj53udpvahZmkkpbvbpauj5PwaY/lZI2egoi+dIToVIS8boLFYHvjXHu6YXboRXS1WnHjRnC9T2+vXW7KUW7XRWzoQWy\
39442# +QmCyTF/mK169PVvtIVPdIFLjHk/jE03tDErZHUn0BTz2+fr+5/Xz6uru6Oju2Nvv19vh2dn2xNPW09L4xdLY0ND3tczjv8jhxcfWwMbhtL71rL69t72+\
39443# vLrSt7rfq7nqp7fMq7DVoK7jmq68oaXelKTgi52nm5rMkprNhpa/hJTngJTmYIvgboquhYmmf4jqY4KxdIG8aYGcdn3YZX3QaXvZX3vnTHfrT3WqWmvjN\
39444# WjOOmPJOF/GM1XYIFXtDUL6xIL1AAAFQklEQVRIx6XWA3QcQRgA4J2ZNc527KRtnDRJbdu2bdu2bdu2bdu2XueS9vU2TfX6v3f7bubm239v5p+5IwAgCM\
39445# JC/HWkB/8ewB2e7QzGyEawLA0YiPugQjsMut/8HgBGoJQRfln79y8T7p+ipH8eb7H8ACSJoOjoU6zz4pszpl5uOuv+qtIBGi3uJjIMEo/f/7nHssIFfNU\
39446# +matUCTFXjRw3a3MSY0K/+r7KoTueXMpXc0zzJW+Kvi8aN79BVDWfkTd6aTIGAEp+71rXyVGoe9lkmoNaQIvD+y6yqw/U7SaiDAji9fs6N8s55u0IAIAg\
39447# CAQSBEQYd88JNYd20ul/FiDQr0uzyEIDRB4CAiHkfpEWzhnwNG9Q3rUigjDdzKLhxQrnnDDARA2RBATAtyLgJE2l1bWCYvoByMkAx4ml7kTm7xfIA47j9\
39448# w/33zcoPNzPX6dQQP2XIpkM01IAR3pOr4Lzf3Gi5hanRAHgLB+2rkObJk2mzLj72qiAApM02RoSzylkgHCWmlm9eQVuCACUf7GFBX19zEGV1d55XRxEgm\
39449# bzqGzTjQB5AMA6uozO3ZungIWM6FLY1yfqdONGhexZco8QAL6F45ZPre16eUmWaV21kYsXAMmGtfY1Ty9Rjg7UJXZrrOPdQLnpULV2ckBvnZQ5jnIXq25\
39450# jAYM9iSEtSE+LLr0bACbrGduFSjIQsWZ0VD8thFDheuYbNFkHaKgSAEWlDaIc87zzDJYtg//jGrEpTggJ6OqgrhxTlhPx2n+vYsDqntcI7S0D4W3U02ga\
39451# A0Jck8uQ+UFSMsC91HfAbDicubvskbI2CWqrJyGEDDtovNWr2tVeAXqa/ZYC8HSpY+Y44FFPVNikoKWpgGCVq3N7mW11FvTaywhEGgBU2Clze8YTfKpnW\
39452# GFSuQHD6jpFmYPVVY8u2CWyAKUSqXR9qxyULGhYwavSCo5M3t0yr9rLZhvbqZKe4oEbhNU3tAfpwMOK5LcHYHhlQota3gav3EWUPOXukfrUt8alA9a2pj\
39453# RAQECamJTijUK8vA4WZxgIgUTvqGftTiDPWZpobilJGAAAIIRYcXuLhHgZmgdCAovAHgVCE2Tr4HfN1kBJYyBgkBpGTfkr3sHHyxEQA93GfHkGEp7hmGe\
39454# zl00DNEwlTo1upW9w/nKp2tHRN9YlyzBiVWT2eMm9mY2lXSLExyRkhnasHdzASCuGQSZrG59WogxQH+rYWikx4CNW3i65J9kppfj1nK32aZcKlFunVFkr\
39455# yQC967yt7mCEABvRocaR68vXbejacWY+dd1EmsY1PLRrdP6yWhlgNa+yqJfQIq0sv7x6cHD12qPyRVbOFtubggQUNaVmZ1lkFJHnnqaYQWO97QMlmtKUb\
39456# pE/KkdISGieCUsTWdJCQMa/cwF7Isd5AkAxmke5srUsx0Atp4zYWTw+PmFnQAUTj/BnAdsK52pn5LSyUwMAsKdhtixtKwgizgJ4nmXdVwAgUann3JwNk6\
39457# BWm/5spUvEeNcsUt6owIcXoVKpIL7i/RTQc2F0zHYJKhTpAGnSrR9pzTEncQjHQQInTy0QjV+xudF5EjSSFjfThYDoLTFq6/iX4Q4W4AxIUDrCty27mPN\
39458# kAoM1Bj8FYvo2zuGd6+y9TR/DypTp36dH18XNokNbDFCSMGOA7xoQF5vZoK497tzUpk0n1ovOkr1hfIWKLPELQJJICHQVbxVrz54JR3Z7ofl9dYGy9crg\
39459# d4hkxeTBJYp2W1+0xEAnRapY8If/ATxLEUJFk8lUUYUsBCmwiIC/JQhhRgAc35sY/FP8L/gKJ0Uj1jCuNAUAAAAASUVORK5CYII=\"/>  \
39460# <a href="https://gumroad.com/sarikasat">Olivio Sarikas - Free LUT Packs</a></center>"}
39461
39462#@gui : sep = value(0)_0+
39463#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAClFBMVEUA\
39464# AABzk3pTeVfj8PHk7u3Q39rp9PW1x726zcSUrJt4l3/Y7fLq5Ojt9PTv9fPn9Pbp8fHk7+/h7u66zMO5y8JtjnRxkXdkhmlnim7R6/Hc7/Ln9fbn8PDq8\
39465# O/n9Pbt9PPR4Nvn7ezg6+zO3di3y8K2yL64zMOZs6KYsaF1lXyu3vDq2d/o5url5uru9PPo9vfm8/Xn8PC2yL+3y8G3ysCkuqtmim1oi2/0PVwAsO7m8f\
39466# MBru33O1kGqe3r8/QKrO32PFrp8vT5PFjzO1r3N1YIp+zyOln0NFbnydHn8fLB4/ABrO4Npez2PVv1OFdNTU3j9PQRoesFq+0LpuwQouvpvMaCgoLzZX1\
39467# SUlK95PG03vB4ze9Rve4ZsO4HpOwVnermx9C9wMHyc4jwbINra2vV6/Kl2PGs3e9aw+4gsu4Ire4vse0Mre1AsewRqewVn+samuoZl+nn3uPW2drstMDo\
39468# tb+nq6ztjZ6HiIjucIbySmX0P11YWFj6OFT3MlLs9Pbm8/Xn8fTu8vPh8fPB5fG34/Gl2/GQ1fGb1PGf2vBox+5vxO5Uwu7k6+0TsO3n6exRsewDqezh5\
39469# +kSmenc4+TqxM7rt8Lsp7XwkqOen6DshpmRkZKAgYHvXnbwWHFnZ2fzRmP0QmBVVVX3NlX5NVL5L07j8vPe8PPM6fJsyfGDyPCs3u+Dzu9gw+9Hvu49uu\
39470# 5duu1Que1KrO1Ku+xJtewmrey/1+ssoOodnuoTkOjj4eXM1eTS1OLa39/k2N7Y29zbztnl0NfjzNPsusS3ubrrrbm1uLjnrLixsrLmpLGur6+urq/snqz\
39471# olaTvip2WmJjxfZHsfZCLjY3raIB9fn7uYXjxUGryTWj1O1r4PVn6PFdLSkoQ11vXAAAAOHRSTlMAGwXd07u0iIRHJv796eTi4tjXiG8eGQ8M/vvp3dnU\
39472# 1L+6urmBdGxCQRj+/vv76+rn2oWCgmwODToLEi0AAAKySURBVDjLZdNlW9tQFAfwFCiluDOc4XPf2hQyUqF0pe3oVvcWdxhuw204A4YPm7u7u7t+mSW5G\
39473# S0Pv1d5zvkn5ya5F7JaExftGO7uGe4YHbcHWo3itC0oS5N9tbVbczYoxIkCrWQX69aa3SdkMns6WExhX4/KLdbOtr8uyiVbyMTkq3RMnFAbSLV5iLODOo\
39474# eoszTdTJJuvYPz8v0OWfmgmtOWy/wvt8uBQs6PUpN9YdcFplVuFhWsY1dLDgu42C5k2dAF+uJ9/63lp0F9sC1v7wpaN3yI05OlRzop1peeR2EgYx+gyvD\
39475# Bvp99db2iY1AqlRZmCMjAqcJEQBuyG4rf9PvP4tO8xETeORQmoRoeCBS60KGdsiU2u0adystrESAIIkBRBEFPXucR9JnbIcdj9Wz24jutvh01yafkpvGK\
39476# yW/P0E79AZz+yFoozMzG1N69hgxJ0ifTlXXKV+l/7yMFqYTLoZCHAg/Uf26Ej0oqDr9Rzj9E5JJDsCrtIK7AHQsk4LiPBXjgrXJ+thkLCNBLaTgsEGZOI\
39477# HxvHJLMfsFG1DVPYU+YvnfLUFRUNBCKLZJLBBrKUVNVVZVp/CP6Qt77ssF8ZthgMGCL3CFrSCL87MVfE8bfFOmvSeIabxYXD2fSoPiNIhBYeN8Ek5oquU\
39478# kJM5klxSUudOxTm7mAuBQmPRCLRKKF51dKBjwDsJ/lakwB7uwnnICrf2EBRs2N22oa/ruDLckkDuGHjM/AiSs7N/tBGN/SOYYNfvkMeXW8PwZsOapMbBN\
39479# QvBaTybIIcuv7e5dZE+KUWrI/5kVZ3vbesjkOKHMmJsBVbZm3s825o5Za+GCuMZm4XbEhgrLi6PluGalm8Dn8sa8MDp9hGQmOAfOt/HzsXUcrPxmnLR9G\
39480# Xe19/KDVAui0SC9PD69IGj3AWv0HukgIS3/cZL8AAAAASUVORK5CYII="/>  <a href="https://www.on1.com/free/lut-packs/">\
39481# ON1 Free Photography LUTs</a></center>"}
39482
39483#@gui : sep = value(0)_0+
39484#@gui : note = note{"<center><a href="https://marcrphoto.wordpress.com/specials/698-2/">PictureFX \
39485# - A Free HaldCLUT Set</a></center>"}
39486
39487#@gui : sep = value(0)_0+
39488#@gui : note = note{"<center><img src="data:image/png;base64,\
39489# iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAC+lBMVEUAAAAAAAAAAADw8O8AAABiYmLv7+84ODgAAAAaGhoJCQjs7OwmDwjQ0NDu7u7k5\
39490# OSPj4/s7Oy2trYAAAAAAACpqanv7+/Z2dnHx8fl5eWRkZFsbGzCwsLg4ODi4uLPz8/s7Ozn5+dfX1/v7++oqKgyGA0uIBseCwTb29vS0tIdDwzy8vLt7e\
39491# zHxsb+rB26urrr6urz2sGTc63vUQPn5+fk5ORYkczWbo4kJCT9pBrY3N1YsJjVKAfkMQToRQCwsLCZe7CPaqZZMGX8pir3ggvd4d/y5tjT09Pz0q2am5v\
39492# SZIJKHEakFBn7lBXkKA6xFQ7MHwL1awHzZQDyWwDbOgDy7eVZm9OTk5ZasYp/gYJZOXVsbnEZNmr3sGGzNVKzK0P6mzdNFjeEGjNeFC0dHiH9pQ/6jQje\
39493# SAPgYADX19fOzs5hjcXYn7yoqKh2VJIeQ4v0xIlbs3/GTWq7Q2LyZlH5q05hIkovMkKtHzDt1gLt2+Feo9eBpsSqqqrRfZ7QdJmJiotnRYRDVX72uHJJJ\
39494# FpdK1cVOVVKRk83Hk9yKE36qEBxFS6eGyz8nB32dw3XHwGek7eFhLSQeLFCa6/zzqN7XZvzx5ZKopXGVnVMLGkRXl5rMl6UNE1aGz66KC6UVSfY0wdmJA\
39495# TCFwQ+EgStbADqWQDv19niyNdqncmvyb1Wf7jj3bOQkrKGda9YqqmZu6Xt5aBBbZlusYbzhoD0vX7w33pfRXlqOG9HRW5bYWSLNl6eJUByHjyQGC7rTir\
39496# +sSX2nwHNwgDJYgDBUABvks3E28y0wcyNrsi1rMKyoreOdK7Kw6tyqaGXX5OGd3Pma3OXd2xTWWctRWIpb2AoIl73mVDFrzgmLTNbjRJ3OBHQpgC/hQDa\
39497# hABgoNGlvtDgts7yzMzuwb6cg7qipqmkpKS4gJ+JtJs/gpuDdpdWcI44kIYnZ36rjn0eTnt9V3p3RHovjHEnhG9sdWoigmXweFRcglMtekwkF0qkc0Lh1\
39498# z3VRDurkQCLhADNWgC0rmDQAAAAK3RSTlMABRL+FyPzRw0IIuj+Ou7HXtmKNi0Y5aqgl3lULNTPwKxuN82+rJCPjmZXgENtfQAABTVJREFUWMPtl2dUUm\
39499# EYx4NLBmju9t7jBUFFAyQxxdSMUjRNwTRzlZlarkzLnC0tLRtmVrb33mV777333nvXOT0X7XTuvcTl5Nf+XzzH1//v/p5HDrzU+Z9/TP3mteybtmLUps+\
39500# xkFqya9FnNBEOMf8boEGb1u0aGmFId4xMLVq3aV4fSQVcju56EwzRBxMNac9i6NxNQ2RIMCm/KVtn3wgZFNEQuU5AA8rzsX49bT1te1IFHjg017VbXfWE\
39501# nIshtoTfgoBAjsw4VAHS/qAO/ZDksKgwAoEn5cd5IFSfItCF0vecNj0nOaxQCQSiQCAPoc4M8qurEdFf298YMiFMqYyKCvP8I+DOj5PDTzPyGtlmRAFbz\
39502# +kJG0NwgcLIyD1uCWs3rcWgjgtkucMfGFMAFgRAT8/pG3NCJrgVSqCuUETtmTdy5KZ+mFbAC+FhkgBMYwLANiEnJHmCm0RiExlV5KvyvXENCGt5mMhDEO\
39503# iB8NTVC8Cm4XU3iY1NpMJXpfJVxdzYO2/kJlwgMwtVAxgkgClhghyo20Cg7+fnp1LF7AOFFQgX4P8FYESYILm6L4ny9bO3t/cDwPW9XzFcIIhnCGDabht\
39504# tX1nkZ+/s7LxoEQ5YgYQeAi8BogWk31mcsBsIEolSoYJ+dLTzopj9Cy8hngM/LkhIC0jfkLThokQiKVQqYQHOzv7+/tE4oFpAjmgBi08nJSUVKRSKoiJf\
39505# GKA7DogGAQwEMoNE9IB+G5KSqqrstYG+TCbzj/658MdxrUAcogegux+q+vTpXhOZj4+Pf8z3K1d4GL4BBwMAwqGnv8n6aDMZ4uMji/7y8dMaJBwsCPRC9\
39506# ID0oWO6Da+o9JFBoF1ZWVlRvjk7WwQCme9OGACAfq/hoX1r0rt370nls4O3rsRAwOv8aqdldID00d0AMKAiDapQ3hUeHj5+tutWKQjIt52f4dR/GQ3g9h\
39507# gtILQiLS1t0qTwiYMm7hyf5/oE44FAftkbJ6f+7voB+AQzRwwIDYX+romDIDiAjwuczS97DwqPeIYBduyCxw+EzElJeYULBObnl70FwHqpXsCS0TABAHZ\
39508# Afc4cb28t4SgmAgFNftkzJ6fExIN6AcKhWsCO8J0pKSl5ed548oS4gEajeQ4Cievd9QLwfwOMUD4edg/JzfUeN64ga7BAvlWjefl4df/ExDWYfgAM0WtE\
39509# +fbZW7ZsCXZ1nZWbCwAP9WW1Jj5+3WpcwAHRAIAwc/v212PHbg4OnjUrd9zn1Cy+OCI1Pn5q7Ik1yw6KEBlAfVNd/PDkyWHDcAIgLlwo4G8TR0D/QEb19\
39510# PQAJL3n0qN0GKS0dGx2dtB9ccS5qVNjD0iRoQA0pcSuR3VKl2dnFogjlsfGxmYgwwEOq0rsID3s7Ipj1UHiiHWOjo5LD+kHWCCCwqpjOMHFpfipV6r4si\
39511# MOQISwyAAz4nkxEKDvslKtrhYIOEw4N2aSP1wbEQHC4pIS6B8NzEoVpzoGBATclBLOW1IAloiUI1NWTjniELQNNgj9URnE08ZsEoDTFkOU8JBcnSo+Nwq\
39512# ylHRMuaoyWI0QNfyrC0zEhw5nZNwiHbSk3jTZbU0pfeHcufNNCoRUMGbOBABZwZwyxHEcoOZRAZYsjo5LPLcp+ar64ur8BSYC6vMtuTqvumxus1ZEiTPQ\
39513# P0URaNGMy2boAjA4LOtmjc2MsT8A6BPLpi0am1uzCH0Cgs3iWltZ1fudDiYd6xFiZWXNZTEJ81Ms2EwmqybcTl25LEKYTDabwaD/TlMTDhdkCaEpUnUYd\
39514# f7HgPwCbcV8KluLH2kAAAAASUVORK5CYII="/>  \
39515# <a href="https://www.pixelmator.com/pro/free-luts/">Pixelmator Free LUTs</a></center>"}
39516
39517#@gui : sep = value(0)_2+
39518#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA5FBMVEX///\
39519# 8AAABJSUnMzMzKysr39/fW1tb9/f3u7u7w8PD6+vrn5+d9fX2VlZXy8vKSkpKHh4e3t7eMjIz8/Pz7+/t2dnZra2tQUFD5+fn09PTq6urj4+PU1NSpqam\
39520# amprt7e3c3NzY2NjR0dHDw8O/v7+vr6+ioqKDg4NFRUXOzs7GxsY1NTUqKioGBga5ubmmpqaenp56enphYWFcXFxNTU09PT3g4ODe3t61tbWxsbGrq6uP\
39521# j49vb29lZWVXV1dCQkIKCgrAwMC8vLx+fn5zc3M5OTloaGgyMjIjIyMZGRkTExMeHh4Ueor/AAADBElEQVRYw+2X53LaQBSF74IaoghVQIjei023Daa4x\
39522# 3be/31yV7vYBKyMCPlhe3JmgHMumk+rvbArATlRQM4iJ+gMARE4QZHvCxDFowFlwZcHkLtN9wl5NGo5YJIEpqyfVJ6kPUCS+LLF+Svh6tUYIMFzzE8pnh\
39523# IfAzJjsqNr6zgA19mylZhxghwaUNV1jY2hlsIoX7b8VEcv6ANqNTax2QIN97pw2IUoP4xLp/FHyvfnaMfsG9lA76APAJjwJv+8V8wv0Bpv3EgMAgHwruw\
39524# U84R5WeOwCh1WHMIA+EWUmDdxTl4rUJrhJEsQEuDSQpUH8Z6QjbvEShfCAkxaON+m2IqQn1hoQGgATHcBoET8NsERgKffARmCujwGQHYBWYdQTZXwgBwt\
39525# dHgwDQwP+FrlQgMkWnB5KKC3rSG+t6ywgCLmNvd19JsYWDZ+JuVwAPcGc4F5j6DKtJm0M/NQAHX0/kPM99DeMdtHWwsGdLZJNGhcsAau0Wp84F30L14gg\
39526# BiSiD5V95u+ov528UT9kK8HixmGXqERBEBNRmtulOAl7SYAsKulAscDRmSrx7oFfwEA5VZzHGc0qKrAVYkysYLKUycIEFr/Af8eoBuGQwGGYdxBKAVvrs\
39527# UvClDib1I+w13atwOIirxXieUOABpZtycSOESDq+eN67afhVq/TXcCudHPZPJoPJrj7Z5Xstu9orkHUIkIlZkMBL2QMOV0FyAdBZQ0ToGQiQPP0SZcN0B\
39528# NFz8ApIjpA6Co1zV5CxBGMlgVcQdwj/BS/gDQrbWawABiZpqDLSC3fqgrADuAzkuyasEB4K5WBg7ITsb8jD5O0Ppzaq45AFx9lYl/cAmwBQwbV0MOUEuK\
39529# ApD31/RGne5sBTWugjxI/gFQtS050WGAfGLQknF2YqYoe7YJcsuLRQQAXdsDNNhG3iRziD7aimJvKl7ETiYfElkn0RwXoIyDGDrJJR5W6RWTs8s9wIXkV\
39530# 8rSBbiSJIqSpCgSCnO2KlwA5CQLx1P1J78kdGJf67/w6QEnP3yfqF/1kj/apHwTgAAAAABJRU5ErkJggg=="/>  \
39531# <a href="https://discuss.pixls.us/t/help-to-create-a-set-of-pixls-us-color-luts">PIXLS.US Contributors</a></center>"}
39532
39533#@gui : sep = value(0)_0+
39534#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAZlBMVEX18v\
39535# ny7PauecS0g8jt5PPr4fHz7vju5/Tm2e7IptjXv+Pw6fXo2++wfMXMrdrj1OusdsPq3/C3icu6jc2yf8fdyOfUueDEn9Xl1u3ezOjaxeXStt/BmdK8ks/\
39536# h0OrPst2mbL6lar0clnGlAAABY0lEQVQ4y5WT2ZqDIAyFhbIp++JWt5n3f8kBtVorc1Eu+ID8JCcJFF8NwimlnPxn5oJZO9d1xzDImIGwXRmgmzyUbW9v\
39537# bijT/tHQbb3AVvOrHQkl6zd3cUcv9zs/XuP2pnojgHbLZ0xmnucVMam7am30kZ/yubQHh/ZVZ9DKNUr1qVxPpZp04IZdQVmubkzZDLAgFRybZr2iH5tOH\
39538# EScjOKRhUUYbTx74OQKil1vLFvd4sWDCBhe+W4DCjeTxM0hzvOvVBBF4Me0U7UDUw/WIoS1ltFvAigpyhfgNE3qLFwj7UAcByDxCmDJ8wCXOFmAHes88G\
39539# wFSCJFPeaBqWdbJwBEOQBBirde46HNAeUgyN5MHvQdEBLj13NgDKJPAISekeNBiWdAV8B6xVIKJwH1BZDlZj8JLVt4An45/B86cGMiAABnrioYJrdvh1h\
39540# HgJNSutIWJPszYyOZYMyi3ZyFCCm+HH88KhF9A33HkAAAAABJRU5ErkJggg=="/>  \
39541# <a href="https://purple11.com/free-luts/">Purple11 - Free LUTs</a></center>"}
39542
39543#@gui : sep = value(0)_0+
39544#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAgCAMAAAAynjhNAAAA1VBMVEX+\
39545# /v4ODg4DAwP7+/sQEBD5+flRUVFubm7z8/Pw8PCioqJSUlKAgIC3t7dqamo6Ojrk5OTn5+elpaU/Pz8iIiIYGBjh4eHa2trX19eysrKtra1eXl5YWFg9P\
39546# T0tLS0kJCT29vb09PSJiYlwcHBgYGBVVVVMTExCQkIyMjInJycUFBTt7e3U1NTR0dHOzs6+vr66urqRkZFzc3NJSUkgICAICAjJycnFxcWvr6+cnJx3d3\
39547# dlZWXp6em/v7+1tbWWlpZ7e3s3Nzfc3Nynp6eEhIR8fHxaWlqIUZ+rAAABlklEQVQoz4WT13KrMBRFZUsCgwFhegdjinFvcb1p997k/z8pJJa7ZrIeNGd\
39548# maaTzsDdoXvFk6PFT8xrQbJxZBZ7lHVaNCxeNVrlmQ4BVzY/Rg0YkHWPwDXbmZHCrzXdp0gInWqOUmBeN+spsCMEFOHxe9AdUV4HXweAW+Gd7qFCtUbGz\
39549# IGCg7nKzCcJx92ch4UyL3u5O2g3AHefNnueVXq9XH+mmS1fkEdVQI1wYZYoSxsWSc5N7LXLeG2cL68K37AU3ftBtWWqrdi+YQbjVXczSYync1v/OdI+ll\
39550# 3PyfwihkJZr1uNGFCmS9JZxosDUhFSryiDiEDBXs5ws/vRLF7N1Bz8v9xPl4DD0S60B3hXiNNhbD3r5qnAdAKyPQlv0xdad9kr93a81dIIyjPIJvNFAnc\
39551# ryOqkHPJrJ8lQ9ad1JjhHDGB4fwnRMRnkDoFxTAYvOP9+sw4SizGWE6YWPBqco8lMBXkthQ6NIg0xSJznb7t95aN7WYKDPR7QG9mtpPpYoDmiJsuquRBT\
39552# Dd1VXMa4r+EuBvwA2hClvJ4GaGgAAAABJRU5ErkJggg=="/>  <a href="https://www.rocketstock.com/free-after-effects-t\
39553# emplates/35-free-luts-for-color-grading-videos/">RocketStock 35 Free LUTs for Color Grading</a></center>"}
39554
39555#@gui : sep = value(0)_0+
39556#@gui : note = note{"<center><img src="data:image/png;base64,\
39557# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAC91BMVEVLNHxKKWxjXKBTO4Pr6nZoYqXWemJON4BNLnVtbqzRa19aUZeILkrJIyaQ69Nd\
39558# V5vr6YyUaoPs63qIY3pKL3nag2QzHFnXc1jlnk9zQk1TFUdoMT1XxK9saKjv36BZS5Xi6JDq64TEb3bikW/hhmvSdWLe41pqOFjgaVI5GkmOEzHJQiu8\
39559# Fh5u5NOD58dq18Viw8J357/l2bxt3LaqmK1dU5/o657p6pNZSJFWQY7X2oxUQ4lFNYatdX+khXybbni1b3Xm2HB4V25TKm6SXW24ZmmiYWeGVGadUGBe\
39560# JGBIHFqGSlnJZVhVFVLQalFtIE/PXUlVKEi/QEbjxkWtMETi40GdOD+SJTx4JzuoHjjcMDdIFDK5GyufECS9IB2A69zHwsiT8L7s5rRy5a3Y7qPGsqKC\
39561# mpuBeZVRdJLHzYnTm4nIh4aJcIJVXHvh5nrlmHrmv3Xl623gdGO/cmPFXE1MLE3TgErhgElGQEeEO0ciGEdJG0PlcUKHHEDJVz65JD3fPDhlFzDULSnC\
39562# LSWDzOGm0dt81dBe1Lea8bCH7bDSvq6Nf6qF36fPyqXNjqJqhqE6QqB2d5muiJjJ5pWjmJFShJB90Y+xu4lmV4nW4oRSNnjJqHLNdXGqdmiHNWXd42Ti\
39563# 1WNlOWLGX2GyVFy2ZlvJV1pSOlrXW1nmkFU2LlLoslCORk6tRE15NEvlp0rlXEo7Y0eWNUVdPEBkHTzgTznSNTnMJTfTRC2wHyPC79mUu8Sqs8Oqn790\
39564# q7ZdqLWNl7WzrbOMnq+DtKtydaedw6WprZzij5dfzZPDvo6ZeI5qZ4XQwH7FnHt6WXk0N3dsPnTlzWy0mGxnLWztiWooF2pEZ2nkxWc4JWW4iWPewl/M\
39565# R1/YnF1DQViYKlaOY1Xfr0ujgj8bMjGJJS53DieS2Liai6k4W6bc3Z9gwZmKgJDxrI7zqIyl1IttQYVajoCMhnyWYHpYU2rgrmeOkmWpLGDOk17YiVrF\
39566# LVXm2lHdp0W4cjZMJjAgVhoxthLEAAAEA0lEQVQ4yy2RZVTaURjGr4LIGDAQAREFJEbZ3d3dCfbsmt3Oud7sdnZ317q7u7u7ex/2F/c7771fnt957jn3\
39567# BZl2+HPbjtXRaJHa4Rs37t6zt3JjdeyRIyuWUFZWlgG9lnhrWry29sHT/UN3kw/FxFXur4zdrrycy8hIgxAzPF6fFt6kWFoUUBQUdO5AfVxybJO0zDLS\
39568# 0qC37WON9p7+kCdisUikKhajD1Qn42FQsswq4GpnqxvOfSIOxWCgwSwKxrY/I7RI6+mtkrAStDJt9zpgFueD160LDg6eDxII7m3XldLTWymBbgimbU/p\
39569# Wkz4e1H9vb29R734BR5FM45dRnp0QzqdbmhoBLL4+Wav4p5XXZWXancyJTm7uV0u8rZuoRuaGJmYGMFggMAdwl+LS25y43eVdOW7KSoqerT3EWyMTGAm\
39570# MBhMVhZQKf1Jk5O5fHDVcXbqPD8EEtDdFk6ykhQ6gOJAqJn+O3m+aiVqIvRdlfOFyx4BrWZOSFlTU1MAZAEIzM09VffoUf4N+ze8OdGFfBKpXZCR6GQD\
39571# lS9hCubzqC7hFS7oktTX6bO3S77e+1EykpDgZiMP/iPq8X9skeA50N1qNzBVMBMYNBdw/2yCkw1SHtkiEfz9A0tdEpm2tvYuFK8Qr4JS3wDzm5e4zlJI\
39572# JFIeQEOhUKf/mFs4BFo6otHomZCxQPRohu7pIZKUPFRigwS5dz992LaG4MI8XsG87RuE9uAKVBdqNuyqdpaSAMrLjQ1o22KTLC3tRxwfB4wVcFUxC3Ua\
39573# YbsdSDckghwcLodbs8bawt6uezQvj5slUF2/LkZTbVP6cWs+ieQMOlavhavspJknmVky7RwpWb4iSNinqaHmeuUKOxGfBVZLBKu0+4QkB4K1l2+wKma9\
39574# t6bmZrWMwpycVPNDYHUHHK5y5uKtO70UKnVqbn1oaFmZvabGZjXzwpzBtvr3ACqQw5252GmVVihcmBAulgmFwhglSDhcODj47Vr9kgDHnUyz6ryV7c7j\
39575# CYuLPXmZm5R0dLQ2ZSsoKNz5AjpSUlJwJ3dadV7/ziYXu5PJnrw2rJKWuk7YpeFhhXEfkMKIxxlrZ6Rd/5xd7IlIZZPJvGMSQf3o+PD4Lx/AaGQYG0f2\
39576# sFgshKc7KxVBZBOfYpWwWupa6tATD3xAI8OgERfZgyIi3N3JLCKRxT6rs+sldmtEVNjNnOwHfqDWIN5YRd9sIB2BQBARHOg6vCO6IWLrlqiwmNnfPn6A\
39577# wYhXgf7aN53DRnA4KA654ugO/bc7tmyN0NhX9hASDJqb5eBykSN9KFdXFArFSTwY3aBfe2JLFFZpQ+nDn34gulkFWpdBeV6fa2YmCsXUfVHbEK1/IiIK\
39578# u3+DItXK7x94612QcMazZgAAAABJRU5ErkJggg=="/>  \
39579# <a href="https://www.facebook.com/shamoon">Shamoon Abbasi - FreshLUTs</a></center>"}
39580
39581#@gui : sep = value(0)_0+
39582#@gui : note = note{"<center><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE4AAAAgCAQAAAAWXYa8AAAG3UlEQVRY\
39583# he3WaYyV1RkH8N/cWa7AzCiCIiMuSBW3AtagYLQWitpQa7VVazRgMLbEiEuitqRRa6WgRUxFTRfjiMaoVGuNQm1IaajLB7WKbcS2Am5RtDDDNsyd5c7c+\
39584# /TDe+879w5D0v2T//PhPec5zznvc/7Pck6NMr6f9rarxCqfGAqzHTak/N/HmLR3e/Kpq5ydbtI/ZVcZ/337dltZMaoybsapPy13O6BjDL4J2kvyd8ASd8\
39585# IfNNDutHSD1Y4GJ6SSnej2WsVPRlbNDcK1VaMq44p27a1fjSNAxuegSxcjjEtnh5sw5KqTfZgeD5+3w5YhFfP7No7T3aRNrbqWlz2o4Bj3F8q7/sLbrvb\
39586# D0qjenW7Q4nJ3GOVh39DHNLIWu9FUtyekNT9hbTNaLPSxY/Elvmo5rJar+nk1bVXGFWF/a92Helf5gVsNt97CVKWlRBxl8hocgTrjLPATUGMCmqyzFGMs\
39587# cqrFcKHnNZVXdyefakdV07Y3c2X0uc/awcKWtHfx4HUvmaZliPTZar5VTrSBYyHrJK/oKYArbAKrdQ5tRaZqtDNtjipHRbdu3Z7z3NAbJKi3yCKaZGXRo\
39588# 8c663xEeMCF0ACTNWnxXnnZmIryMQT2Yu5kl6g13pG+C45zCdjsdRxVGlE/sPRVI9nQtMOZXqjebLMW/uTSVDDb8ybb5hZLTTF+36RhMHPo1G6rjVv2bJ\
39589# m3RafOPjk5Ob2gPMrJiYFFO3XyI99TDzUV270s351vyJfjabfXjNHuU42m2VPTWam7FwaYy2o0hneSWNvypDvNtMNmq+D4ROmjZMSwrMvLK8eWf/yYq+o\
39590# fkFhYwnwOtQ3eTA7XpNlmpzjBbusM092gxlwTMHpfxk11tYOqZvpef2rC1B0HqwXnlaXqcWaF3jTZMv+Pe8avyyTNKit8xctwpAsadDvaWw7pru3ett8X\
39591# nOYlWFxRKaEoXKdQNi7jEaPJ+pITZaz2Lr6ceXhsQto5RnrX06DWGWrQoCmpDMOgxQywzlIHOgCcrh4jzLfAVH9Ei6IjrJcd9mI37VrU6WUcTS4wwe7yd\
39592# Vh1K45VVJwa2yJBPu6Px/I394mT4pPoL0k/iNnxUL458lFGV/wyfh5iTBSiEjNiZjwYuyokb4QQcyOiKw4PIS+3PZfLNeQUxfHxUUmvEPdHbcGzib/SmE\
39593# vIa7XGIa41yvW21jHPWO3u8r5p5mjzM5B3s35jzXWxe7DLSpd6Ja2MH8hZ4yIstwd8mvIwzG2uGJSIrcZZaaWxbnJYZaKVmRsbxdgVmRCiMUaEvJgYfdE\
39594# Vk0MIMTKEfHPkozOyIcQ5EfFiCLEwIu4u6ZVbW0S0VEnmRsTfIx/Hh4JCmbn9ohj50o63xP4hZS49QU6vRnNk0CnHNi5S5yl/hnZtO9u0oVhqJT6GGxId\
39595# Q4t5XMaiKkmfPerNV4s+u+kloS81rkOrWiusd2Xyw0OZTJJP3WaZaKKJ6Y4Z9nM93kpFX7TMMsvMJr0nby3JRqRa7VY5P6bVqElSiYLlwnIbLNAIw5xl/\
39596# IDxzbpFXTwTOyMi4uOYGUKsiYgLQngk1cs3F/KFQmFDvB27ImJbHJO6tYw7QmhL3FrGQalbfxwnRX9xbVGxvZjLNeTkRCZuKGl3xXlJCCwYYK5DK/1eNd\
39597# 5NPnaoZxyMPRgFF5luuukDj8iMJvvb5GQb0xM+aooppri3wmVnl2Q7Kih/05NmmqmDLIiiu413jfcMszJ5ElbcHJclcbQwhGiOv0XEpSFui4gVyUn6y62\
39598# 5kC90FrIxKnZEX5IsxSESom1fCXFXiKOLPfHbeDVy0RAKCgrJ/PB4PSIWhHBNVcwxr6apRON7JdN/peBbCV21aqfX1tQOaG+3TF3yWh/qftzrKqrEJq3O\
39599# qniBzckcCLq8W7VdWucu06pLj81Ocbac32ODFa60zj0+cIZvW+JWkrdYQf29FjjHrLS6TXJVqfeCv5R6c+0Gea2V1hUX1841TheY7WFtHvBXk5yv15pBR\
39600# 7lMcWTxifQu2BVfLzkiGw+l1X9zTA79zfl8vjOfzQuxICLeiMyghIj4Tlrnyuiocqt+sTii5NbGQmv0lfR6Y07y52sGXHKZR1GzxIcO8Knf2E5BT1IBjj\
39601# NDk02e10Ohrvg1RasU80Y0OFeN3+kw0YkVZ13vfZxbinfo9ywON9VGb9GpsdlZ+j0nisgcaZbRtjrBjeSFSQOZNtIT+sXCyvDdZaGHyqGatnzaOgfN/Cv\
39602# trop+Tt/AaGEIveYkZg1E3xLmJa/mfVb3/xQ595HziY2OSYUTspnrkrwcTaOb4cZkauCx2cGK/5VV1XjawMWSdV2vpUm/Z7DiQCnZ+n8w6zN8Bv4Bs+PS\
39603# zRd3ZU8AAAAASUVORK5CYII="/>  <a href="https://www.smallhd.com/community/movie-looks-download">SmallHD Free \
39604# Movie Look Pack</a></center>"}
39605
39606#@gui : sep = value(0)_0+
39607#@gui : note = note{"<center><a href="https://freshluts.com/users/52679">Youssef Hossam</a></center>"}
39608
39609#@gui : sep = value(0)_2+
39610#@gui : sep = separator()
39611#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.
39612#@gui :         Latest Update: <i>2019/10/11</i>.</small>")
39613fx_color_presets :
39614  category=${arg\ 1+$1,abigailgonzalez,alexjordan,berat,cinematic,cinematic_travel,creative,ericellerbrock,filtergrade,\
39615                       inavision,jtsemple,kylerholland,lutifyme,michaelezra,moviz,ohadperetz,oliviosarikas,on1,\
39616                       picturefx,pixelmator,pixlsus,purple11,rocketstock,shamoonabbasi,smallhd,youssefhossam,others}
39617  presets=${-_fx_cluts_$category}
39618  index={arg(1+$1,${2-27})}
39619  thumbsize,strength,brightness,contrast,gamma,hue,saturation,normalize=${28-35}
39620
39621  if $normalize==1" || "$normalize==3 # Pre-normalization
39622    repeat $! l[$>] split_opacity balance_gamma[0] , a c endl done
39623  fi
39624  if $index>=2 # Apply CLUT
39625    path_clut=${-path_cache}
39626    name=${arg\ 1+$index-2,$presets}
39627    clut $name,{0$_is_preview" && "!isfile(['{/${path_clut}clut_$name.cimgz}'])?17:48}
39628    repeat $!-1 if $strength<100 +map_clut[$>] . j[$>] .,0,0,0,0,{$strength%} rm. else map_clut[$>] . fi done
39629    rm.
39630    adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
39631    if $normalize==2" || "$normalize==3 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi # Post-normalization
39632
39633  elif $index==1 # "None"
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  else # Collage
39638    repeat $! l[$>] if max(w,h)>$thumbsize rr2d $thumbsize,$thumbsize,0,2 fi endl done
39639    N=$! +to "Original",1%,1%,7.5%,2,0.5
39640    Np={narg($presets)} repeat $Np
39641      clut_name=${arg\ 1+$>,$presets}
39642      clut $clut_name mv. $N
39643      repeat $N
39644        if $strength<100 [$>] +map_clut. [$N] j.. .,0,0,0,0,{$strength%} rm. else +map_clut[$>] [$N] fi
39645        adjust_colors. $brightness,$contrast,$gamma,$hue,$saturation,0,255
39646        if $normalize==2" || "$normalize==3 l. split_opacity n[0] 0,255 a c endl fi # Post-normalization
39647        strcapitalize $clut_name clut_name=${}
39648        to. $clut_name,0.01~,0.01~,7.5%,1
39649      done
39650      rm[$N]
39651      progress {$>*100/($Np-1)}
39652    done
39653    k[$N--1] frame 1,1,0,0,0,255 - 128 append_tiles {s=floor(sqrt($!));w>h?[s,0]:[0,s]} + 128
39654  fi
39655
39656fx_color_presets_preview :
39657  _is_preview=1
39658  index={arg(1+$1,${2-27})}
39659  if !$index gui_warning_preview "Preview disabled in 'Collage' mode"
39660  else gui_split_preview "fx_color_presets $*",${36-38}
39661  fi
39662  u "{$1}{$2}_"{2*($1==0)}\
39663        "{$3}_"{2*($1==1)}\
39664        "{$4}_"{2*($1==2)}\
39665        "{$5}_"{2*($1==3)}\
39666        "{$6}_"{2*($1==4)}\
39667        "{$7}_"{2*($1==5)}\
39668        "{$8}_"{2*($1==6)}\
39669        "{$9}_"{2*($1==7)}\
39670        "{$10}_"{2*($1==8)}\
39671        "{$11}_"{2*($1==9)}\
39672        "{$12}_"{2*($1==10)}\
39673        "{$13}_"{2*($1==11)}\
39674        "{$14}_"{2*($1==12)}\
39675        "{$15}_"{2*($1==13)}\
39676        "{$16}_"{2*($1==14)}\
39677        "{$17}_"{2*($1==15)}\
39678        "{$18}_"{2*($1==16)}\
39679        "{$19}_"{2*($1==17)}\
39680        "{$20}_"{2*($1==18)}\
39681        "{$21}_"{2*($1==19)}\
39682        "{$22}_"{2*($1==20)}\
39683        "{$23}_"{2*($1==21)}\
39684        "{$24}_"{2*($1==22)}\
39685        "{$25}_"{2*($1==23)}\
39686        "{$26}_"{2*($1==24)}\
39687        "{$27}_"{2*($1==25)}\
39688        "{$28}_"{1+!$index}\
39689        "{$29}{$30}{$31}{$32}{$33}{$34}{$35}{$36}{$37,$38}"\
39690        "{0}_"{2*($1!=24)}\
39691        "{0}_"{2*($1==0)}\
39692        "{0}_"{2*($1==1)}\
39693        "{0}_"{2*($1==2)}\
39694        "{0}_"{2*($1==3)}\
39695        "{0}_"{2*($1==4)}\
39696        "{0}_"{2*($1==5)}\
39697        "{0}_"{2*($1==6)}\
39698        "{0}_"{2*($1==7)}\
39699        "{0}_"{2*($1==8)}\
39700        "{0}_"{2*($1==9)}\
39701        "{0}_"{2*($1==10)}\
39702        "{0}_"{2*($1==11)}\
39703        "{0}_"{2*($1==12)}\
39704        "{0}_"{2*($1==13)}\
39705        "{0}_"{2*($1==14)}\
39706        "{0}_"{2*($1==15)}\
39707        "{0}_"{2*($1==16)}\
39708        "{0}_"{2*($1==17)}\
39709        "{0}_"{2*($1==18)}\
39710        "{0}_"{2*($1==19)}\
39711        "{0}_"{2*($1==20)}\
39712        "{0}_"{2*($1==21)}\
39713        "{0}_"{2*($1==22)}\
39714        "{0}_"{2*($1==23)}\
39715        "{0}_"{2*($1==24)}\
39716        "{0}"
39717
39718_fx_cluts_abigailgonzalez :
39719  u blade_runner,blue_house,blue_ice,caribe,cinema,cinema_2,cinema_3,\
39720    cinema_4,cinema_5,cinema_noir,cinematic_for_flog,day_4nite,eterna_for_flog,filmic,\
39721    fuji_hdr,goldengate,matrix,monochrome_1,monochrome_2,old_west,science_fiction
39722
39723_fx_cluts_alexjordan :
39724  u action_magenta_01,action_red_01,adventure_1453,agressive_highligjtes_recovery_5,bleech_bypass_green,\
39725    bleech_bypass_yellow_01,blue_dark,blue_shadows_01,bright_green_01,brownish,colorful_0209,conflict_01,\
39726    contrast_with_highlights_protection,contrasty_afternoon,contrasty_green,cross_process_cp_130,cross_process_cp_14,\
39727    cross_process_cp_15,cross_process_cp_16,cross_process_cp_18,cross_process_cp_3,cross_process_cp_4,\
39728    cross_process_cp_6,dark_green_02,dark_green_1,dark_place_01,dream_1,dream_85,faded_retro_01,faded_retro_02,\
39729    film_0987,film_9879,film_high_contrast,flat_30,green_2025,green_action,green_afternoon,\
39730    green_conflict,green_day_01,green_day_02,green_g_09,green_indoor,green_light,harsh_day,harsh_sunset,\
39731    highlights_protection,indoor_blue,low_contrast_blue,low_key_01,magenta_day,magenta_day_01,magenta_dream,\
39732    memories,moonlight_01,mostly_blue,muted_01,night_01,only_red,only_red_and_blue,operation_yellow,orange_dark_4,\
39733    orange_dark_7,orange_dark_look,orange_underexposed,protect_highlights_01,red_afternoon_01,red_day_01,red_dream_01,\
39734    retro_brown_01,retro_magenta_01,retro_yellow_01,saturated_blue,smart_contrast,subtle_blue,subtle_green,yellow_55b,\
39735    yellow_film_01
39736
39737_fx_cluts_berat :
39738  u brownbm,cineblue,cinebm4k,goldentime,green_and_orange,monochrome,sevsuz,sunlightlove,western,westernlut2
39739
39740_fx_cluts_cinematic :
39741  u deep,dimension,enchanted,flavin,frosted,shine,ultra_water,wipe
39742
39743_fx_cluts_cinematic_travel :
39744  u blue_cold_fade,bright_teal_orange,bright_warm,clear_teal_fade,cold_clear_blue,cold_clear_blue_1,deep_blue,\
39745    deep_dark_warm,deep_high_contrast,deep_teal_fade,deep_warm_fade,faded_green,greenish_contrasty,greenish_fade,\
39746    greenish_fade_1,hard_teal_orange,neutral_teal_orange,neutral_warm_fade,smooth_clear,smooth_green_orange,\
39747    smooth_teal_orange,teal_fade,very_warm_greenish,warm_dark_contrasty,warm_fade,warm_fade_1,warm_neutral,\
39748    warm_sunset_red,warm_teal
39749
39750_fx_cluts_creative :
39751  u anime,bleachbypass_1,bleachbypass_2,bleachbypass_3,bleachbypass_4,candlelight,colornegative,crispwarm,crispwinter,\
39752    dropblues,edgyember,fallcolors,foggynight,futuristicbleak_1,futuristicbleak_2,futuristicbleak_3,futuristicbleak_4,\
39753    horrorblue,latesunset,moonlight,nightfromday,redblueyellow,smokey,softwarming,tealmagentagold,tealorange,\
39754    tealorange_1,tealorange_2,tealorange_3,tensiongreen_1,tensiongreen_2,tensiongreen_3,tensiongreen_4
39755
39756_fx_cluts_ericellerbrock :
39757  u avalanche,black_star,helios,hydracore,hypnosis,killstreak,nemesis,night_blade_4,paladin,seringe_4,serpent,terra_4,\
39758    victory,yellowstone
39759
39760_fx_cluts_filtergrade :
39761  u fgcinebasic,fgcinebright,fgcinecold,fgcinedrama,fgcinetealorange_1,fgcinetealorange_2,fgcinevibrant,fgcinewarm
39762
39763_fx_cluts_inavision :
39764  u 7drk_21,bc_darkum,brown_mobster,cold_ice,dark_man_x,film_gb-19,formula_b,gremerta,hitman,jwick_21,london_nights,\
39765    louetta,nightlife,vfb_21,vintage_mob
39766
39767_fx_cluts_jtsemple :
39768  u brightgreen,crispromance,crushin,frostedbeachpicnic,justpeachy,lateafternoonwanderlust,lushgreensummer,\
39769    magentacoffee,minimalistcaffeination,mysticpurplesunset,nostalgiahoney,springmorning,toastedgarden,\
39770    winterlighthouse
39771
39772_fx_cluts_kylerholland :
39773  u kh1,kh2,kh3,kh4,kh5,kh6,kh7,kh8,kh9,kh10
39774
39775_fx_cluts_lutifyme :
39776  u hackmanite,herderite,heulandite,hiddenite,hilutite,howlite,hypersthene
39777
39778_fx_cluts_michaelezra :
39779  u deepskintones2,deepskintones3
39780
39781_fx_cluts_moviz :
39782  u moviz_1,moviz_2,moviz_3,moviz_4,moviz_5,moviz_6,moviz_7,moviz_8,moviz_9,moviz_10,\
39783    moviz_11,moviz_12,moviz_13,moviz_14,moviz_15,moviz_16,moviz_17,moviz_18,moviz_19,moviz_20,\
39784    moviz_21,moviz_22,moviz_23,moviz_24,moviz_25,moviz_26,moviz_27,moviz_28,moviz_29,moviz_30,\
39785    moviz_31,moviz_32,moviz_33,moviz_34,moviz_35,moviz_36,moviz_37,moviz_38,moviz_39,moviz_40,\
39786    moviz_41,moviz_42,moviz_43,moviz_44,moviz_45,moviz_46,moviz_47,moviz_48
39787
39788_fx_cluts_ohadperetz :
39789  u cold_simplicity_2,d_o_1,retro_summer_3,subtle_yellow,teal_moonlight,true_colors_8,vintage_warmth_1
39790
39791_fx_cluts_oliviosarikas :
39792  u analog_film_1,atomic_pink,beach_aqua_orange,beach_faded_analog,bw_but_yellow,city_dust,dark_orange_teal,\
39793  day_to_night_kings_blue,duotone_blue_red,faded_pink-ish,flat_blue_moon,honey_light,infrared_-_dust_pink,neutral_pump,\
39794  shade_kings_ink,sunset_aqua_orange,sunset_intense_violet_blue,sunset_violet_mood,violet_taste
39795
39796_fx_cluts_on1 :
39797  u 2-strip-process,aqua,aqua_and_orange_dark,berlin_sky,blues,\
39798    bw_1,bw_2,bw_3,bw_4,bw_5,bw_6,bw_7,bw_8,bw_9,bw_10,chrome_01,\
39799    cinematic-1,cinematic-2,cinematic-3,cinematic-4,cinematic-5,cinematic-6,cinematic-7,cinematic-8,\
39800    cinematic-9,cinematic-10,\
39801    classic_teal_and_orange,earth_tone_boost,fade_to_green,film_print_01,film_print_02,french_comedy,green_blues,\
39802    green_yellow,\
39803    landscape_1,landscape_2,landscape_3,landscape_4,landscape_5,landscape_6,landscape_7,landscape_8,landscape_9,\
39804    landscape_10,\
39805    lc_1,lc_2,lc_3,lc_4,lc_5,lc_6,lc_7,lc_8,lc_9,lc_10,\
39806    moody_1,moody_2,moody_3,moody_4,moody_5,moody_6,moody_7,moody_8,moody_9,moody_10,\
39807    nw-1,nw-2,nw-3,nw-4,nw-5,nw-6,nw-7,nw-8,nw-9,nw-10,oranges,\
39808    portrait_1,portrait_2,portrait_3,portrait_4,portrait_5,portrait_6,portrait_7,portrait_8,portrait_9,portrait_10,\
39809    purple_2,reds,reds_oranges_yellows,studio_skin_tone_shaper,vintage_chrome
39810
39811_fx_cluts_picturefx :
39812  u analogfx_anno_1870_color,analogfx_old_style_i,analogfx_old_style_ii,analogfx_old_style_iii,\
39813    analogfx_sepia_color,analogfx_soft_sepia_i,analogfx_soft_sepia_ii,\
39814    faux_infrared_bw_1,faux_infrared_color_p2,faux_infrared_color_p3,faux_infrared_color_r0a,\
39815    faux_infrared_color_r0b,faux_infrared_color_yp1,\
39816    goldfx_bright_spring_breeze,goldfx_bright_summer_heat,goldfx_hot_summer_heat,\
39817    goldfx_perfect_sunset_01min,goldfx_perfect_sunset_05min,goldfx_perfect_sunset_10min,\
39818    goldfx_spring_breeze,goldfx_summer_heat,technicalfx_backlight_filter,\
39819    zilverfx_bw_solarization,zilverfx_infrared,zilverfx_vintage_bw
39820
39821_fx_cluts_pixelmator :
39822  u black_white_01,black_white_02,black_white_03,black_white_04,black_white_05,black_white_06,\
39823    pmcinematic_01,pmcinematic_02,pmcinematic_03,pmcinematic_04,pmcinematic_05,pmcinematic_06,pmcinematic_07,\
39824    classic_films_01,classic_films_02,classic_films_03,classic_films_04,classic_films_05,\
39825    landscape_01,landscape_02,landscape_03,landscape_04,landscape_05,\
39826    modern_films_01,modern_films_02,modern_films_03,modern_films_04,modern_films_05,modern_films_06,modern_films_07,\
39827    pmnight_01,pmnight_02,pmnight_03,pmnight_04,pmnight_05,\
39828    urban_01,urban_02,urban_03,urban_04,urban_05,\
39829    vintage_01,vintage_02,vintage_03,vintage_04,vintage_05
39830
39831_fx_cluts_pixlsus :
39832  u amstragram,amstragram+,autumn,cinematic_lady_bird,cinematic_mexico,dark_blues_in_sunlight,delicatessen,expired_69,\
39833    fadedlook,faded_print,hypressen,magenta_yellow,metropolis,modern_film,newspaper,night_spy,progressen,prussian_blue,\
39834    seventies_magazine,street,sweet_bubblegum,sweet_gelatto,taiga,tarraco,unknown,uzbek_bukhara,\
39835    uzbek_marriage,uzbek_samarcande,velvetia,warm_vintage,whiter_whites
39836
39837_fx_cluts_purple11 :
39838  u good_morning,going_for_a_walk,nah,once_upon_a_time,serenity,passing_by,smooth_sailing,undeniable,undeniable2,\
39839    urban_cowboy,well_see,you_can_do_it
39840
39841_fx_cluts_rocketstock :
39842  u arabica_12,ava_614,azrael_93,bourbon_64,byers_11,chemical_168,clayton_33,clouseau_54,cobi_3,contrail_35,\
39843    cubicle_99,django_25,domingo_145,\
39844    faded_47,folger_50,fusion_88,hyla_68,korben_214,lenox_340,lucky_64,mckinnon_75,milo_5,neon_770,paladin_1875,\
39845    pasadena_21,pitaya_15,reeve_38,remy_24,sprocket_231,teigen_28,trent_18,tweed_71,vireo_37,zed_32,zeke_39
39846
39847_fx_cluts_shamoonabbasi :
39848  u city_7,coffee_44,date_39,day_for_night,denoiser_simple_40,desert_gold_37,directions_23,drop_green_tint_14,\
39849  elegance_38,golden_night_softner_43,golden_sony_37,green_15,happyness_133,hlg_1_1,industrial_33,morning_6,\
39850  morroco_16,night_king_141,rest_33,shadow_king_39,spy_29,thriller_2,turkiest_42,vintage_163,wooden_gold_20
39851
39852_fx_cluts_smallhd :
39853  u apocalypse_this_very_moment,bboyz_2,bob_ford,life_giving_tree,moonrise,saving_private_damon,the_matrices
39854
39855_fx_cluts_others :
39856  u 60s,60s_faded,60s_faded_alt,alien_green,black_and_white,bleach_bypass,blue_mono,\
39857    cinematic_01,cinematic_02,cinematic_03,\
39858    color_rich,faded,faded_alt,faded_analog,faded_extreme,faded_vivid,expired_fade,expired_polaroid,extreme,fade,\
39859    faux_infrared,golden,golden_bright,golden_fade,golden_mono,golden_vibrant,green_mono,hong_kong,instantc,\
39860    k_tone_vintage_kodachrome,light_blown,lomo,mono_tinted,\
39861    muted_fade,mute_shift,natural_vivid,nostalgic,orange_tone,pink_fade,purple,retro,rotate_muted,\
39862    rotate_vibrant,rotated,rotated_crush,\
39863    smooth_cromeish,smooth_fade,soft_fade,solarized_color,solarized_color_2,summer,summer_alt,sunny,sunny_alt,\
39864    sunny_warm,\
39865    sunny_rich,super_warm,super_warm_rich,sutro_fx,vibrant,vibrant_alien,vibrant_contrast,vibrant_cromeish,\
39866    vintage,vintage_alt,vintage_brighter,warm,warm_highlight,warm_yellow
39867
39868_fx_cluts_youssefhossam :
39869  u cinematic_forest,city,darkness,hallowen_dark,sea
39870
39871#@gui Colorful Blobs : fx_colorful_blobs, fx_colorful_blobs_preview
39872#@gui : Colorspace = choice(1,"sRGB","Linear RGB","Lab")
39873#@gui : Background Color = color(200,200,200,0)
39874#@gui : Display Blob Controls = bool(1)
39875#@gui : sep = separator()
39876#@gui : Blob 1 = point(25,25,1,1,0,0,0,0,5)
39877#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39878#@gui : Blob 1 Color = color(255,0,0)
39879#@gui : Previous = value(-1,-1,-1,-1)
39880#@gui : sep = separator()
39881#@gui : Blob2 = point(75,25,1,1,0,0,0,0,5)
39882#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39883#@gui : Blob 2 Color = color(0,255,0)
39884#@gui : Previous = value(-1,-1,-1,-1)
39885#@gui : sep = separator()
39886#@gui : Blob 3 = point(50,75,1,1,0,0,0,0,5)
39887#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39888#@gui : Blob 3 Color = color(0,0,255)
39889#@gui : Previous = value(-1,-1,-1,-1)
39890#@gui : sep = separator()
39891#@gui : Blob 4 = point(5,90,-1,1,0,0,0,0,5)
39892#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39893#@gui : Blob 4 Color = color(255,255,0)
39894#@gui : Previous = value(-1,-1,-1,-1)
39895#@gui : sep = separator()
39896#@gui : Blob 5 = point(5,90,-1,1,0,0,0,0,5)
39897#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39898#@gui : Blob 5 Color = color(255,0,255)
39899#@gui : Previous = value(-1,-1,-1,-1)
39900#@gui : sep = separator()
39901#@gui : Blob 6 = point(5,90,-1,1,0,0,0,0,5)
39902#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39903#@gui : Blob 6 Color = color(0,255,255)
39904#@gui : Previous = value(-1,-1,-1,-1)
39905#@gui : sep = separator()
39906#@gui : Blob 7 = point(5,90,-1,1,0,0,0,0,5)
39907#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39908#@gui : Blob 7 Color = color(255,255,255)
39909#@gui : Previous = value(-1,-1,-1,-1)
39910#@gui : sep = separator()
39911#@gui : Blob 8 = point(5,90,-1,1,0,0,0,0,5)
39912#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39913#@gui : Blob 8 Color = color(0,0,0)
39914#@gui : Previous = value(-1,-1,-1,-1)
39915#@gui : sep = separator()
39916#@gui : Blob 9 = point(5,90,-1,1,0,0,0,0,5)
39917#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39918#@gui : Blob 9 Color = color(255,128,64)
39919#@gui : Previous = value(-1,-1,-1,-1)
39920#@gui : sep = separator()
39921#@gui : Blob 10 = point(5,90,-1,1,0,0,0,0,5)
39922#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39923#@gui : Blob 10 Color = color(255,64,128)
39924#@gui : Previous = value(-1,-1,-1,-1)
39925#@gui : sep = separator()
39926#@gui : Blob 11 = point(5,90,-1,1,0,0,0,0,5)
39927#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39928#@gui : Blob 11 Color = color(128,64,255)
39929#@gui : Previous = value(-1,-1,-1,-1)
39930#@gui : sep = separator()
39931#@gui : Blob 12 = point(5,90,-1,1,0,0,0,0,5)
39932#@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0
39933#@gui : Blob 12 Color = color(64,128,255)
39934#@gui : Previous = value(-1,-1,-1,-1)
39935#@gui : sep = separator()
39936#@gui : note = note("This filter can be used to create custom palettes with given color shades.
39937#@gui : It has been inspired by
39938#@gui : <a href="https://research.adobe.com/project/playful-palette-an-interactive-parametric-color-mixer-for-artists/">
39939#@gui : Adobe's Playful Palette</a>.")
39940#@gui : sep = separator()
39941#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/08/26</i>.</small>")
39942fx_colorful_blobs :
39943  N=12
39944  colorspace,bgR,bgG,bgB,bgA,display_controls,\
39945  x0,y0,rx0,ry0,R0,G0,B0,p_x0,p_y0,p_rx0,p_ry0,\
39946  x1,y1,rx1,ry1,R1,G1,B1,p_x1,p_y1,p_rx1,p_ry1,\
39947  x2,y2,rx2,ry2,R2,G2,B2,p_x2,p_y2,p_rx2,p_ry2,\
39948  x3,y3,rx3,ry3,R3,G3,B3,p_x3,p_y3,p_rx3,p_ry3,\
39949  x4,y4,rx4,ry4,R4,G4,B4,p_x4,p_y4,p_rx4,p_ry4,\
39950  x5,y5,rx5,ry5,R5,G5,B5,p_x5,p_y5,p_rx5,p_ry5,\
39951  x6,y6,rx6,ry6,R6,G6,B6,p_x6,p_y6,p_rx6,p_ry6,\
39952  x7,y7,rx7,ry7,R7,G7,B7,p_x7,p_y7,p_rx7,p_ry7,\
39953  x8,y8,rx8,ry8,R8,G8,B8,p_x8,p_y8,p_rx8,p_ry8,\
39954  x9,y9,rx9,ry9,R9,G9,B9,p_x9,p_y9,p_rx9,p_ry9,\
39955  x10,y10,rx10,ry10,R10,G10,B10,p_x10,p_y10,p_rx10,p_ry10,\
39956  x11,y11,rx11,ry11,R11,G11,B11,p_x11,p_y11,p_rx11,p_ry11,\
39957  =$*
39958  if !0$_is_preview display_controls=0 fi
39959  if $1==1 srgb2cs=srgb2rgb cs2srgb=rgb2srgb
39960  elif $1==2 srgb2cs=srgb2lab cs2srgb=lab2srgb
39961  fi
39962
39963  {0,s=min(w,h);[s,s]},1,4 k. 100%,100%,1,1,1e-8
39964
39965  repeat $N
39966    # If center point has been moved -> Update radius point.
39967    rx$>,ry$>={"P = ["${x$>},${y$>}"];
39968                R = ["${rx$>},${ry$>}"];
39969                oP = ["${p_x$>},${p_y$>}"];
39970                oP==[-1,-1]?P + [10,0]:P!=oP?R + P - oP:R"}
39971
39972    if !isnan(${x$>})
39973      x,y,rx,ry,R,G,B={"const w1 = (w - 1)%; const h1 = (h -1)%; "\
39974                       round([${x$>}*w1,${y$>}*h1,${rx$>}*w1,${ry$>}*h1,${R$>},${G$>},${B$>}])}
39975      r={max(1,round(norm($x-$rx,$y-$ry)))}
39976      if $1 ($R^$G^$B) $srgb2cs. R,G,B={^} rm. fi
39977      f. "*
39978        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;
39979        const r = 1.2*"($r)";
39980        dist = norm(x-"$x",y-"$y")/r;
39981        w = pexp(dist);
39982        j(#0,0,0,0) += w*"$R";
39983        j(#0,0,0,0,1) += w*"$G";
39984        j(#0,0,0,0,2) += w*"$B";
39985        i + w;
39986      "
39987    fi
39988  done
39989  sh[0] 0,2 /. [1] if $1 $cs2srgb. fi
39990  f[0] "*begin(bg = [ "$bgR,$bgG,$bgB,$bgA" ]); i(#1)<0.5?bg:[R,G,B,255]" k[0]
39991
39992  repeat $N
39993    if !isnan(${x$>})" && "$display_controls
39994    x,y,rx,ry,R,G,B={"const w1 = (w - 1)%; const h1 = (h -1)%; "\
39995                     round([${x$>}*w1,${y$>}*h1,${rx$>}*w1,${ry$>}*h1,${R$>},${G$>},${B$>}])}
39996      circle $x,$y,3,0.85,0xFFFFFFFF,{v=avg(crop($x-3,$y-3,7,7))>128?0:255;[v,v,v,255]}
39997      rectangle {"const x = "$rx"; const y = "$ry"; [x-2,y-2,x+2,y+2]"},0.85,0xFFFFFFFF,\
39998                {v=avg(crop($rx-3,$ry-3,7,7))>128?0:255;[v,v,v,255]}
39999      line $x,$y,$rx,$ry,0.5,0xF0F0F0F0,255 line $x,$y,$rx,$ry,0.5,0x0F0F0F0F,0,0,0,255
40000    fi
40001  done
40002
40003  if 0$_is_preview
40004    u \{$colorspace\}\{$bgR,$bgG,$bgB,$bgA\}\{$display_controls\}\
40005      \{$x0,$y0\}\{$rx0,$ry0\}\{$R0,$G0,$B0\}\{$x0,$y0,$rx0,$ry0\}\
40006      \{$x1,$y1\}\{$rx1,$ry1\}\{$R1,$G1,$B1\}\{$x1,$y1,$rx1,$ry1\}\
40007      \{$x2,$y2\}\{$rx2,$ry2\}\{$R2,$G2,$B2\}\{$x2,$y2,$rx2,$ry2\}\
40008      \{$x3,$y3\}\{$rx3,$ry3\}\{$R3,$G3,$B3\}\{$x3,$y3,$rx3,$ry3\}\
40009      \{$x4,$y4\}\{$rx4,$ry4\}\{$R4,$G4,$B4\}\{$x4,$y4,$rx4,$ry4\}\
40010      \{$x5,$y5\}\{$rx5,$ry5\}\{$R5,$G5,$B5\}\{$x5,$y5,$rx5,$ry5\}\
40011      \{$x6,$y6\}\{$rx6,$ry6\}\{$R6,$G6,$B6\}\{$x6,$y6,$rx6,$ry6\}\
40012      \{$x7,$y7\}\{$rx7,$ry7\}\{$R7,$G7,$B7\}\{$x7,$y7,$rx7,$ry7\}\
40013      \{$x8,$y8\}\{$rx8,$ry8\}\{$R8,$G8,$B8\}\{$x8,$y8,$rx8,$ry8\}\
40014      \{$x9,$y9\}\{$rx9,$ry9\}\{$R9,$G9,$B9\}\{$x9,$y9,$rx9,$ry9\}\
40015      \{$x10,$y10\}\{$rx10,$ry10\}\{$R10,$G10,$B10\}\{$x10,$y10,$rx10,$ry10\}\
40016      \{$x11,$y11\}\{$rx11,$ry11\}\{$R11,$G11,$B11\}\{$x11,$y11,$rx11,$ry11\}
40017   fi
40018
40019fx_colorful_blobs_preview :
40020  _is_preview=1
40021  rm {s=min($_preview_width,$_preview_height)/2;[s,s]},1,1
40022  fx_colorful_blobs $*
40023
40024#@gui Colormap : fx_colormap,fx_colormap_preview
40025#@gui : Colormap = choice{2,"Adaptive","Custom","Standard (256)","HSV (256)","Lines (256)","Hot (256)",
40026#@gui : "Cool (256)","Jet (256)","Flag (256)","Cube (256)"}
40027#@gui : Dithering = float(1,0,1)
40028#@gui : sep = separator()
40029#@gui : Number of Tones = int(32,2,256)_0
40030#@gui : Number of Colors = int(8,2,8)_0
40031#@gui : 1st Color = color(0,0,0)_0
40032#@gui : 2nd Color = color(255,255,255)_0
40033#@gui : 3rd Color = color(255,0,0)_0
40034#@gui : 4th Color = color(0,255,0)_0
40035#@gui : 5th Color = color(0,0,255)_0
40036#@gui : 6th Color = color(255,255,0)_0
40037#@gui : 7th Color = color(255,0,255)_0
40038#@gui : 8th Color = color(0,255,255)_0+
40039#@gui : sep = separator()
40040#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40041#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40042#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40043#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40044#@gui : sep = separator()
40045#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/27/12</i>.</small>")
40046fx_colormap :
40047  repeat $! l[$>] split_opacity to_rgb[0]
40048    if $1>=2                # Pre-defined colormap.
40049      index[0] {$1-2},$2,1
40050    elif $1==1              # Custom colormap.
40051      (${5-28}) z. 0,{3*$4-1}
40052      r. 3,{w/3},1,1,-1 permute. yzcx r. $3,1,1,3,3
40053      index[0] .,$2,1 rm.
40054    else                      # Adaptive colormap.
40055      autoindex[0] $3,$2,{if($3<=32,1,0)}
40056    fi
40057    a c
40058  endl done
40059
40060fx_colormap_preview :
40061  gui_split_preview "fx_colormap $*",${-3--1}
40062  is_ad,is_cu={2*[$1==0||$1==1,$1==1]}
40063  u "{$1}{$2}"\
40064    "{$3}_"$is_ad\
40065    "{$4}_"$is_cu\
40066    "{${5-7}}_"$is_cu\
40067    "{${8-10}}_"$is_cu\
40068    "{${11-13}}_"$is_cu\
40069    "{${14-16}}_"$is_cu\
40070    "{${17-19}}_"$is_cu\
40071    "{${20-22}}_"$is_cu\
40072    "{${23-25}}_"$is_cu\
40073    "{${26-28}}_"$is_cu\
40074    "{$29}{$30,$31}"
40075
40076#@gui Color Mask [Interactive] : fx_mask_color, gui_no_preview
40077#@gui : Color Metric = _choice(13,"RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","Linear RGB [All]",
40078#@gui : "Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
40079#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [red chrominance]",
40080#@gui : "YCbCr [green chrominance]","Lab [all]","Lab [lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
40081#@gui : "Lab [b-Chrominance]","Lch [all]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]",
40082#@gui : "HSV [all]","HSV [hue]","HSV [saturation]","HSV [value]","HSI [all]","HSI [intensity]","HSL [all]",
40083#@gui : "HSL [lightness]","CMYK [cyan]","CMYK [magenta]","CMYK [yellow]","CMYK [key]","YIQ [luma]","YIQ [chromas]")
40084#@gui : Spatial Tolerance = _float(10,0,100)
40085#@gui : Color Tolerance = _float(5,0,100)
40086#@gui : sep = separator()
40087#@gui : Output Mode = _choice(0,"Masked image","Color mask")
40088#@gui : sep = separator()
40089#@gui : note = note{"<small><b>Note:</b> This filter is CPU consuming, so use it at least with 4+ cores
40090#@gui : (or reduce the size of the interactive window to speed up computation).</small>"}
40091#@gui : note = note{"<small><b>Interactions:</b>\n
40092#@gui : Use the following actions in the interactive window to build your color mask :\n\n
40093#@gui : - <b>Left mouse button</b> make the color pointed by the mouse wanted for the mask.\n
40094#@gui : - <b>Right mouse button</b> make the color pointed by the mouse unwanted for the mask.\n
40095#@gui : - <b>Middle mouse button</b> or key <b>R</b> resets color mask.\n
40096#@gui : - Key <b>SPACE</b> or <b>TAB</b> toggles view modes (half/full-masked RGB or color mask).\n
40097#@gui : - Keys <b>CTRL+D</b> increase window size.\n
40098#@gui : - Keys <b>CTRL+C</b> decrease window size.\n
40099#@gui : - Keys <b>CTRL+R</b> resets window size.\n
40100#@gui : - Keys <b>ESC</b>, <b>Q</b> or <b>ENTER</b> exit the interactive window.
40101#@gui : </small>"}
40102#@gui : sep = separator()
40103#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>01/20/2017</i>.</small>")
40104fx_mask_color :
40105  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,\
40106     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,\
40107     cmyk_c,cmyk_m,cmyk_y,cmyk_k,yiq_y,yiq_iq
40108  repeat $! l[$<] to_rgb nm={n} nm ${-gui_layer_name}
40109    +x_mask_color ${arg\ 1+$1,$cs},$2,$3
40110    if $4==1 channels. 100% fi
40111    nm $nm rv
40112  endl done
40113
40114#@gui Curves : fx_curves_interactive, fx_curves_interactive_preview
40115#@gui : Colorspace = choice{"RGB","CMY","CMYK","HSI","HSL","HSV","Lab","Lch","YCbCr"}
40116#@gui : Output Preset as a HaldCLUT Layer = _choice("Disable","Lowres CLUT","Highres CLUT")
40117#@gui : Apply Transformation From = _choice("New Curves [Interactive]","Curves Previously Defined")
40118#@gui : Colorspace = value(0)
40119#@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)
40120#@gui : sep = separator()
40121#@gui : note = note{"<small><b>Description:</b>\n
40122#@gui : This filter allows to apply color curves on your images, in many different colorspaces.
40123#@gui : Click on the <i>Apply</i> or <i>OK</i> buttons below to open the G'MIC interactive windows and
40124#@gui : start building your color curves.
40125#@gui : When you're done, exit the main image window: your modified result will be transferred back to the
40126#@gui : host software.\n\n
40127#@gui : Once you've set curves, you can save them by pressing the <b>Add to faves</b> button below the filter tree.
40128#@gui : To clear control points for your curves, click on the <i>Reset</i> button above.
40129#@gui : </small>"}
40130#@gui : sep = separator()
40131#@gui : note = note{"<small><b>Interactions:</b>\n
40132#@gui : Use the following actions in the interactive windows to manage your colorization :\n\n
40133#@gui : - <b>Left mouse button</b> on a curve creates a new color control point (or move an existing one).\n
40134#@gui : - <b>Right mouse button</b> on a control point deletes it.\n
40135#@gui : - <b>Left mouse button</b> on the main image window shows the initial image until button is released.\n
40136#@gui : - <b>Right mouse button</b> on the main image window adds a keypoint to all curves from picked color.\n
40137#@gui : - Key <b>R</b> on a curve resets it.\n
40138#@gui : - Keys <b>CTRL+D</b> increase window size.\n
40139#@gui : - Keys <b>CTRL+C</b> decrease window size.\n
40140#@gui : - Keys <b>CTRL+R</b> resets window size.\n
40141#@gui : - Keys <b>ESC</b>, <b>Q</b> or <b>ENTER</b> close the current window.
40142#@gui : </small>"}
40143#@gui : sep = separator()
40144#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>09/28/2014</i>.</small>")
40145fx_curves_interactive :
40146  nm "Color curves"
40147  repeat 4 __xcc_C$>=0,0,100,100 done
40148  if $4==$1 l[] ($5) s -,-1 repeat $! __xcc_C$>={$>,^} done rm endl fi
40149  if $3 # Apply transformation from previously defined curves
40150    _xcc_colorbase=${arg\ {$4+1},rgb,cmy,cmyk,hsi,hsl,hsv,lab,lch,ycbcr} x_color_curves last
40151  else # Run interactive curve builder
40152    x_color_curves ${arg\ {$1+1},rgb,cmy,cmyk,hsi,hsl,hsv,lab,lch,ycbcr}
40153    u "{$1}{$2}{$3}{$1}{"$__xcc_C0,-1,$__xcc_C1,-1,$__xcc_C2,-1,$__xcc_C3,-1,$__xcc_C4"}"
40154  fi
40155  if $2 # Add HaldCLUT layer
40156    (0,255) (0;255) (0/255) r[-3--1] 2,2,2 a[-3--1] c
40157    if $2==2 r. 256,256,256,3,3 r. 4096,4096,1,3,-1 # High-res HaldCLUT
40158    else r. 64,64,64,3,3 r. 512,512,1,3,-1 # Low-res HaldCLUT
40159    fi
40160    x_color_curves. last
40161  fi
40162
40163fx_curves_interactive_preview :
40164  fx_curves_interactive $1,0,1,$4,"$5"
40165
40166#@gui Customize CLUT : fx_customize_clut,fx_customize_clut_preview(1)+
40167#@gui : Keypoint Influence (%) = float(100,0,100)
40168#@gui : Lock Uniform Sampling = choice{0,"None","8 Keypoints (RGB Corners)","27 Keypoints","64 Keypoints",
40169#@gui : "125 Keypoints","216 Keypoints","343 Keypoints"},
40170#@gui : Spatial Regularization = int(10,0,30)
40171#@gui : sep = separator()
40172#@gui : note = note("<small><b>Global correction:</b></small>")
40173#@gui : Brightness (%) = float(0,-100,100)
40174#@gui : Contrast (%) = float(0,-100,100)
40175#@gui : Gamma (%) = float(0,-100,100)
40176#@gui : Hue (%) = float(0,-100,100)
40177#@gui : Saturation (%) = float(0,-100,100)
40178#@gui : Post-Normalize = bool(0)
40179#@gui : sep = separator()
40180#@gui : Output Corresponding CLUT = _choice("Disable","512x512 Layer","4096x4096 Layer")
40181#@gui : Preview Type = choice{8,"Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40182#@gui : "Backward Vertical","Duplicate Horizontal","Duplicate Vertical","HaldCLUT","3D CLUT (Fast)","3D CLUT (Precise)"}
40183#@gui : CLUT Opacity = float(0.5,0,1)
40184#@gui : sep = separator()
40185#@gui : note = note("<small><b>Color correspondences:</b></small>")
40186#@gui : Action #1 = choice(1,"Ignore","Lock Source","Replace Source by Target")
40187#@gui : Source Color #1 = color(0,0,0), Target Color #1 = color(0,0,0)
40188#@gui : sep = separator()
40189#@gui : Action #2 = choice(1,"Ignore","Lock Source","Replace Source by Target")
40190#@gui : Source Color #2 = color(255,255,255), Target Color #2 = color(255,196,128)
40191#@gui : sep = separator()
40192#@gui : Action #3 = choice("Ignore","Lock Source","Replace Source by Target")
40193#@gui : Source Color #3 = color(0,0,0), Target Color #3 = color(0,0,0)
40194#@gui : sep = separator()
40195#@gui : Action #4 = choice("Ignore","Lock Source","Replace Source by Target")
40196#@gui : Source Color #4 = color(0,0,0), Target Color #4 = color(0,0,0)
40197#@gui : sep = separator()
40198#@gui : Action #5 = choice("Ignore","Lock Source","Replace Source by Target")
40199#@gui : Source Color #5 = color(0,0,0), Target Color #5 = color(0,0,0)
40200#@gui : sep = separator()
40201#@gui : Action #6 = choice("Ignore","Lock Source","Replace Source by Target")
40202#@gui : Source Color #6 = color(0,0,0), Target Color #6 = color(0,0,0)
40203#@gui : sep = separator()
40204#@gui : Action #7 = choice("Ignore","Lock Source","Replace Source by Target")
40205#@gui : Source Color #7 = color(0,0,0), Target Color #7 = color(0,0,0)
40206#@gui : sep = separator()
40207#@gui : Action #8 = choice("Ignore","Lock Source","Replace Source by Target")
40208#@gui : Source Color #8 = color(0,0,0), Target Color #8 = color(0,0,0)
40209#@gui : sep = separator()
40210#@gui : Action #9 = choice("Ignore","Lock Source","Replace Source by Target")
40211#@gui : Source Color #9 = color(0,0,0), Target Color #9 = color(0,0,0)
40212#@gui : sep = separator()
40213#@gui : Action #10 = choice("Ignore","Lock Source","Replace Source by Target")
40214#@gui : Source Color #10 = color(0,0,0), Target Color #10 = color(0,0,0)
40215#@gui : sep = separator()
40216#@gui : Action #11 = choice("Ignore","Lock Source","Replace Source by Target")
40217#@gui : Source Color #11 = color(0,0,0), Target Color #11 = color(0,0,0)
40218#@gui : sep = separator()
40219#@gui : Action #12 = choice("Ignore","Lock Source","Replace Source by Target")
40220#@gui : Source Color #12 = color(0,0,0), Target Color #12 = color(0,0,0)
40221#@gui : sep = separator()
40222#@gui : Action #13 = choice("Ignore","Lock Source","Replace Source by Target")
40223#@gui : Source Color #13 = color(0,0,0), Target Color #13 = color(0,0,0)
40224#@gui : sep = separator()
40225#@gui : Action #14 = choice("Ignore","Lock Source","Replace Source by Target")
40226#@gui : Source Color #14 = color(0,0,0), Target Color #14 = color(0,0,0)
40227#@gui : sep = separator()
40228#@gui : Action #15 = choice("Ignore","Lock Source","Replace Source by Target")
40229#@gui : Source Color #15 = color(0,0,0), Target Color #15 = color(0,0,0)
40230#@gui : sep = separator()
40231#@gui : Action #16 = choice("Ignore","Lock Source","Replace Source by Target")
40232#@gui : Source Color #16 = color(0,0,0), Target Color #16 = color(0,0,0)
40233#@gui : sep = separator()
40234#@gui : Action #17 = choice("Ignore","Lock Source","Replace Source by Target")
40235#@gui : Source Color #17 = color(0,0,0), Target Color #17 = color(0,0,0)
40236#@gui : sep = separator()
40237#@gui : Action #18 = choice("Ignore","Lock Source","Replace Source by Target")
40238#@gui : Source Color #18 = color(0,0,0), Target Color #18 = color(0,0,0)
40239#@gui : sep = separator()
40240#@gui : Action #19 = choice("Ignore","Lock Source","Replace Source by Target")
40241#@gui : Source Color #19 = color(0,0,0), Target Color #19 = color(0,0,0)
40242#@gui : sep = separator()
40243#@gui : Action #20 = choice("Ignore","Lock Source","Replace Source by Target")
40244#@gui : Source Color #20 = color(0,0,0), Target Color #20 = color(0,0,0)
40245#@gui : sep = separator()
40246#@gui : Action #21 = choice("Ignore","Lock Source","Replace Source by Target")
40247#@gui : Source Color #21 = color(0,0,0), Target Color #21 = color(0,0,0)
40248#@gui : sep = separator()
40249#@gui : Action #22 = choice("Ignore","Lock Source","Replace Source by Target")
40250#@gui : Source Color #22 = color(0,0,0), Target Color #22 = color(0,0,0)
40251#@gui : sep = separator()
40252#@gui : Action #23 = choice("Ignore","Lock Source","Replace Source by Target")
40253#@gui : Source Color #23 = color(0,0,0), Target Color #23 = color(0,0,0)
40254#@gui : sep = separator()
40255#@gui : Action #24 = choice("Ignore","Lock Source","Replace Source by Target")
40256#@gui : Source Color #24 = color(0,0,0), Target Color #24 = color(0,0,0)
40257#@gui : sep = separator()
40258#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/14/06</i>.</small>")
40259fx_customize_clut :
40260
40261  # Build CLUT.
40262  if !narg($_N) N=64 else N=$_N fi N1={$N-1}
40263  $N,$N,$N,4
40264
40265  if $2 # Lock uniform sampling
40266    uniform_distribution {(1+$2)^3},3
40267    repeat w point.. {round($N1*I[$>])},1,{255*I[$>]},1 done rm.
40268  fi
40269
40270  $=arg # Add user-defined color correspondences
40271  repeat 24
40272    mode=${arg{13+7*$<}}
40273    if $mode
40274      sr=${arg{14+7*$<}} sg=${arg{15+7*$<}} sb=${arg{16+7*$<}}
40275      tr=${arg{17+7*$<}} tg=${arg{18+7*$<}} tb=${arg{19+7*$<}}
40276      xyz={round(($N1/255)*[$sr,$sg,$sb])}
40277      point. $xyz,1,{$mode==2?[$tr,$tg,$tb]:[$sr,$sg,$sb]},1
40278    fi
40279  done
40280
40281  s c,-3
40282  if $1<100 # Need to compute a weighting map.
40283    +distance. 1
40284    if $1 ^. {1/(0.05+4*$1%)} else f. 0 fi
40285    n. 0,1 nm. influence mv. -3
40286  fi
40287  ==. 0 inpaint_pde.. .,100%,1,20 rm. c. 0,255
40288
40289  if $influence
40290    100%,100%,100%,3,[x,y,z] n. 0,255
40291    j. ..,0,0,0,0,1,...
40292    rm[-3,-2]
40293  fi
40294
40295  # Apply CLUT on input layers + global color corrections.
40296  if !$3 map_clut[^-1] . # w/o spatial regularization
40297  else repeat $!-1       # w/ spatial regularization
40298    +luminance[$>] +map_clut[$>] .. -. [$>]
40299    repeat $3 guided. ..,2,50 done +[$>,-1] rm.
40300  done fi
40301  adjust_colors ${4-8},0,255
40302  if $9 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi
40303  if $10
40304    if $10==2 r. 256,256,256,3,5 c. 0,255 fi
40305    siz={w^1.5} r. $siz,$siz,1,3,-1
40306    mv. 0
40307  else rm.
40308  fi
40309
40310fx_customize_clut_preview :
40311  if $11<7 gui_split_preview "fx_customize_clut ${1-9},0,0,${12--1}",$11
40312  elif $11==7 # HaldCLUT preview
40313    rm fx_customize_clut ${1-9},1,0,${12--1}
40314  elif $11>=8 # 3D CLUT preview
40315    _N={$11>=9?64:32}
40316    k[0] to_rgb w={w} h={h}
40317    +fx_customize_clut ${1-9},1,0,${12--1} mv. 1
40318    r. $_N,$_N,$_N,3,-1 pointcloud3d. o3d. $12
40319    l[]
40320      if $2 # Lock uniform sampling
40321        uniform_distribution {(1+$2)^3},3
40322        repeat w circle3d {0,round($_N*I[$>])},0.75 col3d. {0,255*I[$>]} done rm[0]
40323      fi
40324      $=arg # Add user-defined color correspondences
40325      repeat 24
40326        mode=${arg{13+7*$<}}
40327        if $mode
40328          sr=${arg{14+7*$<}} sg=${arg{15+7*$<}} sb=${arg{16+7*$<}}
40329          tr=${arg{17+7*$<}} tg=${arg{18+7*$<}} tb=${arg{19+7*$<}}
40330          xy={round(($_N/255)*[$sr,$sg])}
40331          z={round(($_N/255)*$sb)-0.1}
40332          circle3d $xy,$z,0.75 col3d. {$mode==2?[$tr,$tg,$tb]:[$sr,$sg,$sb]}
40333        fi
40334      done
40335      colorcube3d *3d. {$_N/255} o3d. 0.5 col3d. 0 p3d. 1
40336    endl
40337    +3d[2--1]
40338    pose3d. 5.10656,2.04904,2.723,-316.115,-0.0815767,4.97762,-3.59262,-41.7094,\
40339            -3.40685,2.95212,4.16756,-118.811,0,0,203,1
40340
40341    # Try to find the best layout for displaying preview.
40342    if $w>$h # Landscape mode
40343      r2dx[0,1] {0,round(w/2)}
40344      to[0] "Before",2,0,13,1,0.75
40345      to[1] "After",2,0,13,1,0.75
40346      a[0,1] y r[0] 100%,$h,1,3,0
40347    else # Portrait mode.
40348      r2dy[0,1] {0,round(h/2)}
40349      to[0] "Before",2,0,13,1,0.75
40350      to[1] "After",2,0,13,1,0.75
40351      a[0,1] x r[0] $w,100%,1,3,0
40352    fi
40353
40354    snapshot3d. {0,1.1*min(w,h)},1.2,64,64,64
40355    autocrop. -. 64 r. {0,max(w,$w-w)},{0,max(h,$h-h)},1,3,0,0,0.5,0.5 +. 64
40356    to. "RGB CLUT",2,0,13,1,0.75
40357    a {`$w>$h?_'x':_'y'`}
40358  fi
40359
40360#@gui Decompose Channels : fx_decompose_channels, fx_decompose_channels_preview
40361#@gui : Color Basis = choice(7,"RGB","HSV","HSL","HSI","YUV","YCbCr","XYZ","Lab","Lch","CMY","CMYK","YIQ")
40362#@gui : Action = choice("Decompose","Recompose")
40363#@gui : Output Multiple Layers = _bool(0)
40364#@gui : Include Opacity Layer = bool(1)
40365#@gui : sep = separator()
40366#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/19/07</i>.</small>")
40367fx_decompose_channels :
40368  if !$2 # Decompose
40369    if $4 to_rgba else to_rgb fi
40370    repeat $! l[$<] nm={0,n}
40371      split_opacity
40372      _s3=A _s4=A
40373      _fx_decompose_channels$1[0]
40374      s[0] c
40375      if !$3 a x nm $nm
40376      else nm=${-gui_layer_name} repeat $! gui_set_layer_name[$>] {``$nm}" ["${_s$>}"]" done
40377      fi
40378    endl done
40379  else # Recompose
40380    channels 0 nbc={3+($1==10)} nb={$nbc+$4}
40381    if $3 repeat int($!/$nb) l[0-{$nb-1}]
40382      a[0-{$nbc-1}] c _fx_recompose_channels$1[0] a c
40383    endl mv. 0 done
40384    else repeat $! l[$>]
40385      s x,$nb a[0-{$nbc-1}] c _fx_recompose_channels$1[0] a c
40386    endl mv. 0 done fi
40387  fi
40388
40389fx_decompose_channels_preview :
40390  repeat $! l[$<]
40391    _s3=A _s4=A
40392    fx_decompose_channels $1,$2,1,$4
40393    if !$2
40394      fs={round(min(w,h)*15%)}
40395      repeat $! to[$>] ${_s$>},5,3,$fs,{max(2,round($fs/15))} done
40396      to_rgba
40397    fi
40398  endl done
40399  append_tiles ,
40400
40401_fx_decompose_channels0 : _s0=R _s1=G _s2=B
40402_fx_decompose_channels1 : rgb2hsv8 _s0=H _s1=S _s2=V
40403_fx_decompose_channels2 : rgb2hsl8 _s0=H _s1=S _s2=L
40404_fx_decompose_channels3 : rgb2hsi8 _s0=H _s1=S _s2=I
40405_fx_decompose_channels4 : rgb2yuv8 _s0=Y _s1=U _s2=V
40406_fx_decompose_channels5 : rgb2ycbcr _s0=Y _s1=Cb _s2=Cr
40407_fx_decompose_channels6 : rgb2xyz8 _s0=X _s1=Y _s2=Z
40408_fx_decompose_channels7 : rgb2lab8 _s0=L _s1=a _s2=b
40409_fx_decompose_channels8 : rgb2lch8 _s0=L _s1=c _s2=h
40410_fx_decompose_channels9 : rgb2cmy _s0=C _s1=M _s2=Y
40411_fx_decompose_channels10 : rgb2cmyk _s0=C _s1=M _s2=Y _s3=K
40412_fx_decompose_channels11 : rgb2yiq8 _s0=Y _s1=I _s2=Q
40413
40414_fx_recompose_channels0 :
40415_fx_recompose_channels1 : hsv82rgb
40416_fx_recompose_channels2 : hsl82rgb
40417_fx_recompose_channels3 : hsi82rgb
40418_fx_recompose_channels4 : yuv82rgb
40419_fx_recompose_channels5 : ycbcr2rgb
40420_fx_recompose_channels6 : xyz82rgb
40421_fx_recompose_channels7 : lab82rgb
40422_fx_recompose_channels8 : lch82rgb
40423_fx_recompose_channels9 : cmy2rgb
40424_fx_recompose_channels10 : cmyk2rgb
40425_fx_recompose_channels11 : yiq82rgb
40426
40427#@gui Detect Skin : fx_detect_skin, fx_detect_skin_preview(1)
40428#@gui : Skin Estimation = choice(1,"Manual","Automatic")
40429#@gui : sep = separator()
40430#@gui : Tolerance = float(0.5,0,1)
40431#@gui : Smoothness = float(0.5,0,5)
40432#@gui : Threshold = float(1,0,10)
40433#@gui : Pre-Normalize Image = bool(1)
40434#@gui : sep = separator()
40435#@gui : note = note("<small><b>Manual estimation:</b>\n
40436#@gui : Use the sliders below to target as much skin pixels as you can.</small>")
40437#@gui : X-Coordinate = float(50,0,100)
40438#@gui : Y-Coordinate = float(50,0,100)
40439#@gui : Radius = float(5,0,25)
40440#@gui : sep = separator()
40441#@gui : Output Mode = choice(1,"Probability Map","Opaque Skin","Transparent Skin")
40442#@gui : sep = separator()
40443#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40444#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40445#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40446#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40447#@gui : sep = separator()
40448#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/03/01</i>.</small>")
40449fx_detect_skin :
40450  to_rgb
40451  m "_fx_detect_skin :
40452       if $5 balance_gamma 128,128,128 fi
40453       if $1 detect_skin $2 else detect_skin $2,$6%,$7%,$8% fi
40454       M={iM} b $3% * {255*$M/iM} * $4 c 0,255"
40455  repeat $! l[$>]
40456    if $9  # Opaque/transparent skin.
40457      +_fx_detect_skin a c
40458      if $9>1 sh 100% *. -1 +. 255 rm. fi
40459    else _fx_detect_skin # Probability mask.
40460    fi
40461  endl done
40462  um _fx_detect_skin
40463
40464fx_detect_skin_preview :
40465  gui_split_preview "fx_detect_skin $*",${-3--1}
40466  to_rgba
40467  if !$1
40468    circle $6%,$7%,$8%,0.3,0,255,0,255
40469    circle $6%,$7%,$8%,1,0xFFFFFFFF,0,255,0,255
40470    line {$6-0.25*$8}%,{$7-0.25*$8}%,{$6+0.25*$8}%,{$7+0.25*$8}%,1,255,255,0,255
40471    line {$6+0.25*$8}%,{$7-0.25*$8}%,{$6-0.25*$8}%,{$7+0.25*$8}%,1,255,255,0,255
40472  fi
40473
40474#@gui Equalize HSV : fx_hsv_equalizer, fx_hsv_equalizer_preview
40475#@gui : Preview Bands = bool(false)
40476#@gui : sep = separator()
40477#@gui : Hue Band = float(180,0,360)
40478#@gui : Band Width = float(40,1,360)
40479#@gui : Hue Shift = float(0,-180,180)
40480#@gui : Saturation Correction = float(0,-0.99,0.99)
40481#@gui : Value Correction = float(0,-0.99,0.99)
40482#@gui : sep = separator()
40483#@gui : Hue Band = float(180,0,360)
40484#@gui : Band Width = float(40,1,360)
40485#@gui : Hue Shift = float(0,-180,180)
40486#@gui : Saturation Correction = float(0,-0.99,0.99)
40487#@gui : Value Correction = float(0,-0.99,0.99)
40488#@gui : sep = separator()
40489#@gui : Hue Band = float(180,0,360)
40490#@gui : Band Width = float(40,1,360)
40491#@gui : Hue Shift = float(0,-180,180)
40492#@gui : Saturation Correction = float(0,-0.99,0.99)
40493#@gui : Value Correction = float(0,-0.99,0.99)
40494#@gui : sep = separator()
40495#@gui : note = note("<small>Author: <i>Jérome Ferrari</i>.
40496#@gui :       Latest Update: <i>01/14/2011</i>.</small>")
40497#@gui : url = link("Filter explained here","http://www.flickr.com/groups/gmic/discuss/72157625798533482")
40498fx_hsv_equalizer :
40499  repeat $! l[$>]
40500  to_rgb rgb2hsv s c
40501# From now on 0,1,2 are H,S,V
40502#3 masks:
40503  +f[0] if(abs(i-$2)<$3/2|abs(i-$2-360)<$3/2|abs(i-$2+360)<$3/2,1,0)
40504  +f[0] if(abs(i-$7)<$8/2|abs(i-$7-360)<$8/2|abs(i-$7+360)<$8/2,1,0)
40505  +f[0] if(abs(i-$12)<$13/2|abs(i-$12-360)<$13/2|abs(i-$12+360)<$13/2,1,0)
40506# From now on 3,4,5 are Masks
40507  +threshold[1,2] 0.01 *[-1,-2] [-1]x2 *[-1,3] *[-1,4] *[-1,5]  #0 saturation and value not in mask
40508# Hue shift:
40509  +*[3] $4 +*[4] $9 +*[5] $14 +[-1,-2,-3]
40510  +[-1,0] %[0] 360
40511# Saturation :
40512  if $5>=0 +*[3] -$5 else +*[3] {1/(1+$5)-1} fi +. 1
40513  if $10>=0 +*[4] -$10 else +*[4] {1/(1+$10)-1} fi +. 1
40514  if $15>=0 +*[5] -$15 else +*[5] {1/(1+$15)-1} fi +. 1
40515  *[-1,-2,-3] ^[1,-1]
40516# Value :
40517  if $6>=0 +*[3] -$6 else +*[3] {1/(1+$6)-1} fi +. 1
40518  if $11>=0 +*[4] -$11 else +*[4] {1/(1+$11)-1} fi +. 1
40519  if $16>=0 +*[5] -$16 else +*[5] {1/(1+$16)-1} fi +. 1
40520  *[-1,-2,-3] ^[2,-1]
40521#reconstruction
40522  rm[3,4,5] a[0,1,2] c hsv2rgb
40523  endl done
40524
40525fx_hsv_equalizer_preview :
40526  l.
40527  if $1==0 fx_hsv_equalizer $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16
40528  else
40529    to_rgb rgb2hsv s c
40530    (0,359) r. ..,{{0,h}/10},1,1,3 . f. 1       #create lower band
40531    j[0] [3],0,91% j[1] [4],0,91% j[2] [4],0,91% rm[-1,-2] #paste lower band
40532    +f[0] if(abs(i-$2)<$3/2|abs(i-$2-360)<$3/2|abs(i-$2+360)<$3/2,1,0)
40533    +f[0] if(abs(i-$7)<$8/2|abs(i-$7-360)<$8/2|abs(i-$7+360)<$8/2,1,0)
40534    +f[0] if(abs(i-$12)<$13/2|abs(i-$12-360)<$13/2|abs(i-$12+360)<$13/2,1,0) #masks
40535    -|[-3--1] +. 0.33 /. 1.33   #1 and 0.25
40536    *[2,-1] a c hsv2rgb
40537  fi endl
40538
40539#@gui Equalize HSI-HSL-HSV : fx_equalize_hsv, fx_equalize_hsv_preview(0)+
40540#@gui : Colorspace = choice(1,"HSI","HSL","HSV")
40541#@gui : Opacity (%) = float(100,0,100)
40542#@gui : Value Blending = float(0,0,64)
40543#@gui : Color Blending = float(0,0,64)
40544#@gui : sep = separator()
40545#@gui : Preview Mapping = choice("None","Grey","Color")
40546#@gui : sep = separator()
40547#@gui : note = note("<small><b>Black:</b></small>")
40548#@gui : Hue Offset = float(0,-180,180)
40549#@gui : Saturation Offset = float(0,-1,1)
40550#@gui : Value Offset = float(0,-1,1)
40551#@gui : sep = separator()
40552#@gui : note = note("<small><b>Near black:</b></small>")
40553#@gui : Hue Offset = float(0,-180,180)
40554#@gui : Saturation Offset = float(0,-1,1)
40555#@gui : Value Offset = float(0,-1,1)
40556#@gui : sep = separator()
40557#@gui : note = note("<small><b>Dark grey:</b></small>")
40558#@gui : Hue Offset = float(0,-180,180)
40559#@gui : Saturation Offset = float(0,-1,1)
40560#@gui : Value Offset = float(0,-1,1)
40561#@gui : sep = separator()
40562#@gui : note = note("<small><b>Mi-dark grey:</b></small>")
40563#@gui : Hue Offset = float(0,-180,180)
40564#@gui : Saturation Offset = float(0,-1,1)
40565#@gui : Value Offset = float(0,-1,1)
40566#@gui : sep = separator()
40567#@gui : note = note("<small><b>Middle grey:</b></small>")
40568#@gui : Hue Offset = float(0,-180,180)
40569#@gui : Saturation Offset = float(0,-1,1)
40570#@gui : Value Offset = float(0,-1,1)
40571#@gui : sep = separator()
40572#@gui : note = note("<small><b>Mid-light grey:</b></small>")
40573#@gui : Hue Offset = float(0,-180,180)
40574#@gui : Saturation Offset = float(0,-1,1)
40575#@gui : Value Offset = float(0,-1,1)
40576#@gui : sep = separator()
40577#@gui : note = note("<small><b>Light grey:</b></small>")
40578#@gui : Hue Offset = float(0,-180,180)
40579#@gui : Saturation Offset = float(0,-1,1)
40580#@gui : Value Offset = float(0,-1,1)
40581#@gui : sep = separator()
40582#@gui : note = note("<small><b>Highlights:</b></small>")
40583#@gui : Hue Offset = float(0,-180,180)
40584#@gui : Saturation Offset = float(0,-1,1)
40585#@gui : Value Offset = float(0,-1,1)
40586#@gui : sep = separator()
40587#@gui : note = note("<small><b>White:</b></small>")
40588#@gui : Hue Offset = float(0,-180,180)
40589#@gui : Saturation Offset = float(0,-1,1)
40590#@gui : Value Offset = float(0,-1,1)
40591#@gui : sep = separator()
40592#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40593#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40594#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40595#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40596#@gui : sep = separator()
40597#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>David Revoy</i>.
40598#@gui :       Latest Update: <i>2018/01/19</i>.</small>")
40599fx_equalize_hsv :
40600  cs=${"arg 1+$1,hsi,hsl,hsv"}
40601  repeat $! l[$>] split_opacity l[0]
40602    to_rgb
40603    9,1,1,4,\
40604    {"V = [${6-30:3}]*pi/180; [cos(V),sin(V)]"},${7-31:3},\
40605    {"V = [${8-32:3}];
40606      const sV = size(V);
40607      repeat (sV,k,
40608        v0 = k/sV;
40609        v1 = (k+1)/sV;
40610        V[k]*=(V[k]>0?(1 - v0):v1);
40611      ); V"}
40612    r. 256,1,1,4,1
40613    sh. 3 b. $3 rm.   # Value smoothness
40614    f. "[ atan2(G,R)*180/pi,B,A,0 ]" channels. 0,2
40615    +rgb2$cs.. +channels. 100% *. 256 round. map. ...
40616    +[-2,-1] rm.. +channels. 100% ${cs}2rgb..
40617    if $4 # Spatial smoothness
40618      *. 255 bilateral.. .,$4,{2+$4}
40619      rgb2$cs.. /. 255 j.. .,0,0,0,2 ${cs}2rgb..
40620    fi
40621    rm.
40622    blend alpha,{$2%}
40623  endl a c endl done
40624
40625fx_equalize_hsv_preview :
40626  if $5
40627    cs=${"arg 1+$1,hsi,hsl,hsv"}
40628    rm {0.8*[${-gui_preview_wh}]},1,3,\
40629    "$5==1?
40630       (H = S = 0; V = y/(h-1)):
40631       (H = x*360/(w-1); S = y/(h-1); V = y/(h-1));
40632     [H,S,V]"
40633    ${cs}2rgb.
40634  fi
40635  gui_split_preview "fx_equalize_hsv $*",${-3--1}
40636  if $5 r. ${-gui_preview_wh},1,3,0,0,0.5,0.5 fi
40637
40638#@gui Mixer [CMYK] : fx_mix_cmyk, fx_mix_cmyk_preview(1)+
40639#@gui : Cyan Factor = float(1,0,4)
40640#@gui : Cyan Shift = float(0,-255,255)
40641#@gui : Cyan Smoothness = float(0,0,10)
40642#@gui : sep = separator()
40643#@gui : Magenta Factor = float(1,0,4)
40644#@gui : Magenta Shift = float(0,-255,255)
40645#@gui : Magenta Smoothness = float(0,0,10)
40646#@gui : sep = separator()
40647#@gui : Yellow Factor = float(1,0,4)
40648#@gui : Yellow Shift = float(0,-255,255)
40649#@gui : Yellow Smoothness = float(0,0,10)
40650#@gui : sep = separator()
40651#@gui : Key Factor = float(1,0,4)
40652#@gui : Key Shift = float(0,-255,255)
40653#@gui : Key Smoothness = float(0,0,10)
40654#@gui : sep = separator()
40655#@gui : Tones Range = choice("All tones","Shadows","Mid-Tones","Highlights")
40656#@gui : Tones Smoothness = float(2,0,10)
40657#@gui : sep = separator()
40658#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40659#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40660#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40661#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40662#@gui : sep = separator()
40663#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
40664fx_mix_cmyk :
40665  repeat $! l. split_opacity rv to_rgb.
40666    fx_start_mix $13,$14
40667    rgb2cmyk. s. c
40668    *[-4] $1 +[-4] $2 b[-4] $3%
40669    *... $4 +... $5 b... $6%
40670    *.. $7 +.. $8 b.. $9%
40671    *. $10 +. $11 b. $12%
40672    a[-4--1] c cmyk2rgb.
40673    fx_end_mix $13
40674  if $!!=3 rv a c fi endl mv. 0 done
40675
40676fx_mix_cmyk_preview :
40677  gui_split_preview "fx_mix_cmyk $*",${-3--1}
40678
40679#@gui Mixer [HSV] : fx_mix_hsv, fx_mix_hsv_preview(1)+
40680#@gui : Hue Factor = float(1,0,4)
40681#@gui : Hue Shift = float(0,-180,180)
40682#@gui : Hue Smoothness = float(0,0,10)
40683#@gui : sep = separator()
40684#@gui : Saturation Factor = float(1,0,4)
40685#@gui : Saturation Shift = float(0,-1,1)
40686#@gui : Saturation Smoothness = float(0,0,10)
40687#@gui : sep = separator()
40688#@gui : Value Factor = float(1,0,4)
40689#@gui : Value Shift = float(0,-1,1)
40690#@gui : Value Smoothness = float(0,0,10)
40691#@gui : sep = separator()
40692#@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights")
40693#@gui : Tones Smoothness = float(2,0,10)
40694#@gui : sep = separator()
40695#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40696#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40697#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40698#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40699#@gui : sep = separator()
40700#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
40701fx_mix_hsv :
40702  repeat $! l. split_opacity rv to_rgb.
40703    fx_start_mix $10,$11
40704    rgb2hsv. s. c -[-2,-1] 0.5
40705    *... $1 +... $2 b... $3%
40706    *.. $4 +.. $5 b.. $6%
40707    *. $7 +. $8 b. $9%
40708    %... 360 +[-2,-1] 0.5 c[-2,-1] 0,1 a[-3--1] c hsv2rgb.
40709    fx_end_mix $10
40710  if $!!=3 rv a c fi endl mv. 0 done
40711
40712fx_mix_hsv_preview :
40713  gui_split_preview "fx_mix_hsv $*",${-3--1}
40714
40715#@gui Mixer [Lab] : fx_mix_lab, fx_mix_lab_preview(1)+
40716#@gui : Lightness Factor = float(1,0.5,1.5)
40717#@gui : Lightness Shift = float(0,-50,50)
40718#@gui : Lightness Smoothness = float(0,0,10)
40719#@gui : sep = separator()
40720#@gui : A-Color Factor = float(1,0,4)
40721#@gui : A-Color Shift = float(0,-20,20)
40722#@gui : A-Color Smoothness = float(0,0,10)
40723#@gui : sep = separator()
40724#@gui : B-Color Factor = float(1,0,4)
40725#@gui : B-Color Shift = float(0,-20,20)
40726#@gui : B-Color Smoothness = float(0,0,10)
40727#@gui : sep = separator()
40728#@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights")
40729#@gui : Tones Smoothness = float(2,0,10)
40730#@gui : sep = separator()
40731#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40732#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40733#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40734#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40735#@gui : sep = separator()
40736#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
40737fx_mix_lab :
40738  repeat $! l[$>] split_opacity to_rgb[0]
40739    gui_parallel_overlap[0] "_fx_mix_lab $*",0,{3*max($3,$6,$9)}
40740    a c
40741  endl mv[$>] 0 done
40742
40743_fx_mix_lab :
40744  fx_start_mix $10,$11
40745  rgb2lab. s. c
40746  *... $1 +... $2 b... $3%
40747  *.. $4 +.. $5 b.. $6%
40748  *. $7 +. $8 b. $9%
40749  a[-3--1] c lab2rgb.
40750  fx_end_mix $10
40751
40752fx_mix_lab_preview :
40753  gui_split_preview "fx_mix_lab $*",${-3--1}
40754
40755#@gui Mixer [PCA] : fx_mix_pca, fx_mix_pca_preview(1)+
40756#@gui : Primary Factor = float(0,-1.5,1.5)
40757#@gui : Primary Shift = float(0,-255,255)
40758#@gui : Primary Twist = float(0,-180,180)
40759#@gui : Primary Gamma = float(0,-100,100)
40760#@gui : sep = separator()
40761#@gui : Secondary Factor = float(0,-1.5,1.5)
40762#@gui : Secondary Shift = float(0,-255,255)
40763#@gui : Secondary Twist = float(0,-180,180)
40764#@gui : Secondary Gamma = float(0,-100,100)
40765#@gui : sep = separator()
40766#@gui : Tertiary Factor = float(0,-1.5,1.5)
40767#@gui : Tertiary Shift = float(0,-255,255)
40768#@gui : Tertiary Twist = float(0,-180,180)
40769#@gui : Tertiary Gamma = float(0,-100,100)
40770#@gui : sep = separator()
40771#@gui : Display Color Axes = bool(1)
40772#@gui : Stats = value(-1,-1,-1,-1)
40773#@gui : Avg Covariance = value(0,0,0,0,0,0,0,0,0,0,0,0)
40774#@gui : sep = separator()
40775#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40776#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40777#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40778#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40779#@gui : sep = separator()
40780#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/07/18</i>.</small>")
40781fx_mix_pca :
40782  repeat $! l[$>] split_opacity l[0] to_rgb
40783
40784    # Get image covariance (and remember it to speed up multiple calls of the filter).
40785    if [$14]==round(stats()[0,4],0.1) _avg={[$15][0,3]} C={[$15][3,9]} status=
40786    else
40787      +rr2d 256,256,0,2 C=${"covariance_colors. _avg"} rm.
40788      __status="{$1}{$2}{$3}{$4}"\
40789               "{$5}{$6}{$7}{$8}"\
40790               "{$9}{$10}{$11}{$12}"\
40791               "${13}"\
40792               "{"{round(stats()[0,4],0.1)}"}"\
40793               "{"$_avg,$C"}"\
40794               "{$16}{${17,18}}"
40795    fi
40796
40797    # Find value ranges for gamma correction.
40798    if "$4 || $8 || $12" +l
40799      f "begin(avg = ["$_avg"]; eig = eig(["$C"]); Pt = eig[3,9]); Pt*(I-avg)"
40800      s c repeat $! vmax$>={$>,1.1*max(abs(im),abs(iM))} done
40801      rm
40802    endl else vmax0,vmax1,vmax2=1 fi
40803
40804    # Modify image values.
40805    f "begin(
40806         do_gamma(val,vmax,gamma) = (vmax*sign(val)*(abs(val)/vmax)^gamma);
40807
40808         const gamma0 = 10^-($4%);
40809         const gamma1 = 10^-($8%);
40810         const gamma2 = 10^-($12%);
40811         const vmax0 = "$vmax0";
40812         const vmax1 = "$vmax1";
40813         const vmax2 = "$vmax2";
40814
40815         avg = ["$_avg"];
40816         eig = eig(["$C"]);
40817         for (k = 3, k<12, k+=3, eig[k]<0?copy(eig[k],eig[k,3]*=-1,3));
40818         Pt = eig[3,9];
40819         P = transpose(Pt,3);
40820         T = mul(P,diag(10^[$1,$5,$9]),3);
40821
40822         R1 = rot(eig[3,3],$3°);
40823         R2 = rot(eig[6,3],$7°);
40824         R3 = rot(eig[9,3],$11°);
40825         T = mul(R1,mul(R2,mul(R3,T,3),3),3);
40826         avg_shift = avg + $2*eig[3,3] + $6*eig[6,3] + $10*eig[9,3];
40827
40828         if ("0$_is_preview",
40829           L = [ 2,5,10]*sqrt(1e-5 + eig[0,3]);
40830           run('__cols=',vtos(round([
40831             avg - L[0]*eig[3,3],
40832             avg + L[0]*eig[3,3],
40833             avg - L[1]*eig[6,3],
40834             avg + L[1]*eig[6,3],
40835             avg - L[2]*eig[9,3],
40836             avg + L[2]*eig[9,3] ])));
40837         );
40838       );
40839       nI = Pt*(I - avg);
40840       ($4 || $8 || $12)?(
40841         nI[0] = do_gamma(nI[0],vmax0,gamma0);
40842         nI[1] = do_gamma(nI[1],vmax1,gamma1);
40843         nI[2] = do_gamma(nI[2],vmax2,gamma2);
40844       );
40845       avg_shift + T*nI"
40846    c 0,255
40847  endl a c endl done u $__status
40848
40849fx_mix_pca_preview :
40850  _is_preview=1
40851  __status=
40852  repeat $! l[$>]
40853    gui_split_preview "fx_mix_pca ${1-13},\"$14\",\"$15\",${16-18}",${-3--1}
40854    if $13
40855      rr2d ${-gui_preview_wh},0,1
40856      ($__cols) r. 3,6,1,1,-1 permute. yzcx s. x,3
40857      r[-3--1] {w#0/2},13,1,3,3 c[-3--1] 0,255
40858      frame[-3--1] 1,1,0
40859      to[0] Primary,4,2,13,1 j[0] ...,64,4
40860      to[0] Secondary,4,17,13,1 j[0] ..,64,19
40861      to[0] Tertiary,4,32,13,1 j[0] .,64,34
40862      k[0]
40863    fi
40864  endl done
40865  u $__status
40866
40867#@gui Mixer [RGB] : fx_mix_rgb, fx_mix_rgb_preview(1)+
40868#@gui : Red Factor = float(1,0,4)
40869#@gui : Red Shift = float(0,-255,255)
40870#@gui : Red Smoothness = float(0,0,10)
40871#@gui : sep = separator()
40872#@gui : Green Factor = float(1,0,4)
40873#@gui : Green Shift = float(0,-255,255)
40874#@gui : Green Smoothness = float(0,0,10)
40875#@gui : sep = separator()
40876#@gui : Blue Factor = float(1,0,4)
40877#@gui : Blue Shift = float(0,-255,255)
40878#@gui : Blue Smoothness = float(0,0,10)
40879#@gui : sep = separator()
40880#@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights")
40881#@gui : Tones Smoothness = float(2,0,10)
40882#@gui : sep = separator()
40883#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40884#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40885#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40886#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40887#@gui : sep = separator()
40888#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
40889fx_start_mix :
40890  if $1==1 +tones. 3 +[-2,-1] b[-2,-1] $2% ri[-2,-1] ... *. ... mv... $!
40891  elif $1==2 +tones. 3 +[-3,-1] b[-2,-1] $2% ri[-2,-1] ... *.. ... mv... $!
40892  elif $1==3 +tones. 3 +[-3,-2] b[-2,-1] $2% ri[-2,-1] ... *.. ... mv... $!
40893  fi
40894
40895fx_end_mix :
40896  if $1==1 *[-3,-1] +[-2,-1]
40897  elif $1==2 *[-2,-1] +[-2,-1]
40898  elif $1==3 *[-2,-1] +[-2,-1]
40899  fi
40900  c 0,255
40901
40902fx_mix_rgb :
40903  repeat $! l. split_opacity rv to_rgb.
40904    fx_start_mix $10,$11
40905    -. 128 s. c
40906    *... $1 +... $2 b... $3%
40907    *.. $4 +.. $5 b.. $6%
40908    *. $7 +. $8 b. $9%
40909    a[-3--1] c +. 128 c. 0,255
40910    fx_end_mix $10
40911  if $!!=3 rv a c fi endl mv. 0 done
40912
40913fx_mix_rgb_preview :
40914  gui_split_preview "fx_mix_rgb $*",${-3--1}
40915
40916#@gui Mixer [YCbCr] : fx_mix_ycbcr, fx_mix_ycbcr_preview(1)+
40917#@gui : Luminance Factor = float(1,0,4)
40918#@gui : Luminance Shift = float(0,-255,255)
40919#@gui : Luminance Smoothness = float(0,0,10)
40920#@gui : sep = separator()
40921#@gui : Blue Chroma Factor = float(1,0,4)
40922#@gui : Blue Chroma Shift = float(0,-255,255)
40923#@gui : Blue Chroma Smoothness = float(0,0,10)
40924#@gui : sep = separator()
40925#@gui : Red Chroma Factor = float(1,0,4)
40926#@gui : Red Chroma Shift = float(0,-255,255)
40927#@gui : Red Chroma Smoothness = float(0,0,10)
40928#@gui : sep = separator()
40929#@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights")
40930#@gui : Tones Smoothness = float(2,0,10)
40931#@gui : sep = separator()
40932#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
40933#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
40934#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
40935#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
40936#@gui : sep = separator()
40937#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
40938fx_mix_ycbcr :
40939  repeat $! l. split_opacity rv to_rgb.
40940    fx_start_mix $10,$11
40941    rgb2ycbcr. -. 128 s. c
40942    *... $1 +... $2 b... $3%
40943    *.. $4 +.. $5 b.. $6%
40944    *. $7 +. $8 b. $9%
40945    a[-3--1] c +. 128 c. 0,255 ycbcr2rgb.
40946    fx_end_mix $10
40947  if $!!=3 rv a c fi endl mv. 0 done
40948
40949fx_mix_ycbcr_preview :
40950  gui_split_preview "fx_mix_ycbcr $*",${-3--1}
40951
40952#@gui CLUT from After - Before Layers : fx_clut_from_ab, fx_clut_from_ab_preview
40953#@gui : Output Mode = choice("Replace Layer with CLUT","Insert New CLUT Layer","Save CLUT as .cube or .png File")
40954#@gui : Output CLUT Resolution = choice{4,16,25,36,49,64,81,100,121,144,169,225,256}_2
40955#@gui : sep = separator()
40956#@gui : Output Folder = _folder()_1-
40957#@gui : Output Filename = _text("output.cube")_1+
40958#@gui : sep = separator()
40959#@gui : Influence of Color Samples (%) = float(50,0,100)_2
40960#@gui : sep = separator()
40961#@gui : note = note{"<b>What is this filter for?</b>\n\n
40962#@gui : This filter requires at least two input layers to work properly.\n
40963#@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
40964#@gui : <b>B</b> both represent the same image but with only color variations
40965#@gui : (typically <b>A</b> has been obtained from <b>B</b> using the color curves tool).\n\n
40966#@gui : This filter is then able to estimate and outputs a color HaldCLUT <b>H</b> so that applying <b>H</b>
40967#@gui : on the base layer <b>B</b> gives back <b>A</b>.\n\n
40968#@gui : This is useful when you have a color transformation between two images, that you want to recover and
40969#@gui : re-apply on a bunch of other images.
40970#@gui : "}
40971#@gui : sep = separator()
40972#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      
40973#@gui : Latest Update: <i>2019/08/27</i>.</small>")
40974fx_clut_from_ab : skip "${3=},${4=}"
40975  if $!<2 error "At least two input layers are needed to run this filter." fi
40976  repeat $!-1 l[$<,-1] nm=${gui_layer_name..}
40977    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"
40978    l[0]
40979      s c,-3 +max. 1 /[-3,-1] ==. 0 inpaint_pde.. .,75%,1 distance. 0 *. {-1/(1+$5)} exp.
40980      f.. "f = i(#-1); f*I + (1-f)*[x,y,z]*255/(w-1)" rm.
40981      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
40982      c 0,255
40983    endl
40984
40985    if $1==2 # Output as a file
40986      is_png={str=lowercase(['"$4"']);find(str,'.png')==size(str)-4}
40987      is_cube={str=lowercase(['"$4"']);find(str,'.cube')==size(str)-5}
40988      if !$is_png" && "!$is_cube $!is_ciube error "Filename extension must be '.cube' or '.png'." fi
40989      if $is_png r[0] {0,r=round(whd^0.5);[r,r]},1,3,-1 o[0] "$3/$4"
40990      else
40991        if {0,w>32} r3dx[0] 32 fi
40992        output_cube[0] "$3/$4"
40993      fi
40994      rm[0]
40995    else
40996      r[0] {0,r=round(whd^0.5);[r,r]},1,3,-1
40997      if !$1 rm[1] fi # Replace Layer with CLUT
40998      nm[0] "name(CLUT to '"$nm"')"
40999    fi
41000  if 0$_output_mode k[0] fi
41001  endl done
41002
41003fx_clut_from_ab_preview : skip "${3=},${4=}"
41004  if $!<2 gui_warning_preview "At least two input layers are needed to run this filter." return fi
41005  _is_preview,_output_mode=1 fx_clut_from_ab 0,4,0,0,$5
41006  repeat $! l[$>]
41007    r {a=round(cbrt(wh));[a,a,a]},3,-1
41008    +r3dx. 24 _fx_clut_from_ab_preview.
41009    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
41010    r[0] {0,r=round(whd^0.5);[r,r]},1,3,-1
41011    rr2d[0] $_preview_width,$_preview_height,2,1 r2dx. 70%
41012    sh. 100% b. 1% n. 0,255 rm.
41013    blend alpha
41014  endl done
41015
41016  file_attr={$1==2?2:1}
41017  u "{$1}{$2}{$3}_"$file_attr"{$4}_"$file_attr"{$5}"
41018
41019# Render 3D visualization of a CLUT.
41020_fx_clut_from_ab_preview :
41021  repeat $! l[$>]
41022    fact={256/w}
41023
41024    # Color data, as a point cloud.
41025    pointcloud3d +3d 0.5,0.5,0.5 *3d $fact circles3d {1.25*$fact} o3d {0.004*$fact}
41026
41027    # Add cube edges.
41028    colorcube3d[]
41029    l. s3d l.. s y,6 repeat $! sh[$>] 4,100%,0,0 /. 1.25 rm. done endl a y endl # Darken colors of edges
41030    p3d. 1
41031    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
41032
41033    # Add 3D axes, without the 'O'.
41034    axes3d 64,64,64,24,R,G,B,0
41035    +3d
41036
41037    # Render 3D snapshot.
41038    pose3d 1.2451,0.120715,-0.893986,-58.3864,-0.572953,1.28275,-0.62477,-11.6557,\
41039           0.696784,0.839071,1.08374,-333.008,0,0,217,1
41040    snapshot3d {max(512,0$_preview_width,0$_preview_height)},1.22,255,255,255
41041    autocrop frame 10,10,255
41042    to_rgba flood 0,0,0,20,1,1,255,255,255,0
41043  endl done
41044
41045#@gui Retinex : fx_retinex, fx_retinex_preview(0)+
41046#@gui : Strength (%) = float(75,0,100)
41047#@gui : Value Offset = float(16,1,256)
41048#@gui : Colorspace = choice(1,"HSI","HSV","Lab","Linear RGB","RGB","YCbCr")
41049#@gui : Min Cut (%) = float(1,0,100)
41050#@gui : Max Cut (%) = float(1,0,100)
41051#@gui : Regularization = float(5,0,32)
41052#@gui : sep = separator()
41053#@gui : Low Scale = float(15,1,512)
41054#@gui : Middle Scale = float(80,1,512)
41055#@gui : High Scale = float(250,1,512)
41056#@gui : sep = separator()
41057#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41058#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41059#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41060#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41061#@gui : sep = separator()
41062#@gui : note = note("<small><b>Note:</b> This filter implements the <i>Multiscale Color Retinex</i> algorithm,
41063#@gui : as described in:</small>")
41064#@gui : url = link{"http://www.ipol.im/pub/art/2014/107/"}
41065#@gui : sep = separator()
41066#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/13/09</i>.</small>")
41067fx_retinex :
41068  repeat $! l[$>]
41069    +retinex $2,${"arg 1+$3,hsi,hsv,lab,lrgb,rgb,ycbcr"},$4,$5,${7--1}
41070    if $6 guided. ..,$6,$6 fi
41071    j[0] .,0,0,0,0,{$1%} rm.
41072    c 0,255
41073  endl done
41074
41075fx_retinex_preview :
41076  gui_split_preview "fx_retinex $*",${-3--1}
41077
41078#@gui Retro Fade : fx_retrofade, fx_retrofade_preview
41079#@gui : Iterations = int(20,1,64)
41080#@gui : Colors = int(6,2,32)
41081#@gui : Grain = float(40,1,100)
41082#@gui : sep = separator()
41083#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41084#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41085#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41086#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41087#@gui : sep = separator()
41088#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/25/10</i>.</small>")
41089fx_retrofade :
41090  repeat $! l[$>] split_opacity l[0]
41091    +f 0
41092    repeat $1
41093      +noise[0] $3 c. 0,255 autoindex. $2,0,0
41094      +[-2,-1]
41095      progress {$>*100/$1}
41096    done
41097    k. n 0,255
41098    progress 100
41099  endl a c endl done
41100
41101fx_retrofade_preview :
41102  gui_split_preview "fx_retrofade $*",${-3--1}
41103
41104#@gui Select-Replace Color : fx_select_color, fx_select_color_preview(0)
41105#@gui : Similarity Space = choice(0,"RGB[A]","RGB","YCbCr","Red","Green","Blue","Opacity","Luminance",
41106#@gui : "Blue & Red Chrominances","Hue","Saturation")
41107#@gui : Tolerance = float(20,0,100)
41108#@gui : Smoothness = float(0,0,10)
41109#@gui : Fill Holes = int(0,0,256)
41110#@gui : Selected Color = color(255,255,255,255)
41111#@gui : Output As = choice(0,"Selected Colors","Selected Mask","Rejected Colors","Rejected Mask","Replaced Color")
41112#@gui : Replacement Color = color(255,0,0,255)
41113#@gui : sep = separator()
41114#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41115#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41116#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41117#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41118#@gui : sep = separator()
41119#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41120_fx_select_color :
41121  if $1==1 to_rgb                          # RGB
41122  elif $1==2 to_rgb rgb2ycbcr              # YCbCr
41123  elif $1==3 channels 0                    # R
41124  elif $1==4 channels 1                    # G
41125  elif $1==5 channels 2                    # B
41126  elif $1==6 to_rgba channels 3            # Opacity
41127  elif $1==7 to_rgb rgb2ycbcr channels 0   # Luminance
41128  elif $1==8 to_rgb rgb2ycbcr channels 1,2 # B&R chrominances
41129  elif $1==9 to_rgb rgb2hsv channels 0     # Hue
41130  elif $1==10 to_rgb rgb2hsv channels 1    # Saturation
41131  fi
41132
41133fx_select_color :
41134  ($5^$6^$7^$8) _fx_select_color. $1 color={^} rm.
41135  repeat $! l[$>] to_rgba
41136    +_fx_select_color $1
41137    select_color[1] $2%,$color
41138    if $4 +area. 0,0 <=. {round($4^1.5)} inpaint.. .,0,3 rm. fi # Fill holes.
41139    b[1] $3 n[1] 0,255
41140    if $9==0 sh[0] 100% &. [1]                      # Selected colors.
41141    elif $9==1 rm[0]                                # Selected mask.
41142    elif $9==2 -[1] 255 *[1] -1 sh[0] 100% &. [1]   # Rejected colors.
41143    elif $9==3 rm[0] - 255 * -1                     # Rejected mask.
41144    else # Replaced color.
41145      /[1] 255 +*[0,1] +*[1] $11 +*[1] $12 +*[1] $13 *[1] $10 a[1,-3--1] c -[1,2] +
41146    fi
41147    k[0]
41148  endl done
41149
41150fx_select_color_preview :
41151  gui_split_preview "fx_select_color $*",${-3--1}
41152
41153#@gui Selective Desaturation : fx_selective_desaturation, fx_selective_desaturation_preview(1)
41154#@gui : Reference Color = color(255,255,255)
41155#@gui : Desaturate = choice("Reference Color","All but Reference Color")
41156#@gui : Strength = float(3,0,10)
41157#@gui : Regularization = int(0,0,20)
41158#@gui : Maximum Saturation = choice("From Input","From Reference Color","Maximum Value")
41159#@gui : sep = separator()
41160#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41161#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41162#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41163#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41164#@gui : sep = separator()
41165#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/15/07</i>.</small>")
41166fx_selective_desaturation :
41167  repeat $! l[$>] to_color split_opacity l[0]
41168    +fc $1,$2,$3
41169    -[1] [0] norm[1] /[1] {1e-6+iM}
41170    if $4 *[1] -{max(0.01,$5)} +[1] 1
41171    else >=[1] {5*$5}%
41172    fi
41173    c[1] 0,1
41174    rgb2hsl[0] s[0] c
41175    mM={[im,iM]} repeat $6 guided. [2],1,0.1 done n. $mM # Regularization step.
41176    if $7==0 *[1,-1]
41177    elif $7==1 ($1^$2^$3) rgb2hsl. *[1] {i[1]} rm[-2,-1]
41178    else rv[1,-1] rm.
41179    fi
41180    a c hsl2rgb
41181  endl a c endl done
41182
41183fx_selective_desaturation_preview :
41184  gui_split_preview "fx_selective_desaturation $*",${-3--1}
41185
41186#@gui Sepia : fx_sepia, fx_sepia_preview(1)+
41187#@gui : Brightness (%) = float(0,-100,100)
41188#@gui : Contrast (%) = float(0,-100,100)
41189#@gui : Gamma (%) = float(0,-100,100)
41190#@gui : sep = separator()
41191#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41192#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41193#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41194#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41195#@gui : sep = separator()
41196#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41197fx_sepia :
41198  sepia adjust_colors ${1-3},0,0,0,255
41199
41200fx_sepia_preview :
41201  gui_split_preview "fx_sepia $*",${-3--1}
41202
41203#@gui Simulate Film : fx_simulate_film, fx_simulate_film_preview(1)+
41204#@gui : Category = choice{"Black & White (25)","Instant [Consumer] (54)","Instant [Pro] (68)","Fuji XTrans III (15)",
41205#@gui : "Negative [Color] (13)","Negative [New] (39)","Negative [Old] (44)","Print Films (12)","Slide [Color] (26)"}
41206
41207##### Black & White
41208#@gui : Preset = choice{1,"All [Collage]","None",
41209#@gui : "Agfa APX 100","Agfa APX 25","Fuji Neopan 1600","Fuji Neopan Acros 100","Ilford Delta 100",
41210#@gui : "Ilford Delta 3200","Ilford Delta 400","Ilford FP4 Plus 125","Ilford HP5 Plus 400","Ilford HPS 800",
41211#@gui : "Ilford Pan F Plus 50","Ilford XP2","Kodak BW 400 CN","Kodak HIE (HS Infra)","Kodak T-Max 100",
41212#@gui : "Kodak T-Max 3200","Kodak T-Max 400","Kodak Tri-X 400","Polaroid 664","Polaroid 667","Polaroid 672",
41213#@gui : "Rollei IR 400","Rollei Ortho 25","Rollei Retro 100 Tonal","Rollei Retro 80s"}_2
41214
41215##### Instant [Consumer]
41216#@gui : Preset = choice{1,"All [Collage]","None",
41217#@gui : "Polaroid PX-100UV+ Cold --","Polaroid PX-100UV+ Cold -","Polaroid PX-100UV+ Cold",
41218#@gui : "Polaroid PX-100UV+ Cold +","Polaroid PX-100UV+ Cold ++","Polaroid PX-100UV+ Cold +++",
41219#@gui : "Polaroid PX-100UV+ Warm --","Polaroid PX-100UV+ Warm -","Polaroid PX-100UV+ Warm",
41220#@gui : "Polaroid PX-100UV+ Warm +","Polaroid PX-100UV+ Warm ++","Polaroid PX-100UV+ Warm +++",
41221#@gui : "Polaroid PX-680 --","Polaroid PX-680 -","Polaroid PX-680","Polaroid PX-680 +","Polaroid PX-680 ++",
41222#@gui : "Polaroid PX-680 Cold --","Polaroid PX-680 Cold -","Polaroid PX-680 Cold","Polaroid PX-680 Cold +",
41223#@gui : "Polaroid PX-680 Cold ++","Polaroid PX-680 Cold ++a","Polaroid PX-680 Warm --","Polaroid PX-680 Warm -",
41224#@gui : "Polaroid PX-680 Warm","Polaroid PX-680 Warm +","Polaroid PX-680 Warm ++","Polaroid PX-70 --",
41225#@gui : "Polaroid PX-70 -","Polaroid PX-70","Polaroid PX-70 +","Polaroid PX-70 ++","Polaroid PX-70 +++",
41226#@gui : "Polaroid PX-70 Cold --","Polaroid PX-70 Cold -","Polaroid PX-70 Cold","Polaroid PX-70 Cold +",
41227#@gui : "Polaroid PX-70 Cold ++","Polaroid PX-70 Warm --","Polaroid PX-70 Warm -","Polaroid PX-70 Warm",
41228#@gui : "Polaroid PX-70 Warm +","Polaroid PX-70 Warm ++","Polaroid Time Zero (Expired) ---",
41229#@gui : "Polaroid Time Zero (Expired) --","Polaroid Time Zero (Expired) -","Polaroid Time Zero (Expired)",
41230#@gui : "Polaroid Time Zero (Expired) +","Polaroid Time Zero (Expired) ++","Polaroid Time Zero (Expired) Cold ---",
41231#@gui : "Polaroid Time Zero (Expired) Cold --","Polaroid Time Zero (Expired) Cold -",
41232#@gui : "Polaroid Time Zero (Expired) Cold"}_0
41233
41234##### Instant [Pro]
41235#@gui : Preset = choice{1,"All [Collage]","None",
41236#@gui : "Fuji FP-100c --","Fuji FP-100c -","Fuji FP-100c","Fuji FP-100c (alt)","Fuji FP-100c +","Fuji FP-100c ++",
41237#@gui : "Fuji FP-100c ++a","Fuji FP-100c +++",
41238#@gui : "Fuji FP-100c Cool --","Fuji FP-100c Cool -","Fuji FP-100c Cool","Fuji FP-100c Cool +","Fuji FP-100c Cool ++",
41239#@gui : "Fuji FP-100c Negative --","Fuji FP-100c Negative -","Fuji FP-100c Negative","Fuji FP-100c Negative +",
41240#@gui : "Fuji FP-100c Negative ++","Fuji FP-100c Negative ++a","Fuji FP-100c Negative +++",
41241#@gui : "Fuji FP-3000b --","Fuji FP-3000b -","Fuji FP-3000b","Fuji FP-3000b +","Fuji FP-3000b ++","Fuji FP-3000b +++",
41242#@gui : "Fuji FP-3000b HC","Fuji FP-3000b Negative --","Fuji FP-3000b Negative -","Fuji FP-3000b Negative",
41243#@gui : "Fuji FP-3000b Negative +","Fuji FP-3000b Negative ++","Fuji FP-3000b Negative +++",
41244#@gui : "Fuji FP-3000b Negative Early","Polaroid 665 --","Polaroid 665 -","Polaroid 665","Polaroid 665 +",
41245#@gui : "Polaroid 665 ++","Polaroid 665 Negative -","Polaroid 665 Negative","Polaroid 665 Negative +",
41246#@gui : "Polaroid 665 Negative HC","Polaroid 669 --","Polaroid 669 -","Polaroid 669","Polaroid 669 +",
41247#@gui : "Polaroid 669 ++","Polaroid 669 +++","Polaroid 669 Cold --","Polaroid 669 Cold -","Polaroid 669 Cold",
41248#@gui : "Polaroid 669 Cold +","Polaroid 690 --","Polaroid 690 -","Polaroid 690","Polaroid 690 +","Polaroid 690 ++",
41249#@gui : "Polaroid 690 Cold --","Polaroid 690 Cold -","Polaroid 690 Cold","Polaroid 690 Cold +","Polaroid 690 Cold ++",
41250#@gui : "Polaroid 690 Warm --","Polaroid 690 Warm -","Polaroid 690 Warm","Polaroid 690 Warm +","Polaroid 690 Warm ++"}_0
41251
41252#### Fuji XTrans III
41253#@gui : Preset = choice{1,"All [Collage]","None",
41254#@gui : "Acros","Acros+G","Acros+R","Acros+Ye","Astia","Classic Chrome","Mono","Mono+G","Mono+R","Mono+Ye",
41255#@gui : "Pro Neg Hi","Pro Neg Std","Provia","Sepia","Velvia"}_0
41256
41257##### Negative [Color]
41258#@gui : Preset = choice{1,"All [Collage]","None",
41259#@gui : "Agfa Ultra Color 100","Agfa Vista 200","Fuji Superia 200","Fuji Superia HG 1600","Fuji Superia Reala 100",
41260#@gui : "Fuji Superia X-Tra 800","Kodak Ektar 100","Kodak Elite 100 XPRO","Kodak Elite Color 200",
41261#@gui : "Kodak Elite Color 400","Kodak Portra 160 NC","Kodak Portra 160 VC","Lomography Redscale 100"}_0
41262
41263##### Negative [New]
41264#@gui : Preset = choice{1,"All [Collage]","None",
41265#@gui : "Fuji 160C -","Fuji 160C","Fuji 160C +","Fuji 160C ++",
41266#@gui : "Fuji 400H -","Fuji 400H","Fuji 400H +","Fuji 400H ++",
41267#@gui : "Fuji 800Z -","Fuji 800Z","Fuji 800Z +","Fuji 800Z ++",
41268#@gui : "Fuji Ilford HP5 -","Fuji Ilford HP5","Fuji Ilford HP5 +","Fuji Ilford HP5 ++",
41269#@gui : "Kodak Portra 160 -","Kodak Portra 160","Kodak Portra 160 +","Kodak Portra 160 ++",
41270#@gui : "Kodak Portra 400 -","Kodak Portra 400","Kodak Portra 400 +","Kodak Portra 400 ++",
41271#@gui : "Kodak Portra 800 -","Kodak Portra 800","Kodak Portra 800 +","Kodak Portra 800 ++","Kodak Portra 800 HC",
41272#@gui : "Kodak T-MAX 3200 -","Kodak T-MAX 3200","Kodak T-MAX 3200 +","Kodak T-MAX 3200 ++","Kodak T-MAX 3200 (alt)",
41273#@gui : "Kodak TRI-X 400 -","Kodak TRI-X 400","Kodak TRI-X 400 +","Kodak TRI-X 400 ++","Kodak TRI-X 400 (alt)"}_0
41274
41275##### Negative [Old]
41276#@gui : Preset = choice{1,"All [Collage]","None",
41277#@gui : "Fuji Ilford Delta 3200 -","Fuji Ilford Delta 3200","Fuji Ilford Delta 3200 +","Fuji Ilford Delta 3200 ++",
41278#@gui : "Fuji Neopan 1600 -","Fuji Neopan 1600","Fuji Neopan 1600 +","Fuji Neopan 1600 ++",
41279#@gui : "Fuji Superia 100 -","Fuji Superia 100","Fuji Superia 100 +","Fuji Superia 100 ++",
41280#@gui : "Fuji Superia 400 -","Fuji Superia 400","Fuji Superia 400 +","Fuji Superia 400 ++",
41281#@gui : "Fuji Superia 800 -","Fuji Superia 800","Fuji Superia 800 +","Fuji Superia 800 ++",
41282#@gui : "Fuji Superia 1600 -","Fuji Superia 1600","Fuji Superia 1600 +","Fuji Superia 1600 ++",
41283#@gui : "Kodak Portra 160 NC -","Kodak Portra 160 NC","Kodak Portra 160 NC +","Kodak Portra 160 NC ++",
41284#@gui : "Kodak Portra 160 VC -","Kodak Portra 160 VC","Kodak Portra 160 VC +","Kodak Portra 160 VC ++",
41285#@gui : "Kodak Portra 400 NC -","Kodak Portra 400 NC","Kodak Portra 400 NC +","Kodak Portra 400 NC ++",
41286#@gui : "Kodak Portra 400 UC -","Kodak Portra 400 UC","Kodak Portra 400 UC +","Kodak Portra 400 UC ++",
41287#@gui : "Kodak Portra 400 VC -","Kodak Portra 400 VC","Kodak Portra 400 VC +","Kodak Portra 400 VC ++"}_0
41288
41289##### Print Films
41290#@gui : Preset = choice{1,"All [Collage]","None",
41291#@gui : "Fuji 3510 (Constlclip)","Fuji 3510 (Constlmap)","Fuji 3510 (Cuspclip)",
41292#@gui : "Fuji 3513 (Constlclip)","Fuji 3513 (Constlmap)","Fuji 3513 (Cuspclip)",
41293#@gui : "Kodak 2383 (Constlclip)","Kodak 2383 (Constlmap)","Kodak 2383 (Cuspclip)",
41294#@gui : "Kodak 2393 (Constlclip)","Kodak 2393 (Constlmap)","Kodak 2393 (Cuspclip)"}_0
41295
41296#### Slide [Color]
41297#@gui : Preset = choice{1,"All [Collage]","None",
41298#@gui : "Agfa Precisa 100","Fuji Astia 100F","Fuji FP 100C","Fuji Provia 100F","Fuji Provia 400F","Fuji Provia 400X",
41299#@gui : "Fuji Sensia 100","Fuji Superia 200 XPRO","Fuji Velvia 50","Generic Fuji Astia 100","Generic Fuji Provia 100",
41300#@gui : "Generic Fuji Velvia 100","Generic Kodachrome 64","Generic Kodak Ektachrome 100 VS",
41301#@gui : "Kodak E-100 GX Ektachrome 100","Kodak Ektachrome 100 VS","Kodak Elite Chrome 200","Kodak Elite Chrome 400",
41302#@gui : "Kodak Elite ExtraColor 100","Kodak Kodachrome 200","Kodak Kodachrome 25","Kodak Kodachrome 64",
41303#@gui : "Lomography X-Pro Slide 200",
41304#@gui : "Polaroid 669","Polaroid 690","Polaroid Polachrome"}_0
41305
41306#@gui : Thumbnail Size = int(512,0,1024)_1
41307#@gui : sep = separator()
41308#@gui : Strength (%) = float(100,0,100)
41309#@gui : Brightness (%) = float(0,-100,100)
41310#@gui : Contrast (%) = float(0,-100,100)
41311#@gui : Gamma (%) = float(0,-100,100)
41312#@gui : Hue (%) = float(0,-100,100)
41313#@gui : Saturation (%) = float(0,-100,100)
41314#@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both")
41315#@gui : sep = separator()
41316#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41317#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41318#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41319#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41320#@gui : sep = separator()
41321#@gui : note = note("<small><b>Note:</b> The color LUTs proposed in this filter come from
41322#@gui : various free sources :</small>")
41323#@gui : note = note{"<small><b>*</b>
41324#@gui : <a href="https://rawpedia.rawtherapee.com/Film_Simulation">RawTherapee Film Simulation</a>.</small>"}
41325#@gui : note = note{"<small><b>*</b>
41326#@gui : <a href="https://patdavid.net/2013/08/film-emulation-presets-in-gmic-gimp.html">Pat David Film Emulation</a>.
41327#@gui : </small>"}
41328#@gui : note = note{"<small><b>*</b>
41329#@gui : <a href="http://blog.sowerby.me/fuji-film-simulation-profiles">Fuji Film Simulation Profiles</a>.</small>"}
41330#@gui : note = note{"<small><b>*</b>
41331#@gui : <a href="http://juanmelara.com.au/print-film-emulation-luts-for-download/">Print Film LUTs For Download</a>.
41332#@gui : </small>"}
41333#@gui : sep = separator()
41334#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/02/27</i>.</small>")
41335fx_simulate_film :
41336  category=${arg\ 1+$1,bw,instant_consumer,instant_pro,fujixtransiii,negative_color,negative_new,negative_old,\
41337           print,colorslide}
41338  presets=${-_fx_cluts_$category}
41339  index={arg(1+$1,${2-10})}
41340  thumbsize,strength,brightness,contrast,gamma,hue,saturation,normalize=${11-18}
41341  if $normalize==1" || "$normalize==3 # Pre-normalization
41342    repeat $! l[$>] split_opacity balance_gamma[0] , a c endl done
41343  fi
41344  if $index>=2 # Apply CLUT
41345    path_clut=${-path_cache}
41346    name=${arg\ 1+$index-2,$presets}
41347    clut $name,{0$_is_preview" && "!isfile(['{/${path_clut}clut_$name.cimgz}'])?17:48}
41348    repeat $!-1 if $strength<100 +map_clut[$>] . j[$>] .,0,0,0,0,{$strength%} rm. else map_clut[$>] . fi done
41349    rm.
41350    adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
41351    if $normalize==2" || "$normalize==3 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi # Post-normalization
41352
41353  elif $index==1 # "None"
41354    adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255
41355    if $normalize==2" || "$normalize==3 repeat $! l[$>] split_opacity n[0] 0,255 a c endl done fi # Post-normalization
41356
41357  else # Collage
41358    repeat $! l[$>] if max(w,h)>$thumbsize rr2d $thumbsize,$thumbsize,0,2 fi endl done
41359    N=$! +to "Original",1%,1%,7.5%,2,0.5
41360    Np={narg($presets)} repeat $Np
41361      clut_name=${arg\ 1+$>,$presets}
41362      clut $clut_name mv. $N
41363      repeat $N
41364        if $strength<100 [$>] +map_clut. [$N] j.. .,0,0,0,0,{$strength%} rm. else +map_clut[$>] [$N] fi
41365        adjust_colors. $brightness,$contrast,$gamma,$hue,$saturation,0,255
41366        if $normalize==2" || "$normalize==3 l. split_opacity n[0] 0,255 a c endl fi # Post-normalization
41367        strcapitalize $clut_name clut_name=${}
41368        to. $clut_name,1%,1%,7.5%,2,0.5
41369      done
41370      rm[$N]
41371      progress {$>*100/($Np-1)}
41372    done
41373    k[$N--1] frame 1,1,0,0,0,255 - 128 append_tiles {s=floor(sqrt($!));w>h?[s,0]:[0,s]} + 128
41374  fi
41375
41376fx_simulate_film_preview :
41377  _is_preview=1
41378  index={arg(1+$1,${2-10})}
41379  if !$index gui_warning_preview "Preview disabled in 'Collage' mode"
41380  else gui_split_preview "fx_simulate_film $*",${19-21}
41381  fi
41382  u "{$1}{$2}_"{2*($1==0)}\
41383        "{$3}_"{2*($1==1)}\
41384        "{$4}_"{2*($1==2)}\
41385        "{$5}_"{2*($1==3)}\
41386        "{$6}_"{2*($1==4)}\
41387        "{$7}_"{2*($1==5)}\
41388        "{$8}_"{2*($1==6)}\
41389        "{$9}_"{2*($1==7)}\
41390        "{$10}_"{2*($1==8)}\
41391        "{$11}_"{1+!$index}\
41392        "{$12}{$13}{$14}{$15}{$16}{$17}{$18}{$19}{$20,$21}"
41393
41394_fx_cluts_bw :
41395  u agfa_apx_100,agfa_apx_25,fuji_neopan_1600,fuji_neopan_acros_100,ilford_delta_100,ilford_delta_3200,\
41396    ilford_delta_400,ilford_fp_4_plus_125,\
41397    ilford_hp_5_plus_400,ilford_hps_800,ilford_pan_f_plus_50,ilford_xp_2,kodak_bw_400_cn,kodak_hie_hs_infra,\
41398    kodak_t-max_100,kodak_t-max_3200,\
41399    kodak_t-max_400,kodak_tri-x_400,polaroid_664,polaroid_667,polaroid_672,rollei_ir_400,rollei_ortho_25,\
41400    rollei_retro_100_tonal,rollei_retro_80s
41401
41402_fx_cluts_instant_consumer :
41403  u polaroid_px-100uv+_cold_--,polaroid_px-100uv+_cold_-,polaroid_px-100uv+_cold,polaroid_px-100uv+_cold_+,\
41404    polaroid_px-100uv+_cold_++,polaroid_px-100uv+_cold_+++,\
41405    polaroid_px-100uv+_warm_--,polaroid_px-100uv+_warm_-,polaroid_px-100uv+_warm,polaroid_px-100uv+_warm_+,\
41406    polaroid_px-100uv+_warm_++,polaroid_px-100uv+_warm_+++,\
41407    polaroid_px-680_--,polaroid_px-680_-,polaroid_px-680,polaroid_px-680_+,polaroid_px-680_++,\
41408    polaroid_px-680_cold_--,polaroid_px-680_cold_-,polaroid_px-680_cold,polaroid_px-680_cold_+,\
41409    polaroid_px-680_cold_++,polaroid_px-680_cold_++_alt,\
41410    polaroid_px-680_warm_--,polaroid_px-680_warm_-,polaroid_px-680_warm,polaroid_px-680_warm_+,polaroid_px-680_warm_++,\
41411    polaroid_px-70_--,polaroid_px-70_-,polaroid_px-70,polaroid_px-70_+,polaroid_px-70_++,polaroid_px-70_+++,\
41412    polaroid_px-70_cold_--,polaroid_px-70_cold_-,polaroid_px-70_cold,polaroid_px-70_cold_+,polaroid_px-70_cold_++,\
41413    polaroid_px-70_warm_--,polaroid_px-70_warm_-,polaroid_px-70_warm,polaroid_px-70_warm_+,polaroid_px-70_warm_++,\
41414    polaroid_time_zero_expired_---,polaroid_time_zero_expired_--,polaroid_time_zero_expired_-,\
41415    polaroid_time_zero_expired,polaroid_time_zero_expired_+,polaroid_time_zero_expired_++,\
41416    polaroid_time_zero_expired_cold_---,polaroid_time_zero_expired_cold_--,polaroid_time_zero_expired_cold_-,\
41417    polaroid_time_zero_expired_cold
41418
41419_fx_cluts_instant_pro :
41420  u fuji_fp-100c_--,fuji_fp-100c_-,fuji_fp-100c,fuji_fp-100c_alt,fuji_fp-100c_+,fuji_fp-100c_++,fuji_fp-100c_++_alt,\
41421    fuji_fp-100c_+++,\
41422    fuji_fp-100c_cool_--,fuji_fp-100c_cool_-,fuji_fp-100c_cool,fuji_fp-100c_cool_+,fuji_fp-100c_cool_++,\
41423    fuji_fp-100c_negative_--,fuji_fp-100c_negative_-,fuji_fp-100c_negative,fuji_fp-100c_negative_+,\
41424    fuji_fp-100c_negative_++,fuji_fp-100c_negative_++_alt,fuji_fp-100c_negative_+++,\
41425    fuji_fp-3000b_--,fuji_fp-3000b_-,fuji_fp-3000b,fuji_fp-3000b_+,fuji_fp-3000b_++,fuji_fp-3000b_+++,fuji_fp-3000b_hc,\
41426    fuji_fp-3000b_negative_--,fuji_fp-3000b_negative_-,fuji_fp-3000b_negative,fuji_fp-3000b_negative_+,\
41427    fuji_fp-3000b_negative_++,fuji_fp-3000b_negative_+++,fuji_fp-3000b_negative_early,\
41428    polaroid_665_--,polaroid_665_-,polaroid_665,polaroid_665_+,polaroid_665_++,\
41429    polaroid_665_negative_-,polaroid_665_negative,polaroid_665_negative_+,polaroid_665_negative_hc,\
41430    polaroid_669_--,polaroid_669_-,polaroid_669,polaroid_669_+,polaroid_669_++,polaroid_669_+++,\
41431    polaroid_669_cold_--,polaroid_669_cold_-,polaroid_669_cold,polaroid_669_cold_+,\
41432    polaroid_690_--,polaroid_690_-,polaroid_690,polaroid_690_+,polaroid_690_++,\
41433    polaroid_690_cold_--,polaroid_690_cold_-,polaroid_690_cold,polaroid_690_cold_+,polaroid_690_cold_++,\
41434    polaroid_690_warm_--,polaroid_690_warm_-,polaroid_690_warm,polaroid_690_warm_+,polaroid_690_warm_++
41435
41436_fx_cluts_fujixtransiii :
41437  u fuji_xtrans_iii_acros,fuji_xtrans_iii_acros+g,fuji_xtrans_iii_acros+r,fuji_xtrans_iii_acros+ye,\
41438    fuji_xtrans_iii_astia,\
41439    fuji_xtrans_iii_classic_chrome,fuji_xtrans_iii_mono,fuji_xtrans_iii_mono+g,fuji_xtrans_iii_mono+r,\
41440    fuji_xtrans_iii_mono+ye,\
41441    fuji_xtrans_iii_pro_neg_hi,fuji_xtrans_iii_pro_neg_std,fuji_xtrans_iii_provia,fuji_xtrans_iii_sepia,\
41442    fuji_xtrans_iii_velvia
41443
41444_fx_cluts_negative_color :
41445  u agfa_ultra_color_100,agfa_vista_200,fuji_superia_200,fuji_superia_hg_1600,fuji_superia_reala_100,\
41446    fuji_superia_x-tra_800,kodak_ektar_100,\
41447    kodak_elite_100_xpro,kodak_elite_color_200,kodak_elite_color_400,kodak_portra_160_nc,kodak_portra_160_vc,\
41448    lomography_redscale_100
41449
41450_fx_cluts_negative_new :
41451  u fuji_160c_-,fuji_160c,fuji_160c_+,fuji_160c_++,\
41452    fuji_400h_-,fuji_400h,fuji_400h_+,fuji_400h_++,\
41453    fuji_800z_-,fuji_800z,fuji_800z_+,fuji_800z_++,\
41454    ilford_hp_5_-,ilford_hp_5,ilford_hp_5_+,ilford_hp_5_++,\
41455    kodak_portra_160_-,kodak_portra_160,kodak_portra_160_+,kodak_portra_160_++,\
41456    kodak_portra_400_-,kodak_portra_400,kodak_portra_400_+,kodak_portra_400_++,\
41457    kodak_portra_800_-,kodak_portra_800,kodak_portra_800_+,kodak_portra_800_++,kodak_portra_800_hc,\
41458    kodak_tmax_3200_-,kodak_tmax_3200,kodak_tmax_3200_+,kodak_tmax_3200_++,kodak_tmax_3200_alt,\
41459    kodak_tri-x_400_-,kodak_tri-x_400,kodak_tri-x_400_+,kodak_tri-x_400_++,kodak_tri-x_400_alt
41460
41461_fx_cluts_negative_old :
41462  u ilford_delta_3200_-,ilford_delta_3200,ilford_delta_3200_+,ilford_delta_3200_++,\
41463    fuji_neopan_1600_-,fuji_neopan_1600,fuji_neopan_1600_+,fuji_neopan_1600_++,\
41464    fuji_superia_100_-,fuji_superia_100,fuji_superia_100_+,fuji_superia_100_++,\
41465    fuji_superia_400_-,fuji_superia_400,fuji_superia_400_+,fuji_superia_400_++,\
41466    fuji_superia_800_-,fuji_superia_800,fuji_superia_800_+,fuji_superia_800_++,\
41467    fuji_superia_1600_-,fuji_superia_1600,fuji_superia_1600_+,fuji_superia_1600_++,\
41468    kodak_portra_160_nc_-,kodak_portra_160_nc,kodak_portra_160_nc_+,kodak_portra_160_nc_++,\
41469    kodak_portra_160_vc_-,kodak_portra_160_vc,kodak_portra_160_vc_+,kodak_portra_160_vc_++,\
41470    kodak_portra_400_nc_-,kodak_portra_400_nc,kodak_portra_400_nc_+,kodak_portra_400_nc_++,\
41471    kodak_portra_400_uc_-,kodak_portra_400_uc,kodak_portra_400_uc_+,kodak_portra_400_uc_++,\
41472    kodak_portra_400_vc_-,kodak_portra_400_vc,kodak_portra_400_vc_+,kodak_portra_400_vc_++
41473
41474_fx_cluts_print :
41475  u fuji_3510_constlclip,fuji_3510_constlmap,fuji_3510_cuspclip,\
41476    fuji_3513_constlclip,fuji_3513_constlmap,fuji_3513_cuspclip,\
41477    kodak_2383_constlclip,kodak_2383_constlmap,kodak_2383_cuspclip,\
41478    kodak_2393_constlclip,kodak_2393_constlmap,kodak_2393_cuspclip
41479
41480_fx_cluts_colorslide :
41481  u agfa_precisa_100,fuji_astia_100f,fuji_fp_100c,fuji_provia_100f,fuji_provia_400f,fuji_provia_400x,fuji_sensia_100,\
41482    fuji_superia_200_xpro,fuji_velvia_50,fuji_astia_100_generic,fuji_provia_100_generic,fuji_velvia_100_generic,\
41483    kodak_kodachrome_64_generic,kodak_ektachrome_100_vs_generic,kodak_e-100_gx_ektachrome_100,kodak_ektachrome_100_vs,\
41484    kodak_elite_chrome_200,\
41485    kodak_elite_chrome_400,kodak_elite_extracolor_100,kodak_kodachrome_200,kodak_kodachrome_25,kodak_kodachrome_64,\
41486    lomography_x-pro_slide_200,\
41487    polaroid_669,polaroid_690,polaroid_polachrome
41488
41489#@gui Transfer Colors [Variational] : fx_transfer_rgb, fx_transfer_rgb_preview(1)+ : *
41490#@gui : Regularization = int(8,0,32)
41491#@gui : Preserve Luminance = float(0.2,0,1)
41492#@gui : Precision = _choice(1,"Low","Normal","High","Very High")
41493#@gui : Reference Colors = choice("Bottom Layer","Top Layer")
41494#@gui : Add User-Defined Constraints (Interactive) = _bool(0)
41495#@gui : sep = separator()
41496#@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0
41497#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41498#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41499#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41500#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41501#@gui : sep = separator()
41502#@gui : note = note{"<small><b>Instructions:</b>\n
41503#@gui : - This filter transfers the colors of one layer to all the others.\n
41504#@gui : - Don't forget to set the <i>Input layers...</i> option on the left to manage your input layers.\n
41505#@gui : </small>"}
41506#@gui : sep = separator()
41507#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/04/04</i>.</small>")
41508fx_transfer_rgb :
41509  to_rgb
41510  ref={$4?0:-1}
41511  transfer_rgb[^$ref] [$ref],0.25,$1,$2,{2^(4+$3)},$5,0
41512  c 0,255
41513
41514fx_transfer_rgb_preview :
41515  if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi
41516  ref={$4?0:-1}
41517  +store[$ref] _fx_trgb_ref
41518  gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_rgb $1,$2,0,0,0 rm.",${-3--1}
41519  _fx_trgb_ref=
41520
41521  mv[$ref] $!
41522  repeat $!-1 l[$>,-1]
41523    rr2d[0] $_preview_width,$_preview_height,0,3 rr2d. {0,[w,h]/3},0,3
41524    to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0
41525    j[0] .,$6%,$7%
41526    rm.
41527  endl done
41528
41529#@gui Transfer Colors [Histogram] : fx_transfer_histogram, fx_transfer_histogram_preview(1)+ : *
41530#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41531#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41532#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41533#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41534#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41535#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41536#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41537#@gui : Reference Colors = choice("Bottom Layer","Top Layer")
41538#@gui : sep = separator()
41539#@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0
41540#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41541#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41542#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41543#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41544#@gui : sep = separator()
41545#@gui : note = note{"<small><b>Note: </b>
41546#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
41547#@gui : multiple input layers.
41548#@gui : </small>"}
41549#@gui : sep = separator()
41550#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/01/13</i>.</small>")
41551fx_transfer_histogram :
41552  to_rgb
41553  ref={$2?0:-1}
41554  transfer_histogram[^$ref] [$ref],256,$1
41555  c 0,255
41556
41557fx_transfer_histogram_preview :
41558  if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi
41559  ref={$2?0:-1}
41560  +store[$ref] _fx_trgb_ref
41561  gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_histogram $1,0 rm.",${-3--1}
41562  _fx_trgb_ref=
41563
41564  mv[$ref] $!
41565  repeat $!-1 l[$>,-1]
41566    rr2d[0] $_preview_width,$_preview_height,0,3 rr2d. {0,[w,h]/3},0,3
41567    to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0
41568    j[0] .,$3%,$4%
41569    rm.
41570  endl done
41571
41572#@gui Transfer Colors [PCA] : fx_transfer_pca, fx_transfer_pca_preview(1)+ : *
41573#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41574#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41575#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41576#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41577#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41578#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41579#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41580#@gui : Reference Colors = choice("Bottom Layer","Top Layer")
41581#@gui : sep = separator()
41582#@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0
41583#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41584#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41585#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41586#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41587#@gui : sep = separator()
41588#@gui : note = note{"<small><b>Note: </b>
41589#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
41590#@gui : multiple input layers.
41591#@gui : </small>"}
41592#@gui : sep = separator()
41593#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/01/13</i>.</small>")
41594fx_transfer_pca :
41595  to_rgb
41596  ref={$2?0:-1}
41597  transfer_pca[^$ref] [$ref],$1
41598  c 0,255
41599
41600fx_transfer_pca_preview :
41601  if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi
41602  ref={$2?0:-1}
41603  +store[$ref] _fx_trgb_ref
41604  gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_pca $1,0 rm.",${-3--1}
41605  _fx_trgb_ref=
41606
41607  mv[$ref] $!
41608  repeat $!-1 l[$>,-1]
41609    rr2d[0] $_preview_width,$_preview_height,0,3 rr2d. {0,[w,h]/3},0,3
41610    to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0
41611    j[0] .,$3%,$4%
41612    rm.
41613  endl done
41614
41615#@gui Tune HSV Colors : fx_tune_hsv, fx_tune_hsv_preview(1)
41616#@gui : Dark = choice(2,"Ignore","Lock","Remap")
41617#@gui : Dark Color = color(0,0,0)
41618#@gui : Target Hue (%) = float(100,0,100)
41619#@gui : Target Saturation (%) = float(100,0,100)
41620#@gui : Target Value (%) = float(100,0,100)
41621#@gui : sep = separator()
41622#@gui : Light = choice(2,"Ignore","Lock","Remap")
41623#@gui : Light Color = color(255,255,255)
41624#@gui : Target Hue (%) = float(100,0,100)
41625#@gui : Target Saturation (%) = float(100,0,100)
41626#@gui : Target Value (%) = float(100,0,100)
41627#@gui : sep = separator()
41628#@gui : Average = choice(1,"Ignore","Lock","Remap")
41629#@gui : Average Color = color(128,128,128)_0
41630#@gui : Target Hue (%) = float(0,0,100)_0
41631#@gui : Target Saturation (%) = float(50,0,100)_0
41632#@gui : Target Value (%) = float(100,0,100)_0
41633#@gui : sep = separator()
41634#@gui : Red = choice("Ignore","Lock","Remap")
41635#@gui : Red Color = color(255,0,0)_0
41636#@gui : Target Hue (%) = float(100,0,100)_0
41637#@gui : Target Saturation (%) = float(12.5,0,100)_0
41638#@gui : Target Value (%) = float(0,0,100)_0
41639#@gui : sep = separator()
41640#@gui : Yellow = choice("Ignore","Lock","Remap")
41641#@gui : Yellow Color = color(255,255,0)_0
41642#@gui : Target Hue (%) = float(100,0,100)_0
41643#@gui : Target Saturation (%) = float(12.5,0,100)_0
41644#@gui : Target Value (%) = float(0,0,100)_0
41645#@gui : sep = separator()
41646#@gui : Green = choice("Ignore","Lock","Remap")
41647#@gui : Green Color = color(0,255,0)_0
41648#@gui : Target Hue (%) = float(100,0,100)_0
41649#@gui : Target Saturation (%) = float(12.5,0,100)_0
41650#@gui : Target Value (%) = float(0,0,100)_0
41651#@gui : sep = separator()
41652#@gui : Cyan = choice("Ignore","Lock","Remap")
41653#@gui : Cyan Color = color(0,255,255)_0
41654#@gui : Target Hue (%) = float(100,0,100)_0
41655#@gui : Target Saturation (%) = float(12.5,0,100)_0
41656#@gui : Target Value (%) = float(0,0,100)_0
41657#@gui : sep = separator()
41658#@gui : Blue = choice("Ignore","Lock","Remap")
41659#@gui : Blue Color = color(0,0,255)_0
41660#@gui : Target Hue (%) = float(100,0,100)_0
41661#@gui : Target Saturation (%) = float(12.5,0,100)_0
41662#@gui : Target Value (%) = float(0,0,100)_0
41663#@gui : sep = separator()
41664#@gui : Magenta = choice("Ignore","Lock","Remap")
41665#@gui : Magenta Color = color(255,0,255)_0
41666#@gui : Target Hue (%) = float(100,0,100)_0
41667#@gui : Target Saturation (%) = float(12.5,0,100)_0
41668#@gui : Target Value (%) = float(0,0,100)_0
41669#@gui : sep = separator()
41670#@gui : Preview color mapping = choice(2,"None","Center","Top Left","Top Right","Bottom Left","Bottom Right")
41671#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41672#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41673#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41674#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41675#@gui : sep = separator()
41676#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/12/17</i>.</small>")
41677_fx_tune_hsv :
41678  mode_dark,Rdark,Gdark,Bdark,Hdark,Sdark,Vdark,\
41679  mode_light,Rlight,Glight,Blight,Hlight,Slight,Vlight,\
41680  mode_avg,Ravg,Gavg,Bavg,Havg,Savg,Vavg,\
41681  mode_red,Rred,Gred,Bred,Hred,Sred,Vred,\
41682  mode_yellow,Ryellow,Gyellow,Byellow,Hyellow,Syellow,Vyellow,\
41683  mode_green,Rgreen,Ggreen,Bgreen,Hgreen,Sgreen,Vgreen,\
41684  mode_cyan,Rcyan,Gcyan,Bcyan,Hcyan,Scyan,Vcyan,\
41685  mode_blue,Rblue,Gblue,Bblue,Hblue,Sblue,Vblue,\
41686  mode_magenta,Rmagenta,Gmagenta,Bmagenta,Hmagenta,Smagenta,Vmagenta,\
41687  preview_mapping=${1-64}
41688  all_colors=dark,light,avg,red,yellow,green,cyan,blue,magenta
41689
41690  repeat $! l[$>] split_opacity l[0]
41691    to_rgb
41692
41693    # Define list of considered colors.
41694    colors,sep=
41695    repeat narg($all_colors) color=${arg\ 1+$>,$all_colors} if ${mode_$color} colors.=$sep$color sep=, fi done
41696
41697    # Find darkest, lightest and average colors.
41698    +srgb2lab channels. 0 darklight={"[I(#-2,xm,ym),I(#-2,xM,yM)]"} rm.
41699    dark={[$darklight][0,3]} light={[$darklight][3,3]}
41700    +r. 1,1,1,3,2 avg={^} rm.
41701
41702    # Find the nearest existing colors to "pure" colors.
41703    eval. ">
41704      begin(
41705        red = yellow = green = cyan = blue = magenta = [0,0,0];
41706        best_red = best_yellow = best_green = best_cyan = best_blue = best_magenta = inf;
41707        test_color(color,tR,tG,tB) = (
41708          val = norm([tR,tG,tB]-[R,G,B]);
41709          val<best_#color?(best_#color = val; color# = I(#-2));
41710        );
41711      );
41712      test_color(red,255,0,0);
41713      test_color(yellow,255,255,0);
41714      test_color(green,0,255,0);
41715      test_color(cyan,0,255,255);
41716      test_color(blue,0,0,255);
41717      test_color(magenta,255,0,255);
41718      end(
41719        run(' red=',vtos(red),
41720            ' yellow=',vtos(yellow),
41721            ' green=',vtos(green),
41722            ' cyan=',vtos(cyan),
41723            ' blue=',vtos(blue),
41724            ' magenta=',vtos(magenta))
41725      ); I"
41726
41727    # Compute color mapping.
41728    if narg($colors)
41729      l[]
41730
41731        # Define color correspondences.
41732        repeat narg($colors)
41733          color=${arg\ 1+$>,$colors}
41734          if ${mode_$color}==2 (${$color},${R$color},${G$color},${B$color},${H$color},${S$color},${V$color})
41735          else (${$color},${$color},0,0,0)
41736          fi
41737        done
41738        a y permute yzcx
41739        s c,-3 srgb2rgb[0,1] rgb2hsv[0,1] /. 100
41740        f.. "
41741          Hs = i(#0,x,0,0,0); Ss = i(#0,x,0,0,1); Vs = i(#0,x,0,0,2);
41742          Hd = i0; Sd = i1; Vd = i2;
41743          DeltaH = Hd - Hs;
41744          alphaH = i(#2,x,0,0,0);
41745          H = abs(DeltaH)<abs(DeltaH - 360)?
41746            lerp(Hs,Hd,alphaH):
41747            lerp(Hs,Hd - 360,alphaH)%360;
41748          S = lerp(Ss,Sd,i(#2,x,0,0,1));
41749          V = lerp(Vs,Vd,i(#2,x,0,0,2));
41750          [ H,S,V ]"
41751        rm.
41752        hsv2rgb rgb2srgb
41753        repeat w color=${arg\ 1+$>,$colors} t_$color={I[$>]} done
41754        -. .. a c
41755
41756        # Create CLUT.
41757        if 0$_is_preview 33,33,33,4 else 63,63,63,4 fi
41758        f.. "I(#-1,round([i0,i1,i2]*(w#-1-1)/255))+=[i3,i4,i5,1];"
41759        s. c,-3 +max. 1 /[-3,-1] eq. 0
41760        inpaint_pde.. . rm[-3,-1]
41761        2,2,2,3,"[x,y,z]*255" ri. ..,3 +[-2,-1]
41762        nm clut
41763      endl
41764
41765      # Apply CLUT.
41766      if $clut map_clut.. . rm. c 0,255 fi
41767    fi
41768
41769    if $preview_mapping # Generate view of color mapping
41770      __px,__py,__ps={s=min(w,h);p=$preview_mapping;p==1?[0.5,0.5,80]:\
41771                                                    p==2?[0,0,48]:\
41772                                                    p==3?[1,0,48]:\
41773                                                    p==4?[0,1,64]:[1,1,64]}
41774      l[]
41775        repeat narg($all_colors) color=${arg\ 1+$>,$all_colors} (${$color}) done a y permute. yzcx
41776        s x r {s=round(max($__ps,16));[s,s]}
41777        repeat narg($all_colors) l[$>]
41778          color=${arg\ 1+$>,$all_colors}
41779          mode=${mode_$color}
41780          if $mode==2
41781            polygon 3,0,100%,100%,100%,100%,0,1,${t_$color}
41782            line 0,100%,100%,0,0.5,0xAAAAAAAA,255 line 0,100%,100%,0,0.5,0x55555555,0
41783          fi
41784          to {`uppercase(['$color'][0])`},0.5~,0.5~,50%,2,0.5
41785          to_rgba
41786          if $mode frame 1,1,0,0,0,255 frame 2,2,255 else frame 3,3,0,0,0,64 fi
41787        endl done
41788        append_tiles 3,3
41789        store __preview_mapping
41790      endl
41791    fi
41792
41793  endl a c endl done
41794
41795fx_tune_hsv :
41796  _fx_tune_hsv ${1-63},0
41797
41798fx_tune_hsv_preview :
41799  preview_mapping=$64
41800  repeat $! l[$>]
41801    gui_split_preview "_is_preview=1 _fx_tune_hsv $*",${-3--1}
41802    if $preview_mapping
41803      $__preview_mapping
41804      if narg($_preview_width) rr2d[0] $_preview_width,$_preview_height,0,2 fi
41805      s. c,-3 to_colormode.. {-3,s}
41806      j... ..,$__px~,$__py~,0,0,1,.,255 rm[-2,-1]
41807    fi
41808  endl done
41809
41810  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]}
41811  u "{$1}{$2,$3,$4}_"$d"{$5}_"$d"{$6}_"$d"{$7}_"$d\
41812    "{$8}{$9,$10,$11}_"$l"{$12}_"$l"{$13}_"$l"{$14}_"$l\
41813    "{$15}{$16,$17,$18}_"$a"{$19}_"$a"{$20}_"$a"{$21}_"$a\
41814    "{$22}{$23,$24,$25}_"$r"{$26}_"$r"{$27}_"$r"{$28}_"$r\
41815    "{$29}{$30,$31,$32}_"$y"{$33}_"$y"{$34}_"$y"{$35}_"$y\
41816    "{$36}{$37,$38,$39}_"$g"{$40}_"$g"{$41}_"$g"{$42}_"$g\
41817    "{$43}{$44,$45,$46}_"$c"{$47}_"$c"{$48}_"$c"{$49}_"$c\
41818    "{$50}{$51,$52,$53}_"$b"{$54}_"$b"{$55}_"$b"{$56}_"$b\
41819    "{$57}{$58,$59,$60}_"$m"{$61}_"$m"{$62}_"$m"{$63}_"$m\
41820    "{$64}{$65}{$66,$67}"
41821
41822#@gui User-Defined : fx_custom_transform, fx_custom_transform
41823#@gui : Red - Green - Blue - Alpha = text{"i"}
41824#@gui : Red - Green - Blue = text{"i + 90*(x/w)*cos(i/10)"}
41825#@gui : Red = text{"i"}
41826#@gui : Green = text{"i"}
41827#@gui : Blue = text{"i"}
41828#@gui : Alpha = text{"i"}
41829#@gui : Value Normalization = choice("None","RGB","RGBA")
41830#@gui : sep = separator()
41831#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41832fx_custom_transform :
41833  to_rgba repeat $!
41834    f. "$1"
41835    s. c a[-4--2] c f.. "$2"
41836    s.. c f[-4] "$3" f... "$4" f.. "$5" f. "$6"
41837    if $7==0 a[-4--1] c c. 0,255
41838    elif $7==1 a[-4--2] c n.. 0,255 c. 0,255 a[-2,-1] c
41839    else a[-4--1] c n. 0,255
41840    fi
41841  mv. 0 done
41842
41843#@gui ____<b>Contours</b>
41844#------------------------
41845
41846#@gui Convolve : fx_convolve, fx_convolve_preview(0)
41847#@gui : Kernel = choice("Custom","Average 3x3","Average 5x5","Average 7x7","Average 9x9","Prewitt-X","Prewitt-Y",
41848#@gui : "Sobel-X","Sobel-Y","Rotinv-X","Rotinv-Y","Laplacian","Robert Cross 1","Robert Cross 2","Impulses 5x5",
41849#@gui : "Impulses 7x7","Impulses 9x9")
41850#@gui : Boundary = choice(1,"Dirichlet","Neumann")
41851#@gui : sep = separator()
41852#@gui : note = note("<small><b>Note:</b> If parameter <i>Kernel</i> is set to <i>Custom</i>, it uses the custom
41853#@gui : convolution kernel defined below. Use commas and semicolons as separators for res. matrix columns and rows.
41854#@gui : </small>")
41855#@gui : Custom Kernel = text("0,1,0;1,-4,1;0,1,0")
41856#@gui : sep = separator()
41857#@gui : note = note("<small><b>Note:</b> Kernel multiplier is useful only when parameter <i>Value range</i> is set
41858#@gui : to <i>Cut</i>.</small>")
41859#@gui : Value Range = choice(1,"Cut","Normalize")
41860#@gui : Kernel Multiplier = float(1,0,50)
41861#@gui : sep = separator()
41862#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
41863#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
41864#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
41865#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
41866#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
41867#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
41868#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
41869#@gui : sep = separator()
41870#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41871#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41872#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41873#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41874#@gui : sep = separator()
41875#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/06/06</i>.</small>")
41876fx_convolve : skip "${3=1}"
41877  ac "_fx_convolve $1,$2,\"$3\",${4--5}",$-4
41878
41879_fx_convolve :
41880  if $1 _fx_convolve$1[] else ($3) fi
41881  if !$4 *. $5 fi
41882  convolve[0--2] .,$2
41883  if $4 n 0,255 else c 0,255 fi
41884  rm.
41885
41886_fx_convolve1 : 3,3 f 1 normalize_sum  # Average 3x3
41887_fx_convolve2 : 5,5 f 1 normalize_sum  # Average 5x5
41888_fx_convolve3 : 7,7 f 1 normalize_sum  # Average 7x7
41889_fx_convolve4 : 9,9 f 1 normalize_sum  # Average 9x9
41890_fx_convolve5 : (1,0,-1;1,0,-1;1,0,-1) # Prewitt-X
41891_fx_convolve6 : (1,1,1;0,0,0;-1,-1,-1) # Prewitt-Y
41892_fx_convolve7 : (1,0,-1;2,0,-2;1,0,-1) # Sobel-X
41893_fx_convolve8 : (1,2,1;0,0,0;-1,-2,-1) # Sobel-Y
41894_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
41895_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
41896_fx_convolve11 : (0,1,0;1,-4,1;0,1,0)   # Laplacian
41897_fx_convolve12 : (1,0;0,-1)             # Robert Cross1
41898_fx_convolve13 : (0,1;-1,0)             # Robert Cross2
41899_fx_convolve14 : 3,3 f 1 r 7,7,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 5x5
41900_fx_convolve15 : 3,3 f 1 r 9,9,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 7x7
41901_fx_convolve16 : 3,3 f 1 r 11,11,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 9x9
41902
41903fx_convolve_preview : skip "${3=1}"
41904  gui_split_preview "fx_convolve $1,$2,\"$3\",${4--1}",${-3--1}
41905
41906#@gui Curvature : fx_curvature, fx_curvature_preview(0)
41907#@gui : Smoothness = float(2,0,10)
41908#@gui : Min Threshold = float(0,0,100)
41909#@gui : Max Threshold = float(100,0,100)
41910#@gui : Absolute Value = bool(0)
41911#@gui : Negative Colors = bool(0)
41912#@gui : sep = separator()
41913#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41914#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41915#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41916#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41917#@gui : sep = separator()
41918#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41919fx_curvature :
41920  repeat $! l[$>] split_opacity l[0]
41921    b $1 iee
41922    if $4 abs fi
41923    c $2%,$3%
41924    if $5 negate fi
41925    n 0,255
41926  endl a c endl done
41927
41928fx_curvature_preview :
41929  gui_split_preview "fx_curvature ${^0}",${-3--1}
41930
41931#@gui Difference of Gaussians : fx_dog, fx_dog_preview(1)
41932#@gui : 1st Variance = float(1.4,0,5)
41933#@gui : 2nd Variance = float(1.5,0,5)
41934#@gui : Threshold = float(0,0,49)
41935#@gui : Negative Colors = bool(0)
41936#@gui : Monochrome = bool(1)
41937#@gui : sep = separator()
41938#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41939#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41940#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41941#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41942#@gui : sep = separator()
41943#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41944fx_dog :
41945  dog $1%,$2%
41946  if $5 norm fi
41947  c $3%,{100-$3}%
41948  if $4 negate fi
41949  n 0,255
41950
41951fx_dog_preview :
41952  gui_split_preview "fx_dog ${^0}",${-3--1}
41953
41954#@gui Distance Transform : fx_distance, fx_distance_preview(0)
41955#@gui : Value = int(128,0,255)
41956#@gui : Metric = choice(2,"Chebyshev","Manhattan","Euclidean","Squared-Euclidean")
41957#@gui : Normalization = choice(2,"Cut","Normalize","Modulo")
41958#@gui : Modulo Value = int(32,1,255)
41959#@gui : sep = separator()
41960#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41961#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41962#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41963#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41964#@gui : sep = separator()
41965#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/07/04</i>.</small>")
41966fx_distance :
41967  repeat $! l[$>] split_opacity l[0]
41968    distance $1,$2
41969    if $3==0 c 0,255
41970    elif $3==1 n 0,255
41971    else % $4 n 0,255
41972    fi
41973  endl a c endl done
41974
41975fx_distance_preview :
41976  gui_split_preview "fx_distance ${^0}",${-3--1}
41977
41978#@gui Edges : fx_edges, fx_edges_preview(0)
41979#@gui : Smoothness = float(0,0,10)
41980#@gui : Threshold = float(15,0,50)
41981#@gui : Negative Colors = bool(0)
41982#@gui : sep = separator()
41983#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
41984#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
41985#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
41986#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
41987#@gui : sep = separator()
41988#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
41989fx_edges :
41990  to_rgb b $1% edges $2%
41991  if $3 negate fi
41992  n 0,255
41993
41994fx_edges_preview :
41995  gui_split_preview "fx_edges ${^0}",${-3--1}
41996
41997#@gui Edges Offsets : fx_edge_offsets, fx_edge_offsets_preview(0)
41998#@gui : Smoothness = float(0,0,10)
41999#@gui : Threshold = float(15,0,50)
42000#@gui : Scale = int(4,0,32)
42001#@gui : Thickness = int(1,0,16)
42002#@gui : Negative Colors = bool(0)
42003#@gui : sep = separator()
42004#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42005#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42006#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42007#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42008#@gui : sep = separator()
42009#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42010fx_edge_offsets :
42011  repeat $!
42012    os={s}
42013    b. $1% gradient_norm. >=. $2% skeleton. 0 distance. 1 round. 1 %. $3 >=. {max(1,$3-$4)}
42014    if !$5 negate. fi
42015    n. 0,255 to_colormode. $os
42016  mv. 0 done
42017
42018fx_edge_offsets_preview :
42019  gui_split_preview "fx_edge_offsets ${^0}",${-3--1}
42020
42021#@gui Extract Foreground [Interactive] : fx_extract_foreground, gui_no_preview
42022#@gui : Feathering = _float(0,0,4)
42023#@gui : Dilation = int(0,-32,32)
42024#@gui : Output Mode = choice{3,"RGBA Image (Full-Transparency / 1 Layer)","RGBA Image (Updatable / 1 Layer)",
42025#@gui : "RGB Image + Binary Mask (2 Layers)","RGBA Foreground + Background (2 Layers)"}
42026#@gui : View Resolution = _choice{1,"Small (Faster)","Medium","High (Slower)","Very High (Even Slower)"}
42027#@gui : sep = separator()
42028#@gui : note = note{"<small><b>Description:</b>\n
42029#@gui : This filter allows to quickly extract foreground objects from background in opaque RGB images.
42030#@gui : Click on the <i>Apply</i> or <i>OK</i> buttons below to open the interactive window and start adding
42031#@gui : foreground and background control points. When you're done, exit the interactive window: your extracted
42032#@gui : foreground will be transferred back to the host software.\n\n
42033#@gui : If you are not satisfied with the result, click on <i>Apply</i> once again to modify your control points
42034#@gui : defined previously. To remove all control points, click on the <i>Reset</i> button above.
42035#@gui : </small>"}
42036#@gui : Last Image Size = value(0,0)
42037#@gui : Control Points = value(-1)
42038#@gui : sep = separator()
42039#@gui : note = note{"<small><b>Interactions:</b>\n
42040#@gui : Use the following actions in the interactive window to build your extraction mask :\n\n
42041#@gui : - <b>Left mouse button</b> or key <b>F</b> create a new <b>foreground</b> control point
42042#@gui : (or move an existing one).\n
42043#@gui : - <b>Right mouse button</b> or key <b>B</b> create a new <b>background</b> control point
42044#@gui : (or move an existing one).\n
42045#@gui : - <b>Mouse wheel</b>, or keys <b>CTRL+arrows UP/DOWN</b> zoom view in/out.\n
42046#@gui : - Key <b>SPACE</b> updates the extraction mask.\n
42047#@gui : - Key <b>TAB</b> toggles background view modes.\n
42048#@gui : - Key <b>M</b> toggles marker view modes.\n
42049#@gui : - Key <b>BACKSPACE</b> deletes the last control point added.\n
42050#@gui : - Key <b>PAGE UP</b> increases background opacity.\n
42051#@gui : - Key <b>PAGE DOWN</b> decreases background opacity.\n
42052#@gui : - Keys <b>CTRL+D</b> increase window size.\n
42053#@gui : - Keys <b>CTRL+C</b> decrease window size.\n
42054#@gui : - Keys <b>CTRL+R</b> reset window size.\n
42055#@gui : - Keys <b>ESC</b>, <b>Q</b> or <b>ENTER</b> exit the interactive window.
42056#@gui : </small>"}
42057#@gui : sep = separator()
42058#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/29/09</i>.</small>")
42059fx_extract_foreground :
42060  if !$! return fi
42061  resolution={arg(1+$3,512,1024,2048,0)}
42062  repeat $! l[$<]
42063    nm=${-gui_layer_name}
42064    nm "[G"{`39`}"MIC] Interactive Foreground Extraction"
42065    if [$6][0]==-1" || "[$5]!=[w,h] _gui_control_points= else _gui_control_points=$6 fi
42066    status=${x_segment\ $resolution}
42067    sh 3 b. $1% if $2>0 dilate. {1+2*$2} elif $2<0 erode. {1-2*$2} fi
42068    rm.
42069    if $3==1 sh 3 max. 1 rm.
42070    elif $3==2 s c,-3 r. 100%,100%,1,4 rv nm[0] name(Mask) nm[1] name($nm)
42071    elif $3==3
42072      .
42073      sh.. 0,2 +channels... 3,3 >=. 3 *[-2,-1] rm.
42074      sh. 0,2 +channels.. 3,3 <=. {255-3} *[-2,-1] rm.
42075      sh. 3 *. -1 +. 255 rm.
42076      gui_autocrop_layers[0]
42077      pos0=${gui_layer_pos[0]} pos1=${gui_layer_pos[1]}
42078      nm[0] name($nm" [foreground]"),pos($pos0)
42079      nm[1] name($nm" [background]"),pos($pos1)
42080    fi
42081  endl done
42082  if narg($status)>=4 u \{$1\}\{$2\}\{$3\}\{$4\}\{{w},{h}\}\{$status\} else u "" fi
42083
42084#@gui Gradient Norm : fx_gradient_norm, fx_gradient_norm_preview(0)
42085#@gui : Smoothness = float(0,0,10)
42086#@gui : Linearity = float(0.5,0,1.5)
42087#@gui : Min Threshold = float(0,0,100)
42088#@gui : Max Threshold = float(100,0,100)
42089#@gui : Negative Colors = bool(0)
42090#@gui : sep = separator()
42091#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42092#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42093#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42094#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42095#@gui : sep = separator()
42096#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42097fx_gradient_norm :
42098  b $1 gradient_norm ^ $2
42099  c $3%,$4%
42100  if $5 negate fi
42101  n 0,255
42102
42103fx_gradient_norm_preview :
42104  gui_split_preview "fx_gradient_norm ${^0}",${-3--1}
42105
42106#@gui Gradient RGB : fx_gradient2rgb, fx_gradient2rgb_preview(0)
42107#@gui : Smoothness = float(0,0,10)
42108#@gui : Min Threshold = float(0,0,100)
42109#@gui : Max Threshold = float(100,0,100)
42110#@gui : Orientation Only = bool(0)
42111#@gui : Negative Colors = bool(0)
42112#@gui : sep = separator()
42113#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42114#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42115#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42116#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42117#@gui : sep = separator()
42118#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42119fx_gradient2rgb :
42120  b $1 gradient2rgb $4
42121  c $2%,$3%
42122  if $5 negate fi
42123  n 0,255
42124
42125fx_gradient2rgb_preview :
42126  gui_split_preview "fx_gradient2rgb ${^0}",${-3--1}
42127
42128#@gui Isophotes : fx_isophotes, fx_isophotes_preview(0)
42129#@gui : Levels = int(8,1,256)
42130#@gui : Smoothness = float(0,0,5)
42131#@gui : Filling = choice(1,"Transparent","Colors")
42132#@gui : sep = separator()
42133#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42134#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42135#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42136#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42137#@gui : sep = separator()
42138#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42139fx_isophotes :
42140  if $3
42141    topographic_map $1,$2
42142  else
42143    b $2 isophotes $1
42144  fi
42145
42146fx_isophotes_preview :
42147  gui_split_preview "fx_isophotes ${^0}",${-3--1}
42148
42149#@gui Laplacian : fx_laplacian, fx_laplacian_preview(0)
42150#@gui : Smoothness = float(0,0,10)
42151#@gui : Min Threshold = float(0,0,100)
42152#@gui : Max Threshold = float(100,0,100)
42153#@gui : Absolute Value = bool(0)
42154#@gui : Negative Colors = bool(0)
42155#@gui : sep = separator()
42156#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42157#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42158#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42159#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42160#@gui : sep = separator()
42161#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42162fx_laplacian :
42163  b $1 laplacian
42164  if $4 abs fi
42165  c $2%,$3%
42166  if $5 negate fi
42167  n 0,255
42168
42169fx_laplacian_preview :
42170  gui_split_preview "fx_laplacian ${^0}",${-3--1}
42171
42172#@gui Local Orientation : fx_local_orientation, fx_local_orientation_preview(1)
42173#@gui : Smoothness = float(0,0,5)
42174#@gui : Min Threshold = float(0,0,100)
42175#@gui : Max Threshold = float(100,0,100)
42176#@gui : Negative Colors = bool(0)
42177#@gui : sep = separator()
42178#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42179#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42180#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42181#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42182#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42183#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42184#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42185#@gui : sep = separator()
42186#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42187#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42188#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42189#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42190#@gui : sep = separator()
42191#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42192_fx_local_orientation :
42193  repeat $! l[$>] split_opacity l[0]
42194    b $1% gradient_orientation 2 complex2polar rm[0--1:2]
42195    c $2%,$3%
42196    if $4 negate fi
42197    n 0,255
42198  endl a c endl done
42199
42200fx_local_orientation :
42201  ac "_fx_local_orientation $1,$2,$3,$4",$5,2
42202
42203fx_local_orientation_preview :
42204  gui_split_preview "fx_local_orientation ${^0}",${-3--1}
42205
42206#@gui Morphological Filter : fx_morphological, fx_morphological_preview(0)
42207#@gui : Action = choice{"Erosion","Dilation","Opening","Closing","Original - Erosion","Dilation - Original",
42208#@gui : "Original - Opening","Closing - Original","Original - (Opening + Closing)/2","Closing - Opening"}
42209#@gui : Kernel = choice(0,"Square","Octagonal","Circular","Custom")
42210#@gui : Size = int(5,2,60)
42211#@gui : note = note("<small>Parameter <i>Size</i> is inactive for <i>Custom</i> kernel.</small>")
42212#@gui : Custom Kernel = text("1,0,1; 0,1,0; 1,0,1")
42213#@gui : Negative = bool()
42214#@gui : Process Transparency = bool(0)
42215#@gui : sep = separator()
42216#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42217#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42218#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42219#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42220#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42221#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42222#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42223#@gui : Value Action = choice("None","Cut","Stretch")
42224#@gui : sep = separator()
42225#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42226#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42227#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42228#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42229#@gui : sep = separator()
42230#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/22/06</i>.</small>")
42231fx_morphological :
42232  ac "_fx_morphological ${1-3},\"$4\",${5-6}",$7,$8
42233
42234fx_morphological_preview :
42235  gui_split_preview "fx_morphological ${1-3},\"$4\",${5--1}",${-3--1}
42236  if $2==3
42237    ('"$4"') f. "(i>=_'0' && i<=_'9') || i==_',' || i==_';'?i:-1"
42238    discard. -1 ({t}) rr2d. {0,max(24,w/6)},{0,max(24,h/6)},0,1 >. 0 *. 255
42239    to_rgba. frame. 1,1,0,0,0,0,255 frame. 1,1,255 frame. 1,1,0,0,0,0,255
42240    j[^-1] .,2,2,0,0,0.75 rm.
42241  else
42242  fi
42243
42244_fx_morphological :
42245  ('"$4"') f. "(i>=_'0' && i<=_'9') || i==_',' || i==_';'?i:-1" discard. -1 ckernel={t} rm.
42246  if $2==0 m "my_erode: erode $""1" m "my_dilate: dilate $""1"
42247  elif $2==1 m "my_erode: erode_oct $""1" m "my_dilate: dilate_oct $""1"
42248  elif $2==2 m "my_erode: erode_circ $""1" m "my_dilate: dilate_circ $""1"
42249  else
42250    m "my_erode : ("$ckernel") erode[^-1] . rm."
42251    m "my_dilate : ("$ckernel") dilate[^-1] . rm."
42252  fi
42253  # Erosion
42254  if $1==0 m "my_action : my_erode $3"
42255  # Dilation
42256  elif $1==1 m "my_action : my_dilate $3"
42257  # Opening
42258  elif $1==2 m "my_action : my_erode $3 my_dilate $3"
42259  # Closing
42260  elif $1==3 m "my_action : my_dilate $3 my_erode $3"
42261  # Original - Erosion
42262  elif $1==4 m "my_action : +my_erode $3 -"
42263  # Dilation - Original
42264  elif $1==5 m "my_action : +my_dilate $3 rv -"
42265  # Original - Opening
42266  elif $1==6 m "my_action : +my_erode $3 my_dilate. $3 -"
42267  # Closing - Original
42268  elif $1==7 m "my_action : +my_dilate $3 my_erode. $3 rv -"
42269  # Original - (Opening + Closing)/2
42270  elif $1==8 m "my_action : +my_erode $3 my_dilate. $3 +my_dilate.. $3 my_erode. $3 +[-2,-1] /. 2 -"
42271  # Closing - Opening
42272  else m "my_action : +my_erode $3 my_dilate. $3 my_dilate.. $3 my_erode.. $3 -"
42273  fi
42274  repeat $! l[$>]
42275    if !$6 split_opacity fi
42276    my_action[0]
42277    a c
42278  endl done
42279  if $5 repeat $! l[$>] split_opacity negate[0] a c endl done fi
42280  um my_erode,my_dilate,my_action
42281
42282#@gui Segmentation : fx_segment_watershed, fx_segment_watershed_preview(0)
42283#@gui : Edge Threshold = float(2,0,15)
42284#@gui : Smoothness = float(1,0,5)
42285#@gui : sep = separator()
42286#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
42287#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
42288#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
42289#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
42290#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
42291#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
42292#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
42293#@gui : Value Action = choice("None","Cut","Normalize")
42294#@gui : sep = separator()
42295#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42296#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42297#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42298#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42299#@gui : sep = separator()
42300#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42301fx_segment_watershed : skip ${4=1}
42302  ac "b $2 segment_watershed $1",$3,$4
42303
42304fx_segment_watershed_preview :
42305  gui_split_preview "fx_segment_watershed ${^0}",${-3--1}
42306
42307#@gui Skeleton : fx_skeleton, fx_skeleton_preview(1)
42308#@gui : Method = choice{"Distance (Fast)","Thinning (Slow)"}
42309#@gui : Smoothness = float(0,0,10)
42310#@gui : sep = separator()
42311#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42312#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42313#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42314#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42315#@gui : sep = separator()
42316#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/07/04</i>.</small>")
42317fx_skeleton :
42318  remove_opacity
42319  b $2% >= 50%
42320  if $1 thinning 1
42321  else
42322    distance 0 sharpen 1e10 >= 100%
42323    repeat $! +erode[$>] 2 -[$>,-1] done
42324  fi
42325  * 255
42326
42327fx_skeleton_preview :
42328  gui_split_preview "fx_skeleton ${^0}",${-3--1}
42329
42330#@gui Super-Pixels : fx_superpixels, fx_superpixels_preview(0)
42331#@gui : Size = int(16,4,64)
42332#@gui : Regularity = float(10,0,128)
42333#@gui : Iterations = int(5,1,16)
42334#@gui : Colors = choice(1,"Random","Average")
42335#@gui : Border Opacity = float(1,0,1)
42336#@gui : Border Color = color(0,0,0,255)
42337#@gui : sep = separator()
42338#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42339#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42340#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42341#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42342#@gui : sep = separator()
42343#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/11/16</i>.</small>")
42344fx_superpixels :
42345  repeat $! l[$>]
42346    +srgb2lab slic. ${1-3}
42347    if $4 +blend shapeaverage else +map. 2,2 fi
42348    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..
42349    else k.
42350    fi
42351  endl done
42352
42353fx_superpixels_preview :
42354  gui_split_preview "fx_superpixels ${^0}",${-3--1}
42355
42356#@gui Thin Edges : fx_thin_edges, fx_thin_edges_preview(0)
42357#@gui : Smoothness = float(0,0,10)
42358#@gui : Threshold = float(15,0,50)
42359#@gui : Negative Colors = bool(0)
42360#@gui : sep = separator()
42361#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
42362#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
42363#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
42364#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
42365#@gui : sep = separator()
42366#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42367fx_thin_edges :
42368  b $1% gradient_norm >= $2% thinning 1
42369  if !$3 negate fi
42370  n 0,255
42371
42372fx_thin_edges_preview :
42373  gui_split_preview "fx_thin_edges ${^0}",${-3--1}
42374
42375
42376#@gui ____<b>Deformations</b>
42377#----------------------------
42378
42379#@gui Breaks : fx_breaks,fx_breaks(0)
42380#@gui : Type = choice("Flat","Relief")
42381#@gui : Amplitude = float(30,0,300)
42382#@gui : Frequency (%) = float(30,0,100)
42383#@gui : Smoothness = float(0.5,0,10)
42384#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42385#@gui : sep = separator()
42386#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/09/09</i>.</small>")
42387fx_breaks :
42388  repeat $! l[$>]
42389    if !$5 to_a fi
42390    100%,100%,1,1,"u<($3%)^6"
42391    if $1 # Wavy
42392      distance. 1 sharpen. 100000 neq. 0 distance. 1 b. $4 g. xy a[-2,-1] c
42393    else # Flat
42394      delaunay. 0 label_fg. 0,1 {1+iM},1,1,2 rand. -30,30 point. 0 map.. . rm.
42395    fi
42396    n. -$2,$2 warp[0] .,1,1,$5 rm.
42397  endl done
42398
42399#@gui Cartesian Transform : fx_custom_deformation, fx_custom_deformation(1)
42400#@gui : X-Warping = text{"(w+h)/20 * cos(y*20/h)"}
42401#@gui : Y-Warping = text{"(w+h)/20 * sin(x*20/w)"}
42402#@gui : Relative Warping = bool(1)
42403#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
42404#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42405#@gui : sep = separator()
42406#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42407fx_custom_deformation :
42408  if !$5 to_a fi
42409  repeat $!
42410    +norm. . f.. "$1" f. "$2"
42411    a[-2,-1] c warp.. .,$3,$4,$5,1 rm.
42412  mv. 0 done
42413
42414#@gui Circle Transform : fx_circle_transform, fx_circle_transform_preview(1)
42415#@gui : Center (%) = point(50,50,0,1)
42416#@gui : Radius = point(75,50,0,1)
42417#@gui : X-Scale = float(-2,-16,16)
42418#@gui : Y-Scale = float(-2,-16,16)
42419#@gui : Symmetry = choice("None","Inside","Outside")
42420#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
42421#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42422#@gui : Preview Reference Circle = bool(1)
42423#@gui : sep = separator()
42424#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/08/01</i>.</small>")
42425fx_circle_transform :
42426  repeat $! l[$>] to_rgba
42427    r={dx=($3-$1)*(w-1)%;dy=($4-$2)*(h-1)%;norm(dx,dy)}
42428    if $7==0 cond="i(X,Y,z,c,$8,$9)"
42429    elif $7==1 cond="if(N<"$r",i(X,Y,z,c,$8,$9),i)"
42430    else cond="if(N>"$r",i(X,Y,z,c,$8,$9),i)"
42431    fi
42432    f "U = x - w*$1%;
42433       V = y - h*$2%;
42434       N = sqrt(U*U + V*V);
42435       Nr = N - "$r";
42436       X = x + $5*Nr*U/N;
42437       Y = y + $6*Nr*V/N;
42438       "$cond
42439  endl done
42440
42441fx_circle_transform_preview :
42442  fx_circle_transform $*
42443  if $10
42444    rr2d ${-gui_preview_wh},0,1
42445    repeat $! l[$>]
42446      x0,y0={[$1,$2]*([w,h]-1)%}
42447      r={dx=($3-$1)*(w-1)%;dy=($4-$2)*(h-1)%;norm(dx,dy)}
42448      circle $x0,$y0,{$r-1},1,0xFFFFFFFF,0,0,0,255
42449      circle $x0,$y0,{$r+1},1,0xFFFFFFFF,0,0,0,255
42450      circle $x0,$y0,$r,1,0xFFFFFFFF,0,255,0,255
42451    endl done
42452  fi
42453
42454#@gui Conformal Maps : fx_conformal_maps, fx_conformal_maps_preview(1)
42455#@gui : Mapping = choice{8,"Custom Formula","z","(z+1)/(z-1)","cos(z)","sin(z)","tan(z)","exp(z)","log(z)",
42456#@gui : "Dipole: 1/(4*z^2-1)","Star: -5*(z^3/3-z/4)/2"}
42457#@gui : Exponent (Real) = float(1,-16,16)
42458#@gui : Exponent (Imaginary) = float(0,-16,16)
42459#@gui : Custom Formula = text{1,"((1.1 + i*z/6)/(1.04 - i*z/6))^6.2"}
42460#@gui : sep = separator()
42461#@gui : Zoom = float(0,-4,4)
42462#@gui : Angle = float(0,-180,180)
42463#@gui : Aspect Ratio = float(0,-1,1)
42464#@gui : X-Shift = float(0,-5,5)
42465#@gui : Y-Shift = float(0,-5,5)
42466#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42467#@gui : Anti-Aliasing = int(0,0,3)
42468#@gui : sep = separator()
42469#@gui : Specify Different Output Size = _bool(0)
42470#@gui : Output Width = _text("1024")
42471#@gui : Output Height = _text("1024")
42472#@gui : sep = separator()
42473#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/15/02</i>.</small>")
42474fx_conformal_maps :
42475  to_a
42476  expr0="$4"
42477  expr1="z"
42478  expr2="(z+1)/(z-1)"
42479  expr3="cos(z)"
42480  expr4="sin(z)"
42481  expr5="tan(z)"
42482  expr6="exp(z)"
42483  expr7="log(z)"
42484  expr8="1/(4*z^2-1)"
42485  expr9="-5*(z^3/3-z/4)/2"
42486
42487  ('${expr$1}')
42488  replace_str. "*","**"
42489  replace_str. "/","//"
42490  replace_str. "^","^^"
42491  replace_str. "exp(","cexp("
42492  replace_str. "log(","clog("
42493  replace_str. "cos(","ccos("
42494  replace_str. "sin(","csin("
42495  replace_str. "tan(","ctan("
42496  expr={t}
42497  rm.
42498
42499  repeat $! l[$>]
42500    wh={$12?[$13,$14]:[w,h]}
42501    {(1+$11)*[$wh]},1,100%
42502    f. "begin(
42503          ccos(z) = (iz = [ -z[1],z[0] ]; (cexp(iz) + cexp(-iz)/2));
42504          csin(z) = (iz = [ -z[1],z[0] ]; (cexp(iz) - cexp(-iz)/2));
42505          ctan(z) = csin(z)//ccos(z);
42506          boundary = $10;
42507          interpolation = 1;
42508          const f = max(w,h);
42509          const f0 = max(w#0,h#0);
42510          i = [0,1];
42511        );
42512        z = (2*[ x,y ] - [ w,h ])/f;
42513        z = rot(-$6°)*z;
42514        z-= [ $8, $9 ];
42515        z/=[ 10^($5 + $7), 10^$5 ];
42516        z = ("$expr");
42517        if ($1, z = z^^[$2,$3]);
42518        z = rot($6°)*z;
42519        z = 0.5*(f0*z + [w#0,h#0]);
42520        I(#0,z)"
42521      r. $wh,1,100%,2
42522    rm..
42523  endl done
42524
42525fx_conformal_maps_preview :
42526  fx_conformal_maps ${1-3},"$4",${5-11},0,0,0
42527
42528#@gui Crease : fx_crease,fx_crease(0)
42529#@gui : Amplitude = float(30,0,300)
42530#@gui : Frequency (%) = float(10,0,100)
42531#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42532#@gui : sep = separator()
42533#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/22</i>.</small>")
42534fx_crease :
42535  repeat $! l[$>]
42536    if !$3 to_a fi
42537    2,2,1,2,{"const w1 = w#-1-1; const h1 = h#-1 - 1; [ 0,w1,0,w1,0,0,h1,h1 ];"}
42538    r. {0,D=$2*[w,h]%;[max(D[0],1),max(D[1],1)]},1,2,3 noise. $1,1
42539    r. ..,..,1,2,3 warp.. .,0,1,$3 rm.
42540  endl done
42541
42542#@gui Distort Lens : fx_distort_lens, fx_distort_lens(1)
42543#@gui : Amplitude = float(0.1,-1,1)
42544#@gui : Aspect Ratio = float(0,-2,2)
42545#@gui : Zoom = float(0,-4,4)
42546#@gui : Center (%) = point(50,50,0,1)
42547#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
42548#@gui : sep = separator()
42549#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/18/02</i>.</small>")
42550fx_distort_lens :
42551  if !$6 to_a fi
42552  undistort ${1-3},$4%,$5%,$6
42553
42554#@gui Drop Water : fx_drop_water, fx_drop_water_preview(1)
42555#@gui : note = note("<small><b>Shape geometry:</b></small>")
42556#@gui : Shapes = choice("Procedural","Opaque Regions on Top Layer")
42557#@gui : Density = float(20,0,100)
42558#@gui : Radius = float(2,0,5)
42559#@gui : Variability = float(80,0,100)
42560#@gui : Random Seed = int(0,0,16384)
42561#@gui : note = note("<small>Parameters <i>Density</i>, <i>Radius</i>, <i>Variability</i> and <i>Random seed</i>
42562#@gui : are used only in <i>Procedural shapes</i> mode.</small>")
42563#@gui : sep = separator()
42564#@gui : note = note("<small><b>Light parameters:</b></small>")
42565#@gui : Refraction = float(3,0,20)
42566#@gui : Light Angle = float(35,0,360)
42567#@gui : Specular Size = float(10,0,100)
42568#@gui : Specular Intensity = float(1,0,1)
42569#@gui : Specular Centering = float(0.5,0,1)
42570#@gui : sep = separator()
42571#@gui : note = note("<small><b>Shadow parameters:</b></small>")
42572#@gui : Shadow Size = float(0.25,0,3)
42573#@gui : Shadow Intensity = float(0.5,0,1)
42574#@gui : Shadow Smoothness = float(0.75,0,3)
42575#@gui : Diffuse Shadow = float(0.05,0,3)
42576#@gui : sep = separator()
42577#@gui : Smoothness = float(0.15,0,3)
42578#@gui : Output as Separate Layers = _bool(1)
42579#@gui : sep = separator()
42580#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/21/07</i>.</small>")
42581fx_drop_water :
42582  N={$!-$1}
42583  if $N<=0 error "At least two layers are required in this mode." fi
42584
42585  repeat $N l[{$!-$>-1}] nm0={n} nm=${-gui_layer_name}
42586    nm img
42587
42588    # Create binary shapes (i.e. opacity map).
42589    srand $5
42590    if $1 # Shape from top layer.
42591      pass[0] 0 to_a. channels. 100% >=. 50%
42592      r. [0],[0],1,1,0,0,0.5,0.5
42593    else # Procedural shape.
42594      100%,100%
42595      rmin={max(0.1,$3*(1-$4%))} rmax={max(0.1,$3)}
42596      repeat 10
42597        100%,100%
42598        random3d {max(1,$2)} *3d. {-2,w},{-2,h},0
42599        j3d.. .,0,0,0,1,1,0,0 rm.
42600        b. {$rmin+($rmax-$rmin)*$>/9}%,0,1
42601        j.. .,0,0,0,0,0.5 rm.
42602      done
42603      >=. 10%
42604    fi
42605    nm. shape
42606
42607    # Create elevation map.
42608    +b[shape] 1% n. 0,30
42609    nm. elevation
42610
42611    # Warp image.
42612    g[elevation] xy a[-2,-1] c nm. grad
42613    +*[grad] {grad,$6*max(w,h)/100} *. [shape] b. $15%
42614    +warp[img] .,1,1,1 rm.. nm. refraction
42615
42616    # Compute specular spots.
42617    +*[grad] -1 100%,100%,1,1,1 a[-2,-1] c orientation.  # 3D normal map.
42618    a={$7*pi/180} ca={-cos($a)} sa={-sin($a)}
42619    mix_channels. ({(1-$10)*$ca},{(1-$10)*$sa},1) c. {100-$8}%,100% n. 0,1
42620    *. [shape] nm. spots
42621
42622    # Compute ambiant light (gradient).
42623    mix_channels[grad] ($ca,$sa)
42624    n[grad] 0,1 *[grad] [shape]
42625
42626    # Drop shadow.
42627    +shift[shape] {-$11*$ca}%,{-$11*$sa}%,0,0,1
42628    -. [shape] >=. 1 b. $13% n. 0,1
42629    nm. shadow
42630    b[shape] $14% n. 0,1  # Add diffuse shadow around each drop.
42631
42632    # Prepare layers for output.
42633    nm[img] name($nm)
42634
42635    *[shadow] 255 channels[shadow] -1,0 mv[shadow] 1
42636    nm[shadow] name($nm" [shadow]"),mode(alpha),opacity({$12*100})
42637
42638    to_a[refraction] sh[refraction] 100% +b[shape] $15% *[-2,-1] rm.
42639    mv[refraction] 2
42640    nm[refraction] name($nm" [refraction]"),mode(alpha)
42641
42642    channels[spots] -1,0 sh[spots] 0 f. 1 rm. *[spots] 255
42643    nm[spots] name($nm" [specular spots]"),mode(alpha),opacity({$9*100})
42644
42645    rv[shape,grad] a[grad,shape] c *[grad] 255 b[grad] $15%
42646    nm[grad] name($nm" [gradient]"),mode(grainmerge)
42647
42648    rv
42649    if !$16 gui_merge_layers nm $nm0 fi
42650  endl done
42651  if $1 rm[0] fi
42652
42653fx_drop_water_preview :
42654  N={$!-$1}
42655  if $N<=0 gui_warning_preview "At least two layers are required in this mode." return fi
42656  if $1
42657    repeat $N l[{$!-$>-1}]
42658      pass[0] 0 mv. 0
42659      fx_drop_water $* gui_merge_layers
42660    endl done
42661    rm[0]
42662  else
42663    repeat $! l[$>]
42664      fx_drop_water $* gui_merge_layers
42665    endl done
42666  fi
42667
42668#@gui Equirectangular to Nadir-Zenith : fx_equirectangular2nadirzenith, fx_equirectangular2nadirzenith(1)
42669#@gui : Mode = choice{"to Nadir / Zenith","to Equirectangular"}
42670#@gui : sep = separator()
42671#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/29/12</i>.</small>")
42672fx_equirectangular2nadirzenith :
42673  if $1 nadirzenith2equirectangular else equirectangular2nadirzenith fi
42674
42675#@gui Euclidean - Polar : fx_euclidean2polar, fx_euclidean2polar(1)
42676#@gui : Center (%) = point(50,50,0,1)
42677#@gui : Stretch Factor = float(1,0.1,10)
42678#@gui : Boundary = choice(1,"Transparent","Nearest","Periodic","Mirror")
42679#@gui : Inverse Transform = bool(0)
42680#@gui : sep = separator()
42681#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42682fx_euclidean2polar :
42683  if !$4 to_a fi
42684  if $5 polar2euclidean $1%,$2%,$3,$4 else euclidean2polar $1%,$2%,$3,$4 fi
42685
42686#@gui Fish-Eye : fisheye, fisheye(1)
42687#@gui : Center (%) = point(50,50,0,1,255)
42688#@gui : Radius = float(70,0,100)
42689#@gui : Amplitude = float(1,0,2)
42690#@gui : sep = separator()
42691#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42692
42693#@gui Flower : fx_flower, fx_flower_preview(1)
42694#@gui : Center (%) = point(50,50,0,1)
42695#@gui : Amplitude / Angle = point(75,50,0,1)
42696#@gui : Petals = int(6,2,20)
42697#@gui : Offset (%) = float(0,0,100)
42698#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42699#@gui : sep = separator()
42700#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42701fx_flower :
42702  if !$7 to_a fi
42703  amplitude,angle={dx=$3-$1;dy=$4-$2;[norm(dx,dy),-atan2(dy,dx)*180/pi]}
42704  flower $amplitude,$5,$6%,$angle,$1%,$2%,$7
42705
42706fx_flower_preview :
42707  fx_flower $*
42708  line $1%,$2%,$3%,$4%,1,0xF0F0F0F0,0
42709  line $1%,$2%,$3%,$4%,1,0x0F0F0F0F,255
42710
42711#@gui Kaleidoscope [Blended] : fx_rotoidoscope, fx_rotoidoscope(1)
42712#@gui : Center (%) = point(50,50)
42713#@gui : Angular Tiles = int(10,1,72)
42714#@gui : Smoothness = float(0.5,0,5)
42715#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42716#@gui : sep = separator()
42717#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42718fx_rotoidoscope :
42719  if !$5 to_a fi
42720  rotoidoscope $1%,$2%,$3,$4%,$5
42721
42722#@gui Kaleidoscope [Polar] : fx_kaleidoscope, fx_kaleidoscope(1)
42723#@gui : Center (%) = point(50,50)
42724#@gui : X-Offset (%) = float(0,0,100)
42725#@gui : Y-Offset (%) = float(0,0,100)
42726#@gui : Radius Cut = float(100,0,100)
42727#@gui : Angle Cut = float(10,0,100)
42728#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42729#@gui : sep = separator()
42730#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42731fx_kaleidoscope :
42732  if !$7 to_a fi
42733  shift $3%,$4%,0,0,2 kaleidoscope $1%,$2%,$5,$6,$7
42734
42735#@gui Kaleidoscope [Symmetry] : fx_symmetrizoscope, fx_symmetrizoscope(1)
42736#@gui : Iterations = int(4,1,32)
42737#@gui : Angle = float(0,0,360)
42738#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42739#@gui : Symmetry Sides = choice("Backward","Forward","Swap")
42740#@gui : sep = separator()
42741#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/07/01</i>.</small>")
42742fx_symmetrizoscope :
42743  if !$3 to_a fi
42744  repeat $1
42745    ang={$2+180*$>/max(1,$1-1)}
42746    symmetrize 50%,50%,$ang,$3,0,{if($4!=2,$4,$>%2)}
42747  done
42748
42749#@gui Morph [Interactive] : fx_morph_interactive, fx_morph_interactive_preview
42750#@gui : Number of Frames = int(16,3,1024)
42751#@gui : Preview Precision = choice{2,"Coarsest (faster)","Coarse","Normal","Fine","Finest (slower)"}
42752#@gui : Keypoints = value(-1)
42753#@gui : sep = separator()
42754#@gui : note = note{"<b>Instructions:</b>"}
42755#@gui : note = note{"
42756#@gui : Use mouse buttons to add/move/remove correspondence keypoints over the interactive window that will appear,
42757#@gui : in order to create the morphing.\n\n
42758#@gui : <span color="#EE5500"><b>Source/target window:</b></span>\n\n
42759#@gui : - <b>Left mouse button</b>: Add new keypoint on current image and move it on the other one.\n
42760#@gui : - <b>Right mouse button</b>: Add/move keypoint on current image.\n
42761#@gui : - Key <b>DELETE</b> or <b>middle mouse button</b>: Delete keypoint.\n
42762#@gui : - Key <b>SPACE</b> or <b>mouse wheel</b>: Toggle source/target.\n\n
42763#@gui : <span color="#EE5500"><b>In-between window:</b></span>\n\n
42764#@gui : - <b>Mouse wheel</b>: Change morphing time, from 0 to 1.\n
42765#@gui : - <b>Left mouse button</b>: Reset morphing time to 0.5.\n\n
42766#@gui : <span color="#EE5500"><b>Both windows:</b></span>\n\n
42767#@gui : - Key <b>TAB</b>: Change keypoint radius.\n
42768#@gui : - Key <b>ENTER</b>: Play/stop in-between animation.\n
42769#@gui : - Key <b>R</b>: Reset keypoints.\n
42770#@gui : - Key <b>K</b>: Show/hide keypoints.\n
42771#@gui : - Keys <b>ESC</b> or <b>Q</b>: Process fullres and exit.
42772#@gui : "}
42773#@gui : sep = separator()
42774#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/04/16</i>.</small>")
42775fx_morph_interactive :
42776  if [$3][0]!=-1 __x_morph_keypoints=$3 fi
42777  rv
42778  x_morph $1,$2
42779  repeat $! gui_set_layer_name[$>] "Morphing ""#"$> gui_set_layer_pos[$>] 0,0 done
42780  rv
42781  u "{$1}{$2}{"$__x_morph_keypoints"}"
42782
42783fx_morph_interactive_preview :
42784  if $!<2 gui_warning_preview "This filter requires at least two input layers!" return fi
42785  rr2d ${-max_wh},0,3 + n 0,255
42786  if [$3][0]!=-1 gui_warning_preview "No preview available\n\nKeypoints from previous\nrun have been saved"
42787  else gui_warning_preview "No preview available"
42788  fi
42789
42790#@gui Perspective : fx_warp_perspective, fx_warp_perspective(1)
42791#@gui : X-Angle = float(1.73,-4,4)
42792#@gui : Y-Angle = float(0,-4,4)
42793#@gui : Zoom = float(1,0.1,4)
42794#@gui : Center (%) = point(50,50,0,1,255)
42795#@gui : X-Offset = float(0,0,100)
42796#@gui : Y-Offset = float(0,0,100)
42797#@gui : Boundary = choice(2,"Transparent","Nearest","Periodic","Mirror")
42798#@gui : sep = separator()
42799#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42800fx_warp_perspective :
42801  if !$8 to_a fi
42802  shift $6%,$7%,0,0,2 warp_perspective $1,$2,$3,$4,$5,$8
42803
42804#@gui Polar Transform : fx_transform_polar, fx_transform_polar(1)
42805#@gui : Preset = choice("Custom Transform","Inverse Radius","Swap Radius / Angle")
42806#@gui : Center (%) = point(50,50,0,1)
42807#@gui : Radius = text{"r + R/10*cos(a*5)"}
42808#@gui : Angle = text{"a"}
42809#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42810#@gui : sep = separator()
42811#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42812fx_transform_polar :
42813  if !$6 to_a fi
42814  if $1==0
42815    transform_polar "$4","$5",$2%,$3%,$6
42816  elif $1==1
42817    transform_polar R-r,a,$2%,$3%,$6
42818  else
42819    transform_polar a*R/(2*pi),r*2*pi/R,$2%,$3%,$6
42820  fi
42821
42822#@gui Quadrangle : fx_quadrangle, fx_quadrangle_preview(1)
42823#@gui : Top-Left Vertex (%) = point(5,5,0,1,255,0,0)
42824#@gui : Top-Right Vertex (%) = point(95,25,0,1,0,255,0)
42825#@gui : Bottom-Right Vertex (%) = point(60,95,0,1,64,128,255)
42826#@gui : Bottom-Left Vertex (%) = point(40,95,0,1,255,255,0)
42827#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
42828#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
42829#@gui : Preview Type = choice(1,"Input","Output","Both")
42830#@gui : sep = separator()
42831#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/10/11</i>.</small>")
42832fx_quadrangle :
42833  at_quadrangle $1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,${9-10}
42834
42835fx_quadrangle_preview :
42836  repeat $! l[$>]
42837    if !$10 to_a fi
42838    if $11 +fx_quadrangle $* rr2d. {0,[w,h]},2,3 fi
42839    polygon[{$11==1?1:0}] 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,0.25,255
42840    if $11>=2
42841      circle[0] $1%,$2%,4,1,0 circle[0] $1%,$2%,3,1,255,0,0
42842      circle[0] $3%,$4%,4,1,0 circle[0] $3%,$4%,3,1,0,255,0
42843      circle[0] $5%,$6%,4,1,0 circle[0] $5%,$6%,3,1,64,128,255
42844      circle[0] $7%,$8%,4,1,0 circle[0] $7%,$8%,3,1,255,255,0
42845    elif $11>0
42846      rm[0]
42847    fi
42848    if $!==2
42849      drgba to[0] Quadrangle to[1] Result frame 1,1,0
42850      +a x a[0,1] y rr2d ${-gui_preview_wh},0,3
42851      k[{max(w#0,h#0)>max(w#1,h#1)?0:1}]
42852    fi
42853  endl done
42854
42855#@gui Raindrops : raindrops, raindrops(0)
42856#@gui : Amplitude = float(80,0,300)
42857#@gui : Density = float(0.1,0,1)
42858#@gui : Wavelength = float(1,0,2)
42859#@gui : Merging Steps = int(0,0,20)
42860#@gui : sep = separator()
42861#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/28/11</i>.</small>")
42862
42863#@gui Random : deform, deform(0)
42864#@gui : Amplitude = float(10,0,100)
42865#@gui : sep = separator()
42866#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42867
42868#@gui Ripple : ripple, ripple(0)
42869#@gui : Amplitude = float(10,0,100)
42870#@gui : Bandwidth = float(20,1,300)
42871#@gui : Shape = choice(2,"Bloc","Triangle","Sine","Sine+","Random")
42872#@gui : Angle = float(0,0,360)
42873#@gui : Offset = float(0,0,500)
42874#@gui : sep = separator()
42875#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/23/08</i>.</small>")
42876
42877#@gui Reflection : fx_reflect, fx_reflect(1)
42878#@gui : Height = float(50,0,100)
42879#@gui : Attenuation = float(1,0.1,4)
42880#@gui : Color = color(110,160,190,64)
42881#@gui : Waves Amplitude = float(0,0,100)
42882#@gui : Waves Smoothness = float(1.5,0,4)
42883#@gui : X-Angle = float(0,-10,10)
42884#@gui : Y-Angle = float(-3.30,-10,10)
42885#@gui : Focale = float(7,0,10)
42886#@gui : Zoom = float(1.5,1,5)
42887#@gui : sep = separator()
42888#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
42889fx_reflect :
42890  repeat $!
42891    to_rgba. +rows. {100-$1}%,100% mirror. y water. $7,$8
42892    s. c
42893    f[-4] "(i*(255-$6) + $6*$3)/255"
42894    f... "(i*(255-$6) + $6*$4)/255"
42895    f.. "(i*(255-$6) + $6*$5)/255" a[-4--1] c
42896    *. '(h^$2-y^$2)/h^$2' a[-2,-1] y
42897    100%,100%,100%,1,$11*$12*(x/w-0.5)
42898    100%,100%,100%,1,$11*$12*(y/h-0.5)
42899    100%,100%,100%,1,"$10*(x/w-0.5) + $9*(y/h-0.5) + $11"
42900    /... . +... 0.5 *... {-3,w}
42901    /[-2,-1] +. 0.5 *. {h}
42902    a[-2,-1] c warp.. .,0,1,0 rm.
42903  mv. 0 done
42904  autocrop 0,0,0,0
42905
42906#@gui Seamcarve : fx_seamcarve, fx_seamcarve_preview(1)
42907#@gui : Width (%) = float(85,0,200)
42908#@gui : Height (%) = float(100,0,200)
42909#@gui : Maximal Seams per Iteration (%) = float(15,0,100)
42910#@gui : Use Top Layer as a Priority Mask = bool(0)
42911#@gui : Antialiasing = bool(1)
42912#@gui : sep = separator()
42913#@gui : note = note{"<small><b>Note:</b>
42914#@gui : You can define a transparent top layer that will help the seam-carving algorithm to preserve or force
42915#@gui : removing image structures:\n
42916#@gui : \n  - Draw areas in <i>red</i> to force removing them.
42917#@gui : \n  - Draw areas in <i>green</i> to preserve them.
42918#@gui : \n  - Don't forget also to set the <i>Input layers...</i> parameter to input both layers to the filter.
42919#@gui : </small>"}
42920#@gui : sep = separator()
42921#@gui : note = note("<small>Authors: <i>Garagecoder</i> and <i>David Tschumperlé</i>.
42922#@gui :       Latest Update: <i>2014/02/06</i>.</small>")
42923fx_seamcarve :
42924  if $4
42925    if $!<2 error "Priority mask (top layer) is missing!" fi
42926    _fx_seamcarve
42927  fi
42928  seamcarve $1%,$2%,$4,$5,$3%
42929  if $4 repeat $! channels[$>] 0,{$>,s-2} done fi
42930  c 0,255
42931
42932fx_seamcarve_preview :
42933  if $4
42934    if $!<2 to_rgb to "Priority mask (top layer) is missing!",5,5,18,2 return fi
42935    _fx_seamcarve
42936  fi
42937  repeat $! l[$>]
42938    w={w} h={h}
42939    seamcarve $1%,$2%,$4,$5,{max($3,10)}%
42940    if $4 channels 0,{s-2} fi
42941    to_rgba r $w,$h,1,100%,0,0,0.5,0.5
42942  endl done
42943  c 0,255
42944
42945_fx_seamcarve :
42946  mv[0] $!
42947  l.
42948    s c k[0,1]
42949    >[1] [0] !=[0] 0 -[0] [1] *[0] -1 + * 256
42950  endl
42951  repeat $!-1 a[$>] .,c done rm.
42952
42953#@gui Sphere : fx_map_sphere, fx_map_sphere_preview(1)
42954#@gui : Width = _int(512,1,4096)
42955#@gui : Height = _int(512,1,4096)
42956#@gui : Radius = float(90,0,400)
42957#@gui : Dilation = float(0.5,0,1)
42958#@gui : Angle = float(0,-50,50)
42959#@gui : Border Smoothness = float(0,0,200)
42960#@gui : Border Width = float(20,0,100)
42961#@gui : Orientation = choice("0 deg.","90 deg.","180 deg.","270 deg.")
42962#@gui : Background = choice("Transparent","Mean Color")
42963#@gui : Fading = float(0,0,100)
42964#@gui : Fading Shape = float(0.5,0,3)
42965#@gui : sep = separator()
42966#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/07/11</i>.</small>")
42967fx_map_sphere :
42968  rotate {$8*90}
42969  if $6
42970    repeat $!
42971      shift. {round(w/2)},0,0,0,2 +columns. {(1-$7/100)*w/2},{(1+$7/100)*w/2}
42972      100% gaussian. {0.1*w},{h},0 100% 100% a[-3--1] c r. ..,..,1,3
42973      smooth.. .,$6,5,0 rm.
42974      j.. .,{(1-$7/100)*{-2,w}/2} rm. shift. -{round(w/2)},0,0,0,2
42975    mv. 0 done
42976  fi
42977  shift $5%,0,0,0,2 to_rgba
42978  if $9
42979    repeat $!
42980      +rows[$>] 0 r. 1,1,1,4,2 RGBA$>={^}
42981      r. [$>],[$>],1,4 -[$>,-1]
42982    done
42983  fi
42984  map_sphere $1,$2,$3,$4,$10,$11
42985  if $9
42986    repeat $!
42987      (${RGBA$>}) y. c r. [$>],[$>],1,4 +[$>,-1]
42988    done
42989  fi
42990
42991fx_map_sphere_preview :
42992  fx_map_sphere {w},{h},${3--1}
42993
42994#@gui Spherize : fx_spherize, fx_spherize_preview(1)
42995#@gui : Radius (%) = float(50,0,300)
42996#@gui : Strength = float(1,-10,10)
42997#@gui : Smoothness (%) = float(0,0,4)
42998#@gui : Center (%) = point(50,50,0,1,255,255,255,170,10)
42999#@gui : Ratio = float(0,-2,2)
43000#@gui : Angle = float(0,-90,90)
43001#@gui : Interpolation = choice(2,"Nearest Neighbor","Linear","Cubic")
43002#@gui : Preview Grid = bool(0)
43003#@gui : sep = separator()
43004#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/10/03</i>.</small>")
43005fx_spherize :
43006  ratio={10^$6}
43007  spherize $1%,$2,$3%,$4%,$5%,$ratio,$7,$8
43008  cut 0,255
43009
43010fx_spherize_preview :
43011  cx,cy=${4,5}
43012  if $9 grid 5%,5%,50%,50%,0.6,255 fi
43013  fx_spherize ${1-3},$cx,$cy,${6--1}
43014
43015#@gui Square to Circle : fx_square_circle, fx_square_circle
43016#@gui : Mode = choice(0,"Square to Circle","Circle to Square")
43017#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
43018#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
43019#@gui : sep = separator()
43020#@gui : X-Factor (%) = float(0,-100,100)
43021#@gui : Y-Factor (%) = float(0,-100,100)
43022#@gui : X-Offset (%) = float(0,-300,300)
43023#@gui : Y-Offset (%) = float(0,-300,300)
43024#@gui : sep = separator()
43025#@gui : note = note("<small>This filter implements the mapping functions described in this page,
43026#@gui : by <i>C. Fong</i>:</small>")
43027#@gui : url = link("http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html")
43028#@gui : sep = separator()
43029#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/10/30</i>.</small>")
43030fx_square_circle :
43031  mode,interp,boundary,factx,facty,offx,offy=${1-7}
43032  if !$boundary to_a fi
43033  base="const interpolation = "$interp";
43034        const boundary = "$boundary";
43035        const offx = "$offx"%;
43036        const offy = "$offy"%;
43037        const factx = 10^-("$factx"%);
43038        const facty = 10^-("$facty"%);
43039        const w2 = int(w/2);
43040        const h2 = int(h/2);"
43041  if !$mode # Square to circle
43042    f $base"
43043       const tst = 2*sqrt(2);
43044       U = (2*x/(w-1) - 1)*factx + offx;
43045       V = (2*y/(h-1) - 1)*facty + offy;
43046       U2 = U^2;
43047       V2 = V^2;
43048       U2mV2 = U2 - V2;
43049       X = 0.5*(sqrt(max(0,2 + tst*U + U2mV2)) - sqrt(max(0,2 - tst*U + U2mV2)));
43050       Y = 0.5*(sqrt(max(0,2 + tst*V - U2mV2)) - sqrt(max(0,2 - tst*V - U2mV2)));
43051       (X+=1)*=w2 - 0.5;
43052       (Y+=1)*=h2 - 0.5;
43053       I(X,Y)"
43054  else # Circle to square
43055    f $base"
43056       X = (2*x/(w-1) - 1)*factx + offx;
43057       Y = (2*y/(h-1) - 1)*facty + offy;
43058       U = X*sqrt(abs(1 - 0.5*Y^2));
43059       V = Y*sqrt(abs(1 - 0.5*X^2));
43060       (U+=1)*=w2 - 0.5;
43061       (V+=1)*=h2 - 0.5;
43062       I(U,V)"
43063  fi
43064
43065#@gui Stereographic Projection : fx_project_stereographic, fx_project_stereographic_preview(1)
43066#@gui : Transform = choice("Direct","Inverse")
43067#@gui : Center (%) = point(50,50,0,1,255,255,255,170)
43068#@gui : Radius / Angle = point(50,75,0,1,255,0,255,170)
43069#@gui : Horizon Leveling (deg) = float(0,-10,10)
43070#@gui : Left / Right Blur (%) = float(0,0,20)
43071#@gui : Dilation = float(0,-2,2)
43072#@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axis")
43073#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
43074#@gui : Last Center = value(50,50)
43075#@gui : sep = separator()
43076#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/07/04</i>.</small>")
43077fx_project_stereographic :
43078  is_inverse,centerx,centery,radangx,radangy,rechor,lrblur,dilation,mirror,boundary,ocenterx,ocentery=${1-11}
43079
43080  if $centerx!=$ocenterx" || "$centery!=$ocentery
43081    deltax,deltay={[$radangx,$radangy]-[$ocenterx,$ocentery]}
43082    radangx,radangy={[$centerx,$centery]+[$deltax,$deltay]}
43083  fi
43084  status=\{$is_inverse\}\{$centerx,$centery\}\{$radangx,$radangy\}\{$rechor\}\{$lrblur\}\
43085         \{$dilation\}\{$mirror\}\{$boundary\}\{$centerx,$centery\}
43086
43087  # So that preview line does not hide left/right frontier:
43088  nradangx,nradangy={[$centerx,$centery]+rot(-90°)*([$radangx,$radangy]-[$centerx,$centery])}
43089  init="const boundary = "$is_inverse?$boundary:2";
43090        const interpolation = 1;
43091        const dilation = 2^"$dilation";
43092        const centerx = "$centerx"%*(W-1);
43093        const centery = "$centery"%*(H-1);
43094        const radangx = "$nradangx"%*(W-1) - centerx;
43095        const radangy = "$nradangy"%*(H-1) - centery;
43096        const R = sqrt(radangx^2 + radangy^2);
43097        const theta0 = atan2(radangy,radangx);
43098        const pi2 = 2*pi;"
43099  m "_fx_project_stereographic_mirror : if !$""1 mirror y elif $""1==1 mirror xy elif $""1==3 mirror x fi"
43100  repeat $! l[$>]
43101    if !$boundary to_a fi
43102    if $rechor rotate $rechor,1,3 fi
43103    if $lrblur
43104      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%
43105      r.. .,.,1,1 j... .,0,0,0,0,1,.. k[0] shift {-round(w/2)},0,0,0,2
43106    fi
43107    if $is_inverse
43108      100%,50%,1,100%,"*
43109        const W = w#0;
43110        const H = h#0;
43111        "$init"
43112        theta = theta0 + x*pi2/w;
43113        phi = (y/h - 0.5)*pi;
43114        z = R*sin(phi);
43115        rho = ((R + z)/(R - z))^(0.5/dilation)*R;
43116        X = centerx + rho*cos(theta);
43117        Y = centery + rho*sin(theta);
43118        I(#0,X,Y)"
43119      _fx_project_stereographic_mirror $mirror
43120    else
43121      _fx_project_stereographic_mirror $mirror
43122      {u=0$_is_preview?min(w,h):max(w,h);[u,u,1,s]},"*
43123        const W = w;
43124        const H = h;
43125        "$init"
43126        X = x - centerx;
43127        Y = y - centery;
43128        theta = atan2(Y,X);
43129        beta = ((X^2 + Y^2)/R^2)^dilation;
43130        z = R*(beta - 1)/(beta + 1);
43131        phi = asin(z/R);
43132        theta = ((theta - theta0)*w#0/pi2)%w#0;
43133        phi = (h#0*(phi/pi + 0.5))%h#0;
43134        I(#0,theta,phi)"
43135    fi
43136  k. endl done um _fx_project_stereographic
43137
43138  if 0$_is_preview
43139    line $centerx%,$centery%,$radangx%,$radangy%,0.75,0xF0F0F0F0,255,255,255,255
43140    line $centerx%,$centery%,$radangx%,$radangy%,0.75,0x0F0F0F0F,0,0,0,255
43141  fi
43142  u $status
43143
43144fx_project_stereographic_preview :
43145  _is_preview=1
43146  fx_project_stereographic $"*"
43147
43148#@gui Symmetrize : fx_symmetrize, fx_symmetrize_preview(1)
43149#@gui : Point 1 = point(50,50,0,1,0,255,0,170,10)
43150#@gui : Point 2 = point(50,75,-1,1,255,255,0,170,10)
43151#@gui : Angle = float(0,-180,180)
43152#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
43153#@gui : Type = choice("Symmetry","Antisymmetry")
43154#@gui : Swap Sides = bool(0)
43155#@gui : sep = separator()
43156#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/06/11</i>.</small>")
43157fx_symmetrize :
43158  if !$6 to_a fi
43159  angle={isnan($3)?$5:atan2($4-$2,$3-$1)*180/pi}
43160  symmetrize $1%,$2%,$angle,${6-8}
43161
43162fx_symmetrize_preview :
43163  fx_symmetrize $*
43164  rr2d ${-gui_preview_wh},0,1
43165  u,v={angle=isnan($3)?$5*pi/180:atan2($4-$2,$3-$1);[cos(angle),sin(angle)]}
43166  repeat $! l[$>]
43167    x0,y0,x1,y1={V=[$u,$v];([${1,2},${1,2}]+10000*[V,-V])*([w,h,w,h]-1)%}
43168    line $x0,$y0,$x1,$y1,1,0x0F0F0F0F,0,0,0,255
43169    line $x0,$y0,$x1,$y1,1,0xF0F0F0F0,255
43170  endl done
43171
43172#@gui Textured Glass : fx_textured_glass, fx_textured_glass_preview(0)
43173#@gui : X-Amplitude = float(40,0,400)
43174#@gui : Y-Amplitude = float(40,0,400)
43175#@gui : X-Smoothness = float(1,0,5)
43176#@gui : Y-Smoothness = float(1,0,5)
43177#@gui : Edge Attenuation = float(0,0,1)
43178#@gui : Edge Influence = float(2,0,10)
43179#@gui : Noise Scale = int(0,0,16)
43180#@gui : sep = separator()
43181#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43182#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43183#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43184#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43185#@gui : sep = separator()
43186#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/21/11</i>.</small>")
43187fx_textured_glass :
43188  repeat $! l[$>]
43189    100%,100%,1,1
43190    if $7 plasma. 1,1,$7 else rand. 0,1 fi
43191    g. xy
43192    if $5
43193      +gradient_norm... +. 1 b. $6 ^. -$5
43194      *... . *[-2,-1]
43195    fi
43196    blur_xy[-2,-1] $3,$4
43197    *.. {-2,$1/max(abs(im),abs(iM))}
43198    *. {$2/max(abs(im),abs(iM))}
43199    a[-2,-1] c
43200    warp.. .,1,1 rm.
43201  endl done
43202
43203fx_textured_glass_preview :
43204  gui_split_preview "fx_textured_glass $*",${-3--1}
43205
43206#@gui Twirl : fx_twirl, fx_twirl(1)
43207#@gui : Amplitude = float(1,-5,5)
43208#@gui : Center (%) = point(50,50,0,1)
43209#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
43210#@gui : sep = separator()
43211#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43212fx_twirl :
43213  if !$4 to_a fi
43214  twirl $1,$2%,$3%,$4
43215
43216#@gui Warp [Interactive] : fx_warp_interactive, fx_warp_interactive_preview(1)
43217#@gui : Preview Precision = choice{1,"Coarsest (faster)","Coarse","Normal","Fine","Finest (slower)"}
43218#@gui : sep = separator()
43219#@gui : note = note{"<small><b>Pre-defined keypoints</b></small>"}
43220#@gui : Regular Grid = int(2,2,10)
43221#@gui : Contours = int(0,0,32)
43222#@gui : Keypoints = value(-1)
43223#@gui : sep = separator()
43224#@gui : note = note{"<b>Instructions:</b>"}
43225#@gui : note = note{"
43226#@gui : Use mouse to add/move/delete keypoints over the interactive window that will appear,
43227#@gui : in order to create the deformation map.\n\n
43228#@gui : - <b>Left mouse button</b>: Add and move keypoint.\n
43229#@gui : - <b>Right mouse button</b>: Delete keypoint.\n
43230#@gui : - Key <b>SPACE</b> or <b>middle mouse button</b>: Show/hide keypoints.\n
43231#@gui : - Key <b>TAB</b>: Change keypoint radius.\n
43232#@gui : - Key <b>SHIFT</b>: Toggle to original image.\n
43233#@gui : - Key <b>R</b>: Reset keypoints.\n
43234#@gui : - Keys <b>ESC</b>, <b>ENTER</b> or <b>Q</b>: Process fullres and exit.
43235#@gui : "}
43236#@gui : sep = separator()
43237#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/04/08</i>.</small>")
43238fx_warp_interactive :
43239  if [$4][0]!=-1 __x_warp_keypoints=$4 fi
43240  x_warp $2,$2,$3,$1
43241  u "{$1}{$2}{$3}{"$__x_warp_keypoints"}"
43242
43243fx_warp_interactive_preview :
43244  if [$4][0]!=-1 __x_warp_keypoints=$4 fi
43245  repeat $! l[$>]
43246    rr2d $_preview_width,$_preview_height,0,3 to_color drgba
43247    if narg($__x_warp_keypoints) ($__x_warp_keypoints) r. 1,{w/4},1,4,-1
43248    else
43249
43250      # Keypoints located on regular grid.
43251      nbp,nbq=$2,$2
43252      1,{$nbp*$nbq},1,4,"const nbp = "$nbp"; const nbq = "$nbq";
43253        p = y%nbp;
43254        q = int(y/nbp);
43255        x = p*100/(nbp - 1);
43256        y = q*100/(nbq - 1);
43257        [ x,y,x,y ]"
43258
43259      # Keypoints located on contours.
43260      nbc=$3
43261      if $nbc>0
43262        +b[0] 0.5 gradient_norm. sqrt. {round([w,h]/4)} gaussian. 20%
43263        1,$nbc,1,4,">
43264          begin(ref(crop(#-1),gauss));
43265          st = stats(#-2);
43266          iM = st[1];
43267          xM = st[8];
43268          yM = st[9];
43269          img = vector(#w#-1*h#-1,-iM);
43270          draw(#-2,img,xM - w#-1/2,yM - h#-1/2,0,0,w#-1,h#-1,1,1,-1,gauss);
43271          nxyM = [ xM,yM ]*100/([w#-2,h#-2]-1);
43272          [ nxyM,nxyM ]"
43273        rm[-3,-2]
43274        a[-2,-1] y
43275      fi
43276    fi
43277
43278    +_x_warp_rbf. {0,round([w,h]/4)} *. 4 r. [0],[0],1,100%,3 +. '[x,y]' warp[0] .,0,1,3 rm.
43279
43280    eval. "*
43281      begin(
43282        col1 = [ 64,200,255 ];
43283        const radius1 = 3;
43284        const radius2 = radius1 + 2;
43285        fact = ([ w#0,h#0 ] - 1)%
43286      );
43287      X = (I)[0,2]*fact;
43288      ellipse(#0,X,radius2,radius2,0,1,0);
43289      ellipse(#0,X,radius1,radius1,0,1,col1); I"
43290    rm.
43291
43292    if narg($__x_warp_keypoints)
43293      0 t. "Keypoints from previous\nrun have been saved",0,0,24,1,255
43294      frame. 5,5,0 +dilate_circ. 5 a[-2,-1] c blend alpha
43295    fi
43296  endl done
43297
43298#@gui Water : water, water(0)
43299#@gui : Amplitude = float(30,0,300)
43300#@gui : Smoothness = float(1.5,0,4)
43301#@gui : Angle = float(45,0,180)
43302#@gui : sep = separator()
43303#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/07/10</i>.</small>")
43304
43305#@gui Wave : wave, wave(1)
43306#@gui : Amplitude = float(10,0,30)
43307#@gui : Frequency = float(0.4,0,2)
43308#@gui : Center (%) = point(50,50,0,1,255,255,255,170,10)
43309#@gui : sep = separator()
43310#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43311
43312#@gui Wind : fx_wind, fx_wind_preview(0)
43313#@gui : Amplitude = int(20,0,500)
43314#@gui : Angle = float(0,0,360)
43315#@gui : Attenuation = float(0.7,0,1)
43316#@gui : Threshold = float(20,0,100)
43317#@gui : Mode = choice(1,"Darker","Brighter")
43318#@gui : sep = separator()
43319#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43320#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43321#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43322#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43323#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43324#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43325#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43326#@gui : Value Action = choice("None","Cut","Normalize")
43327#@gui : sep = separator()
43328#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43329#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43330#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43331#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43332#@gui : sep = separator()
43333#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/13/07</i>.</small>")
43334fx_wind :
43335  if !$5 negate fi
43336  ac "wind ${1-4}",$6,$7
43337  if !$5 negate fi
43338
43339fx_wind_preview :
43340  gui_split_preview "fx_wind $*",${-3--1}
43341
43342#@gui Zoom : fx_zoom, fx_zoom(1)
43343#@gui : Factor = float(2,0.01,10)
43344#@gui : Center (%) = point(50,50,0,1,255)
43345#@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror")
43346#@gui : sep = separator()
43347#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43348fx_zoom :
43349  if !$4" && "$1<1 to_a fi
43350  zoom $1,{$2%},{$3%},0,$4
43351
43352
43353#@gui ____<b>Degradations</b>
43354#-----------------------------
43355
43356#@gui Add Grain : fx_simulate_grain, fx_simulate_grain_preview(0)
43357#@gui : Preset = choice{"Orwo NP20-GDR","Kodak TMAX 400","Kodak TMAX 3200","Kodak TRI-X 1600","Unknown"}
43358#@gui : Blend Mode = choice(1,"Alpha","Grain Merge","Hard Light","Overlay","Soft Light","Grain Only")
43359#@gui : Opacity = float(0.2,0,1)
43360#@gui : Scale = float(100,30,800)
43361#@gui : Sharpness = float(0,0,512)
43362#@gui : Colored Grain = bool()
43363#@gui : sep = separator()
43364#@gui : Brightness (%) = float(0,-100,100)
43365#@gui : Contrast (%) = float(0,-100,100)
43366#@gui : Gamma (%) = float(0,-100,100)
43367#@gui : Hue (%) = float(0,-100,100)
43368#@gui : Saturation (%) = float(0,-100,100)
43369#@gui : sep = separator()
43370#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43371#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43372#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43373#@gui : Preview Grain Alone = bool()
43374#@gui : sep = separator()
43375#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/02/08</i>.</small>")
43376fx_simulate_grain :
43377  __fx_simulate_grain ${arg\ {1+$1},${-_fx_simulate_grain}},${2-11},0,0
43378
43379_fx_simulate_grain :
43380  u orwo_np20,kodak_tmax400,kodak_tmax3200,kodak_trix1600,unknown
43381
43382fx_simulate_grain_preview :
43383  gui_split_preview "_fx_simulate_grain_preview $*",$-2
43384
43385_fx_simulate_grain_preview :
43386  __fx_simulate_grain ${arg\ {1+$1},${-_fx_simulate_grain}},${2-13}
43387
43388__fx_simulate_grain :
43389  bm0=alpha bm1=grainmerge bm2=hardlight bm3=overlay bm4=softlight bm5=alpha
43390  input_cached data_film_presets/grain_$1.cimgz
43391  r. $4%,$4%,1,1,6
43392  if $4>100 b. 1 fi
43393  sharpen. $5 c. 0,255
43394
43395  repeat $!-1 l[$>,-1] split_opacity[0]
43396    +syntexturize. {0,[w,h]}
43397    if $6 +syntexturize.. {w},{h} +syntexturize... {w},{h} a[-3--1] c fi
43398    c. 0,255
43399    adjust_colors. ${7-11}
43400    if $13 k[0,-1] rv
43401    else blend[0,-1] ${bm$2},{if($2<=4,$3,1)}
43402    fi
43403  a[^-1] c endl done rm.
43404
43405#@gui Blur [Angular] : fx_blur_angular, fx_blur_angular_preview(1)
43406#@gui : Amplitude (%) = float(2,0,20)
43407#@gui : Center (%) = point(50,50,0,1)
43408#@gui : Sharpness = float(0,0,500)
43409#@gui : Preview Guides = bool(1)
43410#@gui : sep = separator()
43411#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43412#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43413#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43414#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43415#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43416#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43417#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43418#@gui : Value Action = choice("None","Cut","Normalize")
43419#@gui : sep = separator()
43420#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/16/01</i>.</small>")
43421fx_blur_angular :
43422  ac "blur_angular $1%,$2%,$3% sharpen $4",$6,$7
43423
43424fx_blur_angular_preview :
43425  fx_blur_angular $*
43426  if $5
43427    line 0,$3%,100%,$3%,0.5,0xF0F0F0F0,255 line 0,$3%,100%,$3%,0.5,0x0F0F0F0F,0
43428    line $2%,0,$2%,100%,0.5,0xF0F0F0F0,255 line $2%,0,$2%,100%,0.5,0x0F0F0F0F,0
43429  fi
43430
43431#@gui Blur [Bloom] : fx_blur_bloom, fx_blur_bloom_preview(0)
43432#@gui : Amplitude = float(1,0,10)
43433#@gui : Ratio = float(2,0,5)
43434#@gui : Iterations = int(5,0,100)
43435#@gui : Operator = choice("Add","Max","Min")
43436#@gui : Kernel = choice(1,"Deriche","Gaussian","Box","Triangle","Quadratic")
43437#@gui : Normalize Scales = bool(0)
43438#@gui : Anisotropy = float(0,0,1)
43439#@gui : Angle = float(0,-180,180)
43440#@gui : note = note("Parameter <i>Angle</i> is only active when <i>Anisotropy</i>&gt;0")
43441#@gui : sep = separator()
43442#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43443#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43444#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43445#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43446#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43447#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43448#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43449#@gui : sep = separator()
43450#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43451#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43452#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43453#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43454#@gui : sep = separator()
43455#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/03/02</i>.</small>")
43456fx_blur_bloom :
43457  op=${"arg 1+$4,+,max,min"}
43458  if !$7 ac "blur_bloom ${1-3},"$op",${5-6},xy",$9
43459  else
43460    wh={[w,h]}
43461    rotate $8,2,1
43462    ac "blur_bloom ${1-3},"$op",${5-6},x blur_bloom {$1*(1-$7)},${2-3},"$op",${5-6},y",$9
43463    rotate {-$8},2,1
43464    r $wh,1,100%,0,0,0.5,0.5 c 0,255
43465  fi
43466
43467fx_blur_bloom_preview :
43468  gui_split_preview "fx_blur_bloom $*",${-3--1}
43469
43470#@gui Blur [Depth-of-Field] : fx_blur_dof, fx_blur_dof_preview(1)
43471#@gui : Blur Amplitude = float(3,0,20)
43472#@gui : Blur Precision = int(16,2,64)
43473#@gui : Depth-of-Field Type = choice{"Gaussian","User-Defined (Bottom Layer)"}
43474#@gui : Invert Blur = bool(0)
43475#@gui : sep = separator()
43476#@gui : note = note("<small><b>Gaussian depth-of-field:</b></small>")
43477#@gui : Center (%) = point(50,50,0,0,255)
43478#@gui : First Radius = float(30,0,200)
43479#@gui : Second Radius = float(30,0,200)
43480#@gui : Angle = float(0,0,180)
43481#@gui : Sharpness = float(1,0,8)
43482#@gui : Preview Guides = bool(1)
43483#@gui : sep = separator()
43484#@gui : note = note("<small><b>User-defined depth-of-field:</b></small>")
43485#@gui : Gamma = float(0,-2,2)
43486#@gui : note = note("<small>You can specify your own depth-of-field image, as a <b>bottom layer</b> image
43487#@gui : whose luminance encodes the depth for each pixel.
43488#@gui : Don't forget to modify the <b>Input layers</b> combo-box to make this layer active for the filter.</small>")
43489#@gui : sep = separator()
43490#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/25/02</i>.</small>")
43491fx_blur_dof :
43492  _$0 ${1-10},0,$12
43493
43494fx_blur_dof_preview :
43495  _fx_blur_dof $*
43496
43497_fx_blur_dof :
43498  if !$3  # Gaussian DOF.
43499    repeat $! l[$>] if $11 drgba fi split_opacity l[0]
43500      rmax={(w*w+h*h)^0.5} R={$7*$rmax/100} r={$8*$rmax/100}
43501      t={$9*pi/180} u={cos($t)} v={sin($t)}
43502      l1={($rmax/(1e-8+$R))^2} l2={($rmax/(1e-8+$r))^2}
43503      a={$l1*($u)^2+$l2*($v)^2} b={$u*$v*($l1-$l2)} c={$l1*($v)^2+$l2*($u)^2}
43504      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)'
43505      -[1] 1 *[1] -$1 ms={im} Ms={iM}
43506
43507      if $11 # With preview of guides.
43508        +isoline3d[1] {0.1*$1} col3d. 255,255,0
43509        +isoline3d[1] {0.5*$1} col3d. 255,128,0
43510        +3d[-2--1]
43511        __fx_dof_blur[0,1] $2,$ms,$Ms,$4
43512        [0],[0],1,3 j3d. ..,0,0,0,1,1,0,0 rm..
43513        circle. $5%,$6%,3,1,255,255,255
43514        +compose_channels. + !=. 0 dilate. 3
43515        j[0] ..,0,0,0,0,0.5,.,1 rm[-2,-1]
43516      else __fx_dof_blur[0,1] $2,$ms,$Ms,$4 # Without preview.
43517      fi
43518    endl if $11 k[0] fi a c endl done
43519  elif $!>1 # User-defined DOF (as bottom layer).
43520    luminance. n. 0,1 ^. {10^$12}
43521    repeat $!-1 +r. {$>,w},{$>,h},1,1,3 l[$>,-1] split_opacity[0]
43522      __fx_dof_blur[0,-1] $2,0,$1,$4
43523    a c endl done rm.
43524  else drgba to "Depth-of-field (bottom layer) is missing !",2,2,13,2,1,255
43525  fi
43526
43527# Render DOF blur (generic)
43528# [0] = Input image
43529# [1] = continuous DOF field (with same size as [0]).
43530# $1 = nb quantization levels.
43531# $2 = minimal blur level.
43532# $3 = maximal blur level.
43533# $4 = invert blur.
43534__fx_dof_blur :
43535  n[1] 0,{$1-1} round[1]
43536  [0],[0],1,{0,s+1}
43537  s=0
43538  repeat $1
43539    +==[1] {if($4,$<,$>)} b. 2%
43540    j.. [0],0,0,0,0,-1,.,1
43541    j.. .,0,0,0,100%,-1
43542    rm.
43543     ns={$2+($3-$2)*($>+1)/($1-1)}
43544     b[0] {sqrt($ns^2-$s^2)}%
43545     s=$ns
43546  done
43547  s. c,{-s+1} /[-2,-1] rm[0,1]
43548
43549#@gui Blur [Gaussian] : fx_gaussian_blur, fx_gaussian_blur_preview(0)
43550#@gui : XY-Amplitude = float(3,0,20)
43551#@gui : X-Amplitude = float(0,0,20)
43552#@gui : Y-Amplitude = float(0,0,20)
43553#@gui : Boundary = choice(1,"Black","Nearest")
43554#@gui : sep = separator()
43555#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43556#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43557#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43558#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43559#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43560#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43561#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43562#@gui : Value Action = choice("None","Cut","Normalize")
43563#@gui : sep = separator()
43564#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43565#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43566#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43567#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43568#@gui : sep = separator()
43569#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43570_fx_gaussian_blur :
43571  b $1,$4
43572  if $2>0 repeat $! l. s y b $2,$4 a y endl mv. 0 done fi
43573  if $3>0 repeat $! l. s x b $3,$4 a x endl mv. 0 done fi
43574
43575fx_gaussian_blur :
43576  ac "_fx_gaussian_blur $1,$2,$3,$4",$5,$6
43577
43578fx_gaussian_blur_preview :
43579  gui_split_preview "fx_gaussian_blur $*",${-3--1}
43580
43581#@gui Blur [Glow] : fx_glow, fx_glow_preview(0)
43582#@gui : Amplitude = float(6,0,20)
43583#@gui : sep = separator()
43584#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43585#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43586#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43587#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43588#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43589#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43590#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43591#@gui : Value Action = choice("None","Cut","Normalize")
43592#@gui : sep = separator()
43593#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43594#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43595#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43596#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43597#@gui : sep = separator()
43598#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43599fx_glow :
43600  ac "glow $1",$2,$3
43601
43602fx_glow_preview :
43603  gui_split_preview "fx_glow $*",${-3--1}
43604
43605#@gui Blur [Linear] : fx_blur_linear, fx_blur_linear_preview(1)
43606#@gui : Tangent Radius = float(10,0,100)
43607#@gui : Orthogonal Radius = float(0.5,0,100)
43608#@gui : Angle = float(0,0,180)
43609#@gui : Sharpness = float(0,0,500)
43610#@gui : Boundary = choice(1,"Black","Nearest")
43611#@gui : sep = separator()
43612#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43613#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43614#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43615#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43616#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43617#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43618#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43619#@gui : Value Action = choice("None","Cut","Normalize")
43620#@gui : sep = separator()
43621#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43622#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43623#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43624#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43625#@gui : sep = separator()
43626#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
43627fx_blur_linear :
43628  ac "blur_linear $1,{$2*$1/100},$3,$5 sharpen $4",$6,$7
43629
43630fx_blur_linear_preview :
43631  gui_split_preview "fx_blur_linear $*",${-3--1}
43632
43633#@gui Blur [Multidirectional] : fx_blur_multidirectional, fx_blur_multidirectional_preview(0)
43634#@gui : Number of Orientations = int(5,1,16)
43635#@gui : Reference Angle (deg.) = float(0,0,360)
43636#@gui : Angle Range (deg.) = float(360,0,360)
43637#@gui : sep = separator()
43638#@gui : Smoothness = float(150,0,1024)
43639#@gui : Kernel type = choice(0,"Mono-Directional","Bi-Directional")
43640#@gui : Boundary conditions = choice(1,"Dirichlet","Neumann","Periodic","Mirror")
43641#@gui : Sharpness = float(0,0,1000)
43642#@gui : Blend Mode = choice{2,"Min","Max","Average","Edges-0.5 (beware: memory-consuming!)",
43643#@gui : "Edges-1 (beware: memory-consuming!)","Edges-2 (beware: memory-consuming!)",
43644#@gui : "Median (beware: memory-consuming!)"}
43645#@gui : Boost Contrast = float(2,0,32)
43646#@gui : sep = separator()
43647#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43648#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43649#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43650#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43651#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43652#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43653#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43654#@gui : sep = separator()
43655#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43656#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43657#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43658#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43659#@gui : sep = separator()
43660#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/09/11</i>.</small>")
43661_fx_blur_multidirectional :
43662  nb_orientations,\
43663  angle_ref,\
43664  angle_range,\
43665  smoothness,\
43666  kernel,\
43667  boundary_conditions,\
43668  sharpness,\
43669  blend_mode,\
43670  contrast=${1-9}
43671  repeat $! l[$>]
43672    # If no median blend, blend step by step in an accumulator to reduce memory usage.
43673    if $blend_mode<3 +f {$blend_mode?0:inf} fi
43674
43675    repeat $nb_orientations
43676      angle={$angle_ref+$angle_range*($>/$nb_orientations-0.5)}
43677
43678      # Kernel definition.
43679      $smoothness,1 gaussian. 20%,0.1
43680      if !$kernel f. "x>w/2?i:0" fi
43681      rotate. $angle,1 # /. {is}
43682
43683      # Blur and blend.
43684      +convolve_fft[0] .,$boundary_conditions rm.. n. 0,255 sharpen. $sharpness
43685      if $blend_mode<3 ${arg\ 1+$blend_mode,min,max,+}[1,-1] fi
43686    done
43687    rm[0]
43688
43689    if $blend_mode==6 blend_median
43690    elif $blend_mode>2 blend_edges {arg($blend_mode-2,0.5,1,2)}
43691    fi
43692
43693    n 0,255 ac "normalize_local "$contrast,hsl_l
43694  endl done
43695
43696fx_blur_multidirectional :
43697  ac "_fx_blur_multidirectional ${1-9}",$10
43698
43699fx_blur_multidirectional_preview :
43700  gui_split_preview "fx_blur_multidirectional $*",${-3--1}
43701
43702#@gui Blur [Radial] : fx_blur_radial, fx_blur_radial_preview(1)
43703#@gui : Amplitude = float(3,0,20)
43704#@gui : Center (%) = point(50,50,0,1)
43705#@gui : Sharpness = float(0,0,500)
43706#@gui : Preview Guides = bool(1)
43707#@gui : sep = separator()
43708#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43709#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43710#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43711#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43712#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43713#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43714#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43715#@gui : Value Action = choice("None","Cut","Normalize")
43716#@gui : sep = separator()
43717#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/16/01</i>.</small>")
43718fx_blur_radial :
43719  ac "blur_radial $1%,$2%,$3% sharpen $4",$6,$7
43720
43721fx_blur_radial_preview :
43722  fx_blur_radial $*
43723  if $5
43724    line 0,$3%,100%,$3%,0.5,0xF0F0F0F0,255 line 0,$3%,100%,$3%,0.5,0x0F0F0F0F,0
43725    line $2%,0,$2%,100%,0.5,0xF0F0F0F0,255 line $2%,0,$2%,100%,0.5,0x0F0F0F0F,0
43726  fi
43727
43728#@gui Chromatic Aberrations : fx_chromatic_aberrations, fx_chromatic_aberrations_preview(0)
43729#@gui : Primary Color = color(255,0,0)
43730#@gui : Deformation Type = choice("Shift","Radial","Angular","Random")
43731#@gui : X-Amplitude = float(2,-32,32)
43732#@gui : Y-Amplitude = float(2,-32,32)
43733#@gui : Smoothness = float(0,0,10)
43734#@gui : Attenuation Near Center (%) = float(50,-100,100)
43735#@gui : Attenuation Decay = float(1,0,8)
43736#@gui : sep = separator()
43737#@gui : Secondary Color = color(0,255,0)
43738#@gui : Deformation Type = choice("Shift","Radial","Angular","Random")
43739#@gui : X-Amplitude = float(0,-32,32)
43740#@gui : Y-Amplitude = float(0,-32,32)
43741#@gui : Smoothness = float(0,0,10)
43742#@gui : Attenuation Near Center (%) = float(0,-100,100)
43743#@gui : Attenuation Decay = float(1,0,8)
43744#@gui : sep = separator()
43745#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43746#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43747#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43748#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43749#@gui : sep = separator()
43750#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/08/10</i>.</small>")
43751fx_chromatic_aberrations :
43752  U1={u=[${1-3}];u/max(1e-8,norm(u))}
43753  U2={u=[${10-12}];u/max(1e-8,norm(u))}
43754
43755  repeat $! l[$>] to_color split_opacity l[0]
43756
43757    # Compute image decomposition : [0] = residual, [1] = weights for primary/secondary colors.
43758    100%,100%,1,2,"*begin(U1 = ["$U1"]; U2 = ["$U2"]);
43759      V = I(#0);
43760      d1 = dot(V,U1); V-=d1*U1;
43761      d2 = dot(V,U2); V-=d2*U2;
43762      I(#0) = V;
43763      [ d1,d2 ]"
43764    s. c
43765
43766    # Shift weights.
43767    _fx_chromatic_aberrations.. ${4-9}
43768    _fx_chromatic_aberrations. ${13-18}
43769
43770    # Reconstruct color image.
43771    a[-2,-1] c
43772    +[0] '"*begin(U1 = ["$U1"]; U2 = ["$U2"]); i(#1,x,y,0,0)*U1 + i(#1,x,y,0,1)*U2"'
43773    rm.
43774  endl a c endl done
43775
43776_fx_chromatic_aberrations :
43777  if $1==0 # Shift
43778    100%,100%,1,2,[$2,$3]
43779  elif $1==1 # Radial
43780    100%,100%,1,2,"
43781      ang = atan2(y - h/2, x - w/2);
43782      U = [ $2*cos(ang), $3*sin(ang) ]"
43783  elif $1==2 # Angular
43784    100%,100%,1,2,"
43785      ang = atan2(x - w/2, -y + h/2);
43786      U = [ $2*cos(ang), $3*sin(ang) ]"
43787  else # Random
43788    100%,100%,1,2,g s. c n.. 0,$1 n. 0,$2 a[-2,-1] c
43789  fi
43790  if $4 # Smoothness
43791    s. c
43792    m,M={-2,[im,iM]} b.. $4 n.. $m,$M
43793    m,M={[im,iM]} b. $4 n. $m,$M
43794    a[-2,-1] c
43795  fi
43796  if $5 # Center/Borders attenuation
43797    100%,100% =. 1,50%,50% distance. 1 n. 0,1.4142 c. 0,1
43798    if $5>0 pow. {0.1+$6} n. {1-$5%},1
43799    else negate. 1 pow. $6 n. {1+$5%},1
43800    fi
43801    *[-2,-1]
43802  fi
43803
43804  warp.. .,1,1,1 rm.
43805
43806fx_chromatic_aberrations_preview :
43807  gui_split_preview "fx_chromatic_aberrations $*",${-3--1}
43808
43809#@gui Dirty : fx_dirty, fx_dirty_preview(0)
43810#@gui : Amplitude = float(30,0,100)
43811#@gui : Monochrome = bool(1)
43812#@gui : sep = separator()
43813#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43814#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43815#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43816#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43817#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43818#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43819#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43820#@gui : Value Action = choice("None","Cut","Normalize")
43821#@gui : sep = separator()
43822#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43823#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43824#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43825#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43826#@gui : sep = separator()
43827#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/24/11</i>.</small>")
43828fx_dirty :
43829  ac "_fx_dirty ${1-2}",$3,$4
43830
43831fx_dirty_preview :
43832  gui_split_preview "fx_dirty ${1--2}",${-3--1}
43833
43834_fx_dirty :
43835  repeat $! l[$>]
43836    dct 100%,100%,1,{if($2,1,s)} noise. $1,2
43837    ==. 0 point. 0,0,0,1,1
43838    * idct c 0,255
43839  endl done
43840
43841#@gui Flip & Rotate Blocs : fx_flip_blocs,fx_flip_blocs_preview(0)
43842#@gui : X-Size (px) = int(4,1,128)
43843#@gui : Y-Size (px) = int(4,1,128)
43844#@gui : Flip = choice(3,"None","X-axis","Y-axis","XY-axes")
43845#@gui : Rotate = choice(1,"-90 deg.","0 deg.","90 deg.")
43846#@gui : sep = separator()
43847#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
43848#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
43849#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
43850#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
43851#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
43852#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
43853#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
43854#@gui : sep = separator()
43855#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43856#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43857#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43858#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43859#@gui : sep = separator()
43860#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/01/09</i>.</small>")
43861fx_flip_blocs :
43862  repeat $! l[$>]
43863    if $5 ac "_fx_flip_blocs ${1-4}",$5
43864    else _fx_flip_blocs ${1-4}
43865    fi
43866  endl done
43867
43868_fx_flip_blocs :
43869  if ($3%2)" && "$1>1 s x,-$1 mirror x a x fi
43870  if ($3>1)" && "$2>1 s y,-$2 mirror y a y fi
43871  if $4!=1" && "$1>1" && "$2>1
43872    s y,-$2 N=$!
43873    s x,-$1 M={$!/$N}
43874    ap "rotate {($4-1)*90}"
43875    append_tiles $M,$N
43876  fi
43877
43878fx_flip_blocs_preview :
43879  gui_split_preview "fx_flip_blocs $*",${-3--1}
43880
43881#@gui JPEG Artefacts : fx_jpeg_artefacts, fx_jpeg_artefacts_preview(0)
43882#@gui : note = note("<small>This filter simulates the JPEG compression artifacts,
43883#@gui : using DCT quantization on 8x8 blocs.</small>")
43884#@gui : Quality (%) = int(50,1,100)
43885#@gui : sep = separator()
43886#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
43887#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
43888#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
43889#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
43890#@gui : sep = separator()
43891#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/05/07</i>.</small>")
43892fx_jpeg_artefacts :
43893
43894  # Input all 8x8 DCT and iDCT kernels.
43895  # (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)
43896  base642img[] \
43897"MSBmbG9hdCBsaXR0bGVfZW5kaWFuCjggOCA2NCAxICMyMjIyCnic5Vs9q51FEC60kKtWMYpaCirYKCoI767iR6UoSangtdM/EAM2Fgab2FhaWdgIUXO"\
43898"qVO8sXEHBVDFia6kgCBaCQSyu+5w8c3nv5p55Bg+3uSmGs19nP2Z2Zt752P39/bJ/G8PHF5+qD1y4v77+yNWyeulegPWy9bbW+9pJ7z9XH6s/f3KxdL"\
43899"BeRr2hjDb0nfR+4OZyxwlw8UbHS4f1nehtBTg66f3kAyOU261OfDTiA/ehEV91ga8T208+cDlg5JNKPiknvR8ykfKwLuRlo7y0k96PegS8MxsB80RAn"\
43900"ROBRYC9R0AZtxF45o1w+cXz9fHvztTnP7y7nv7z5dLBern1NvTht37/75P17Qt/lOsPPQqwXrbeth6D/6Hv3F2fl2/++WXqMPfyegzmwZz439e9755f"\
43901"HwbMvTxjHqwFQBlt6MMYjMV/8F/MgbkwJ+bGGlgLa2Jt7AF7wRjMg/+hD3vG3nmGxr0Y5/S+euPGe/XVJ/ZKB+vlBkAZbeh77ctX6re/f1E6WC83AMp"\
43902"oQ9+Zd++oV6+8WTpYLzcAymhD30+fPlee/e2zqcPcywZAGW3oO6J+aPw437jeuJ9xv+N5xvMCD/d1vABPL3QcdVjjpbcV4rb+2PGK8jsdxx3QjrZCvJ"\
43903"evOl2A3/c7jTqs8dvbJtKkgK7A/eU+rsMEGoDOpJehjDb0YQzG4j+kpWEuzIm5sQbWwpqkc8NesCfsDXvEXrFn3oGGs+BMOBvOiLPizPgP+fzgW3Gsu"\
43904"87wb4mxTj53fixjfeDxo+qb2suC/2+Z1+ub9uX1TefyOundSG/HlxFfjfRupHclvo34dno30tvpNZNeRnob6V1I75n0nknvQnr7fZl4XybSu5Lexvs2"\
43905"8b45vSvp7fe18L4W0ruS3n7fC+97JV808oWRLyr5ppCvGvnKyFeVfFfIl418aeTLSr4t5GsjX8/ka+f76Yj6ofHjfON6437G/Y7nGc/rMtZlJfVBoz5"\
43906"weXrAS9QHjfrAKI+N8tj1gVEfuDyfKc+N+sCoD2bqg4n6oPia1AeuTwr1SaU+KNQHE/VRoT5yfVBd9lCfVeoz1wfVdZPbSJvAv6ECsAjc5vq/oOZX+1"\
43907"Pncx3nusp1zkJ3lL1+p37gXcad7GXb490CoIw212MYi//4XcNcmNPvNdbCmn4vo341v9qfOt8HH71V/7rzSulgvYx6Qxlt3vfg36cBxnZDGW3+v6F+a"\
43908"Pw4n1pv7Ffzq/2p9YCb6x0nwMXZjpcOa7z3trUcAW6f6TgFLv07BDjvbRNwDEAZbf5dgrH4zx5lEebCnJgba2AtrOlyK+pX86v9qfO5LeQ20lgfeHzk"\
43909"+RK0L30ut8ybrav51f7UOsRHIz6cHkZ6NOLTiM9Cesykh5EehfQw0mMiPQrpWUnPRnoU0qOqfjW/2p86H/nA5YDzbCWfFPKR85mRz5zvyxH1Q+PH+dR"\
43910"6Y7+aX+1PrUd52yhvG+Vlo7w0ylujvDXKW6O8nSlvC+VtobwulNcT5W2lvHV5Xynvi+pX86v9qfMp+x6+ogiUfU0fVAQlAuWfgF8rAuUfUPb9Kdrnl/"\
43911"p32E7/VuswXaJ93vvKKrCvV7Qhdm/aCQf2H77bd29+m69tMpTR5vYfxuI/+O8q8E9g7VP0L2BP2Bv2eIn+hVP85o/8A9QxZW9h37ucQd+1fkee7veow"\
43912"9TLBYAy2q5Rr0T29dmF/U55d2C/uz4a6ofGK//EuJ9xv+N5xvMu7XvgfHew74Ez4BV4Bo6BW+B1h/a529erhX29WtjXmBPf9+g732ncYU1XfN9jLQDK"\
43913"aEMfxmAs/rNL2w5zrRb+idXCP4E9YC/YE/aGPWKvO/QvLP0DXO+Qf0DZ9+Rz5ycb68q+Hnh85HkL2m3B/xvt+E378rryD+wG9j1wRXqXnYV9zvuAvsl"\
43914"5dLSvSa9KelfS20jvifQupHcjvQvvy7z87+oI/4TLFtLbdhb+Bd4H3Nd5N/APYL29hb3s37P+nYA+8lUhX03kKyPfzcq+9m+Qswv7fcH3dkT90Hjlnx"\
43915"j3M+53PM94XmXfU55OlKeF+qBQH0xL+xo0WQ32NeV5oTx3fVDcN+Q+HuqDRn1g1Afz0j+BuVeDf4L6aKY+MuoDoz6YlX8gESO3LWHbGP2xrp+InzfGS"\
43916"w58kxjLeEpN9G8bvz/W9RPx823j7dvG7491/UT8fB07Zbx0rZsYT/VvZ9W/bfz+WNdPxMu3jbNvG68/1vUT8XMjvmxBr0J8lkT/tvH7Y10/ET/fNt6+"\
43917"bfz+WNdPxM+N8tS/Pz3uZQt5GvVvG78/1vUT9nccP9f2t8ohCO3zhP8h9A8o/0PC/g7t+4T9HfoXlH2e8D+E/gHlf0jY36F9n7C/Q/+Css8T/ofQP6D"\
43918"8Dwn7O7TvE/Z36F9Q9nnC/xD6B5T/IWF/h/Z9wv4O/QvKPk/4H0L/gPI/JOzv0L5P2N+hf0HZ5wn/Q+gfUP6HhP0d2vcJ+zv0Lyj7POF/CP0Dyv+QsL"\
43919"9D+z5hf4f+BWWfJ/wPoX9A+R8S8XMVo1f5Ayp+r/IHwvnV/tT5EvFzFb9X+QMqfq/yB8L5E/kD4fkS8XMVv1f5Ayp+r/IHVL6Ayh8I10vEz1X8XuUPq"\
43920"Pi9yh8I50/kD4TnS8TPVbxe5Q+o+L3KH1B5Aip/IFwnET9X8XuVP6Di9yp/IJw/kT8Qni8RP1fxe5U/oOL3Kn9A5Quo/IFwvUT8XMXvVf6Ait+r/IFw"\
43921"/kT+QHg+lZ+fyL8P7XvlP1D2u/IPJN4fhO8LVH5+Iv8+tO9V/n3i/UHoH0i8PwjfF6j8/ET+ffg+QPkPEu8Pwvz+xPuD8H2Bys9P5N+H7wNU/n3i/UG"\
43922"Y3594fxC+L1D5+Yn8+9C+V/4DZb8r/0Di/UH4vkDl5yfy78P3ASr/PvH+IMzvT7w/CN8XqPz8RP59+D5A+Q8S7w/C/P7E+4PwfYHKz0/k34f2vcq/T7"\
43923"w/CP0DifcH4fuC/wBiGjbi"
43924
43925  base642img[] \
43926"MSBmbG9hdCBsaXR0bGVfZW5kaWFuCjggOCA2NCAxICMyOTE3CnicjVsxjybFEV0JB4iDyHdYhhAJIznBAiSS6ZbBkb3YS4glHOI/4ENyQuDTOsAJIRG"\
43927"BEyP7di+6cKo5yZY4J95DmxKChIREYMln5ODo1/OqXd9KU1XZzX37Tc+rqqlX9aq+o6Ojevr+T+rvlh/VH9x6uh716189d79c/un9cue1pwo+O//pu/"\
43928"Xhw9/WF/5+Mv62vnet/vzHn5Qb37xe8D189vs//Lr+4i8/G/d5853H6r+/d7d89sGrBffE9/DZP/734jjjN7e+Lp989efy4Jnny6NHj4o9H9e/NOfj3"\
43929"zgP98T3cP3u4x+V+3ffKufffr7g7/AsOA/3xDU+++F/bpRXvvxwwT3wnHgWnIdrfA+fPfnFswvw4lx87/y1p6Rft/48rT9Pe+O5+4Jnw/PjjO9/8zqu"\
43930"W7dH65hbee8a/m5gw/kPPnhV8D181u3RTt55rOHewI1n+9czz0u/bv1+rX+nvX3rawFePf+sn9+vxZ4PvMANTH/79vO1X0u3R+v2aDcf/0jwPdwbeF/"\
43931"68sO1X0t/VunPI5/efUuAF7iB99oXz679Wro9pNtDbvf7Af8bm73HucDf7VDUH8BfNn8LcONvuh2KxgP+7mTztwA3nrvboWg84B5vb/4W4Ab+boei8Q"\
43932"D89nzgPzPnA//Nzd8DN/B3OywaD8D/6ebvgRv36XZYNB6A//bm7xW4gb/bYdF4AP4723mtx72YeBjPA/w3NrwN55t4GPbAc3624R0+MfEw7IF7Pdjwt"\
43933"v68YuJh2AP49XxgM/Ewzgf+8w1v63Fv42HYA/hf2fAK/s/Ew7AH7v3khlf6fVYTD8MexC/EX4i/8fxK/EL8lfgb8VfiF+KvxN+IvxK/EH8h/kb8lfiF"\
43934"+Avxz/OJfyX+QvxC/JX4V+IvxC/EX4h/Jf6F+IX4C+Nf9DzGvzD+GuN/4mX8C+O/Mf4nXsa/MP4b418UL+NfGP+N8T/PZ/zP8xn/Ey/jf2X8C+N/4mX"\
43935"8r4x/YfyvipfxvzL+xfgb+a8w/1Xmn2L8jfxXmP8q85/1N/JfYf6rzH/V+Bv5rzD/VeY/62/kv8L8N883/kb+W5j/KvOf9Tfy38L8V5j/ivE38t/C/F"\
43936"eY/0b+Z74ddiD/CflHyH9N/U3+a+Q/If819Tf5r5H/hPzX1N/kPyH/Cflvnk/+m+eT/5r6m/wn5L+V/Cfqb/KfkP9W8p+ov8l/K/lvPQr4X/n7+Ap/3"\
43937"yN/R/yLd+k6+eain4/rv/bzX+7PhvMt3xG/EP/I1X/sn93p9v9vtzHsiGvaX2j/ybe0f6P9x5m4F95fvKO0n8B+/+w2OiP/efyv+Rp4Lwx/H5O/I/7F"\
43938"PfE94H2ixx/eseusRz7u8Qe8mu8Y/7MeQvwDr+ZbfK9f126P2u1R+f41zbd8/2Y9hvcPeIEbeG9v70/B9/AZ3p+I/5W/7xn+7niKxkPEv8D/8ebvBc+"\
43939"Pez/BfHyxxb8w/xXmv8L8N84CfubfAtzAj3jSeMDfMf8W5t/K/DvsAvw3N38P3MCPfKjxEPG/8vfxFf5We0T8C/zkn9LjfjHxMOwB/OS/Sv7TeBj2AP"\
43940"4bG95Rd5t4GPbAc5N/K/lX42HYA/jPNryV/KnxMOwR8b/y9/EV/ib+kH+JfyH+lfgL8QvxF+IX4q/E34i/EH8j/kr8jfgL8Tfir8TfiH8hfiH+Qvwt4"\
43941"n/l73uGvy82eww7RPzL+F8UL+N/YfwXxn9RvIz/wvivjP+Jl/FfGP+V8T/xMv4L478y/idexv/C+C8R/xt/I/9N/mb+C/nX+Bv5b2X+E+a/1fhb2P9M"\
43942"PoRfjL8HjzD/NeY/629h/zX5GBiNv0e/w/zXmP8k4n/l7+Mr/E3+k4h/yX9F/U3+W8h/i6132P8K+9/xXpD/qvqb/FfJfwf1Fvvvxv572Jj8V9Xf5L9"\
43943"C/lsi/rd8YftvxNPZ1n9PviN+If5RY0f8HfG/5Uvar9B+o3/P6g979UvE/9pvAS/jZ9YT7L9F8x3jf9ZDiP+IvyP+B17Nl3x/Zj2F9yerP+zVLxH/a/"\
43944"+NeGH+EOYP7b+F+a8w/xXmv2GXiL8j/sezMH+uzJ/C/DnsktUf9uqXiP+Bn/xx0H+rPfB35L9C/tN4GPaI+Dvi/3PTv5M/NR6GPbL6w179EvE/8a8nV"\
43945"/pv4q/EvxC/vg+F+CXi74j/z03/zvqpaD19nfpjRn/Yq18i/tf+W/Ey/teT//ffwvq3sP4trH+HHSL+jvif8b8qXsb/yviXrP6wV79E/G/8Xdg/TD5h"\
43946"/63+FvY/kw+RFyL+jvjf+Hth/zT5FHkhqz/s1S8R/9t6wfbf5D/t94r6m/xXyH9LxN8R/9t6if1zYf88+ves/rBXv0T8H+nnmf5b9WrbfyNXnG/1n6v"\
43947"/R/ODqP6wej31m4P6I+L/SD/P9t94JupXc56B9yfS/6P5QVR/nJr6g/rdnOdo/+fxf6SfZ/tvvBPULwv1yxEPkf4fzQ+i+uPU1B/Ubyv122b4b5f/I/"\
43948"08039Tvz7ov9Uekf4fzQ+i+uOU9cdD1h8mHoY9Iv6P9PNM/835xUH/Tfw6/9jV/6P5QVR/EH8h/nZq6g/khYj/I/08238rXsb/wvgvkf4fzQ+i+uPU1"\
43949"B+c31XO75rWPx7/R/p5tv9m/lutno28EOn/0fwgqj+Mv4Xz26nnq/7j8X+kn2f6b51X2P6b/LdE+n80P4jqDzuv4fz+oP5I8P/kO+IX4l8Nfo//3fl9"\
43950"gv8n3xF/I34x+D3+d/cXEvwvmu8Y/7MeMvHv8b87v0/wf9N8x/if9ZCJf4//3f2FBP8L89/C/FeY/8TkP4//3fl9gv8b819h/qvMf83kP4//3f2FBP9"\
43951"P/Z78p/GwGP7z+N+d3yf4v5H/KvlP46EY/vP4391fSPD/1O9Z/4jWw6b+8fjfnd8n+L+x/qmsf5rWw6b+8fjf3V9I8L+w/l1Y/xbWv2LqX4//3fl9gv"\
43952"8b69/C+rey/m2m/vX4391fSPC/+ntl/zP50PQ/Hv+78/sE/6u/hf3P5EPT/3j87+4vJPh/1jvsf4X972r6X4//3fl9gv9nvcP+t7H/FdP/evzv7i8k9"\
43953"H93fp7Q/6feTf3jgH8T+r+7P5DQ/6febeuHe6wfEvq/Oz9P6P+Tf6l/zXmI0b88/d/dH0jo/031zgtTPxyzfkjo/+78PKH/T/6l/tmof1ajf3r6v7s/"\
43954"kND/G/XPWT9Q/2yqfwb6vzs/T+j/lfp3o/49+dfo357+7+4PJPT/Rv37oH5QeyT0f3d+ntD/K+cfjfOPyb9m/uHp/+7+QEL/b5x/HNQPxB/2/9H8PKH"\
43955"/T/7l/Ktx/lXN/MvT/939gYT+3zj/mvUD519N51+B/u/OzxP6/+Rfzj+nHm7mn57+7+4PJPR/9bdcmPqB+S+c/0fz84T+P+cdnH8f8G9C/3f3BxL6/5"\
43956"x32PqB/Bf2/xF/Wr7b2f9z5/eJ/T+3fsjoD97+QmL/z+XPy3j/z53fJ/b/3Pohqz/s7S8k9v9c/ryM9//c+X1i/8+tH7L6w97+QmL/z+XPy3j/z53fJ"\
43957"/b/3Pohoz94+wuJ/T+XPy/j/T93fp/Y/3Prh4z+4O0vJPb/XP68jPf/3Pl9Yv/PrR+y+sPe/kJi/8/lT+Pvvf0/d36f2P9z64es/rC3v5DY/3P509Y7"\
43958"O/t/7vw+sf/n1g8Z/cHbX4j4P5qfZ/vvPf0+0v+j/YGo/ojmF4nf/7nz82z/vaffR/p/tD8Q1R/R/CLx+z93fp7tv/f0+0j/j/YHovojml8kfv/nzs+"\
43959"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"\
43960"zbf+/p95H+H+0PRPVHNL/4DgTd2gQ="
43961  nm[-2,-1] dct,idct
43962
43963  # Input DCT quantization matrix.
43964  (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;\
43965   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)
43966  f. "const S = $1<50?5000/$1:200-2*$1; max(1,round((S*i+50)/100,1,-1))"
43967  nm. Q
43968
43969  # Process images.
43970  repeat $!-3 l[$>,-3--1]
43971
43972    # Decompose image into luma/chroma
43973    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
43974
43975    repeat 3
43976      # Compute DCT of image with 8x8 blocs.
43977      [$>]
43978      f[$>] "begin(boundary = 2; ref(vector64(),res));
43979        if (!(x%8) && !(y%8),
43980          ref(crop(x,y,8,8),src);
43981          repeat (8,l, repeat (8,k, off = k + 8*l; res[off] = sum(src*crop(#"$dct",0,0,off,8,8,1))));
43982          draw(#-1,res,x,y,0,0,8,8);
43983        ); i"
43984      round.
43985
43986      # Compute DCT quantization.
43987      +r[Q] {$>,w},100%,1,1,0,2 /.. . round.. *[-2,-1]
43988
43989      # Compute iDCT of 8x8 blocs.
43990      f. "begin(boundary = 2; ref(vector64(),res));
43991        if (!(x%8) && !(y%8),
43992          ref(crop(x,y,8,8),src);
43993          repeat (8,l, repeat (8,k, off = k + 8*l; res[off] = sum(src*crop(#"$idct",0,0,off,8,8,1))));
43994          draw(#"$>",res,x,y,0,0,8,8);
43995        ); i"
43996      rm.
43997      round[$>]
43998    done
43999    l[^3--1] r ${-max_wh},1,1,1 a c ycbcr2rgb round. r $w,$h,1,3,0 endl
44000
44001  endl done
44002  rm[dct,idct,Q]
44003
44004fx_jpeg_artefacts_preview :
44005  gui_split_preview "fx_jpeg_artefacts $*",${-3--1}
44006
44007#@gui Lomo : fx_lomo, fx_lomo_preview(1)
44008#@gui : Vignette Size = float(20,0,100)
44009#@gui : sep = separator()
44010#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44011#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44012#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44013#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44014#@gui : sep = separator()
44015#@gui : note = note("<small>Authors: <i>Jérome Boulanger</i> and <i>David Tschumperlé</i>.
44016#@gui :       Latest Update: <i>2012/06/06</i>.</small>")
44017fx_lomo :
44018  remove_opacity repeat $! l[$>] to_rgb
44019    +gaussian {100-$1}%,{100-$1}% n. 0,1 *
44020    s c
44021    f[0] '255*atan((i-128)/128)'
44022    f[1] '255*tan((i-128)/128)'
44023    f[2] '255*atan((i-128)/255)'
44024    a c
44025    sharpen 1
44026    normalize 0,255
44027  endl done
44028
44029fx_lomo_preview :
44030  gui_split_preview "fx_lomo $*",${-3--1}
44031
44032#@gui Mess with Bits : fx_mess_with_bits, fx_mess_with_bits_preview
44033#@gui : note = note("<small><b>Input processing:</b></small>")
44034#@gui : Pre-Normalize = bool(1)
44035#@gui : Smoothness (%) = float(15,0,100)
44036#@gui : Multiplier = int(1,1,256)
44037#@gui : sep = separator()
44038#@gui : note = note("<small><b>Output processing:</b></small>")
44039#@gui : Reversing = choice{1,"None","Reverse bits","Reverse bytes"}
44040#@gui : Bit Masking (Start) = int(0,0,15)
44041#@gui : Bit Masking (End) = int(15,0,15)
44042#@gui : Opacity (%) = float(100,0,100)
44043#@gui : sep = separator()
44044#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44045#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44046#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44047#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44048#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44049#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44050#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44051#@gui : sep = separator()
44052#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44053#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44054#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44055#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44056#@gui : sep = separator()
44057#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/01/16</i>.</small>")
44058fx_mess_with_bits :
44059  ac "_fx_mess_with_bits ${1-7}",$8
44060
44061_fx_mess_with_bits :
44062  repeat $! +l[$>]
44063    b {($2/100)^2*100}% if $1 n 0,255 fi * $3 round
44064    # -> Here, images are 'ushort'-valued.
44065    if $4==1 f "for (k = res = 0, k<15, ++k, res|=((i>>k)&1)<<(15-k))"
44066    elif $4==2 f "res = ((i>>8)&255) | ((i&255)<<8)"
44067    fi
44068    f "begin( mask = (65535>>(15-max($5,$6))) & (65535<<min($5,$6))); i & mask"
44069    n 0,255
44070  endl j[$>] .,0,0,0,0,{$7%} rm. done
44071
44072fx_mess_with_bits_preview :
44073  gui_split_preview "fx_mess_with_bits $*",${-3--1}
44074
44075#@gui Noise [Additive] : fx_noise, fx_noise_preview(0)
44076#@gui : Amplitude = float(10,0,200)
44077#@gui : Noise Type = choice("Gaussian","Uniform","Salt and Pepper","Poisson")
44078#@gui : sep = separator()
44079#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44080#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44081#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44082#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44083#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44084#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44085#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44086#@gui : Value Action = choice(1,"None","Cut","Normalize")
44087#@gui : sep = separator()
44088#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44089#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44090#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44091#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44092#@gui : sep = separator()
44093#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44094fx_noise :
44095  ac "_fx_noise $1,$2",$3,$4
44096
44097_fx_noise :
44098  repeat $! l[$>] split_opacity l[0] noise $1,$2 endl a c endl done
44099
44100fx_noise_preview :
44101  gui_split_preview "fx_noise $*",${-3--1}
44102
44103#@gui Noise [Perlin] : fx_noise_perlin, fx_noise_perlin_preview(1)
44104#@gui : Random Seed = int(0,0,65536)
44105#@gui : sep = separator()
44106#@gui : note = note("<small><b>1st scale:</b></small>")
44107#@gui : Amplitude = float(100,0,512)
44108#@gui : Scale (%) = float(8,0,100)
44109#@gui : X/Y-Ratio = float(0,-4,4)
44110#@gui : sep = separator()
44111#@gui : note = note("<small><b>2nd scale:</b></small>")
44112#@gui : Amplitude = float(0,0,512)
44113#@gui : Scale (%) = float(4,0,100)
44114#@gui : X/Y-Ratio = float(0,-4,4)
44115#@gui : sep = separator()
44116#@gui : note = note("<small><b>3rd scale:</b></small>")
44117#@gui : Amplitude = float(0,0,512)
44118#@gui : Scale (%) = float(2,0,100)
44119#@gui : X/Y-Ratio = float(0,-4,4)
44120#@gui : sep = separator()
44121#@gui : note = note("<small><b>4th scale:</b></small>")
44122#@gui : Amplitude = float(0,0,512)
44123#@gui : Scale (%) = float(1,0,100)
44124#@gui : X/Y-Ratio = float(0,-4,4)
44125#@gui : sep = separator()
44126#@gui : Channel(s) = choice(2,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44127#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44128#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44129#@gui : "YCbCr [Green chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44130#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44131#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44132#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]")
44133#@gui : sep = separator()
44134#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44135#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44136#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44137#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44138#@gui : sep = separator()
44139#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/01/24</i>.</small>")
44140_fx_noise_perlin :
44141  seedx,seedy={[$1>>8,$1&255]}
44142  repeat $! l[$>]
44143    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
44144    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
44145    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
44146    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
44147  endl done
44148
44149fx_noise_perlin :
44150  ac "_fx_noise_perlin $*",$-4,1
44151
44152fx_noise_perlin_preview :
44153  gui_split_preview "fx_noise_perlin $*",${-3--1}
44154
44155#@gui Noise [Spread] : fx_spread, fx_spread_preview(0)
44156#@gui : X-Variations = float(4,0,20)
44157#@gui : Y-Variations = float(4,0,20)
44158#@gui : sep = separator()
44159#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44160#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44161#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44162#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44163#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44164#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44165#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44166#@gui : Value Action = choice("None","Cut","Normalize")
44167#@gui : sep = separator()
44168#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44169#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44170#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44171#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44172#@gui : sep = separator()
44173#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44174fx_spread :
44175  ac "spread $1,$2",$3,$4
44176
44177fx_spread_preview :
44178  gui_split_preview "fx_spread $*",${-3--1}
44179
44180#@gui Old-Movie Stripes : fx_stripes_y, fx_stripes_y_preview(1)
44181#@gui : Frequency = float(10,0,100)
44182#@gui : sep = separator()
44183#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44184#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44185#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44186#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44187#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44188#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44189#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44190#@gui : Value Action = choice("None","Cut","Normalize")
44191#@gui : sep = separator()
44192#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44193#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44194#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44195#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44196#@gui : sep = separator()
44197#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44198fx_stripes_y :
44199  ac "stripes_y $1",$2,$3
44200
44201fx_stripes_y_preview :
44202  gui_split_preview "fx_stripes_y $*",${-3--1}
44203
44204#@gui Oldschool 8bits : fx_8bits, fx_8bits_preview(0)
44205#@gui : Scale = float(25,1,100)
44206#@gui : Dithering = float(800,0,10000)
44207#@gui : Levels = int(16,2,256)
44208#@gui : sep = separator()
44209#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44210#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44211#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44212#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44213#@gui : sep = separator()
44214#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/02/11</i>.</small>")
44215fx_8bits :
44216  remove_opacity repeat $! l[$>]
44217    w={w} h={h}
44218    r $1%,$1%,1,100%,2
44219    +luminance sharpen. $2 otsu. 256 blend[-2,-1] shapeaverage0
44220    l. s c quantize $3,1,1 a c endl
44221    r. $w,$h,1,100%,1
44222  endl done
44223
44224fx_8bits_preview :
44225  gui_split_preview "fx_8bits $*",${-3--1}
44226
44227#@gui Pixel Sort : fx_pixelsort, fx_pixelsort_preview(1)+
44228#@gui : note = note{"<small><b>Sorting parameters:</b></small>"}
44229#@gui : Order = choice(1,"Decreasing","Increasing")
44230#@gui : Axis = choice("X-axis","Y-axis","X-axis Then Y-axis","Y-axis Then X-axis")
44231#@gui : Sorting Criterion = choice("Red","Green","Blue","Intensity","Luminance","Lightness","Hue",
44232#@gui : "Saturation","Minimum","Maximum","Random")
44233#@gui : sep = separator()
44234#@gui : note = note{"<small><b>Masking parameters:</b></small>"}
44235#@gui : Mask By = choice(1,"Bottom Layer","Criterion","Contours","Random")
44236#@gui : Lower Mask Threshold (%) = float(0,0,100)
44237#@gui : Higher Mask Threshold (%) = float(100,0,100)
44238#@gui : Mask Smoothness (%) = float(0,0,5)
44239#@gui : Invert Mask = bool(0)
44240#@gui : Preview Mask = bool(0)
44241#@gui : sep = separator()
44242#@gui : note = note{"<small><b>Note:</b>
44243#@gui : This filter implements one version of the algorithm described here :
44244#@gui : </small>"}
44245#@gui : url = link("http://satyarth.me/articles/pixel-sorting/")
44246#@gui : sep = separator()
44247#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/10/29</i>.</small>")
44248fx_pixelsort :
44249  _fx_pixelsort ${1-8},0
44250
44251_fx_pixelsort :
44252  repeat $!-($4==0) l[$>]
44253    if $3==0 +to_rgb channels. 0             # Red
44254    elif $3==1 +to_rgb channels. 1           # Green
44255    elif $3==2 +to_rgb channels. 2           # Blue
44256    elif $3==3 +compose_channels +           # Intensity
44257    elif $3==4 +luminance                    # Luminance
44258    elif $3==5 +to_rgb rgb2hsl. channels. 2  # Lightness
44259    elif $3==6 +to_rgb rgb2hsl. channels. 0  # Hue
44260    elif $3==7 +to_rgb rgb2hsl. channels. 1  # Saturation
44261    elif $3==8 +compose_channels min         # Minimum
44262    elif $3==9 +compose_channels max         # Maximum
44263    else 100%,100%,1,1 rand. 0,100           # Random
44264    fi
44265    if $4==0 pass. 0 norm.
44266    elif $4==1 .
44267    elif $4==2 +gradient_norm[0]
44268    else +rand. 0,100
44269    fi
44270    b. $7% ir. $5%,{$6+0.01}%
44271    if $8 ==. 0 fi
44272    if $9 k. * 255
44273    else pixelsort[0] {`$1?_'+':_'-'`},{`$2==0?'x':$2==1?'y':$2==2?'xy':'yx'`},[1],[2] k[0]
44274    fi
44275  endl done
44276
44277fx_pixelsort_preview :
44278  _fx_pixelsort $*
44279
44280#@gui Rain & Snow : fx_rain, fx_rain_preview(0)
44281#@gui : Angle = float(65,-180,180)
44282#@gui : Speed = float(10,0,50)
44283#@gui : Density (%) = float(50,0,100)
44284#@gui : Radius = float(0.1,0,3)
44285#@gui : Gamma = float(1,0,2)
44286#@gui : Opacity = float(1,0,1)
44287#@gui : sep = separator()
44288#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44289#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44290#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44291#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44292#@gui : sep = separator()
44293#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/29/06</i>.</small>")
44294fx_rain :
44295  repeat $! l[$<] nm=${-gui_layer_name}
44296    100%,100% l.
44297      noise 300 c 0,255 b 1,0
44298      c {100-$3}%,100%
44299      +>= 40% blend shapeaverage0
44300      blur_linear $2,$4,$1
44301      max n 0,255 apply_gamma $5
44302      nm name($nm),mode(screen),opacity({$6*100})
44303    endl
44304    rv
44305  endl done
44306
44307fx_rain_preview :
44308  gui_split_preview "repeat $! l[$>] fx_rain $* rv blend screen,$6 endl done",${-3--1}
44309
44310#@gui Random Shade Stripes : fx_shade_stripes, fx_shade_stripes_preview(1)
44311#@gui : Frequency = float(30,1,100)
44312#@gui : Orientation = choice(1,"Horizontal","Vertical")
44313#@gui : Darkness = float(0.8,0,3)
44314#@gui : Lightness = float(1.3,0,3)
44315#@gui : sep = separator()
44316#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44317#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44318#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44319#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44320#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44321#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44322#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44323#@gui : Value Action = choice("None","Cut","Normalize")
44324#@gui : sep = separator()
44325#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44326#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44327#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44328#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44329#@gui : sep = separator()
44330#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44331fx_shade_stripes :
44332  ac "shade_stripes $1,$2,$3,$4",$5,$6
44333
44334fx_shade_stripes_preview :
44335  gui_split_preview "fx_shade_stripes $*",${-3--1}
44336
44337#@gui Rebuild From Similar Blocs : fx_rebuild_from_similar_blocs, fx_rebuild_from_similar_blocs(1)
44338#@gui : Bloc Size (%) = float(5,2,50)
44339#@gui : sep = separator()
44340#@gui : Regularization factor = float(10,0,20)
44341#@gui : Luminance factor = float(0.75,0,3)
44342#@gui : Norm type = choice(1,"L1","L2")
44343#@gui : sep = separator()
44344#@gui : note = note{"This filter subdivides the image into blocs, and replace each bloc by the most similar bloc
44345#@gui : found in the other blocs."}
44346#@gui : sep = separator()
44347#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/09/17</i>.</small>")
44348fx_rebuild_from_similar_blocs :
44349  repeat $! l[$>] split_opacity l[0] to_rgb
44350    w0,h0,S={[w,h,round(min(w,h)*$1%)]} # S: Bloc size
44351
44352    # Split image into regular blocs.
44353    r {ceil([w,h]/$S)*$S},1,100%,0,3,0.5,0.5
44354    M,N={[w,h]/$S}
44355
44356    s yx,-$S a z # Original RGB blocs
44357#    +mirror x +mirror y a z # + all xy mirrors
44358#    repeat 3 +rotate. 90 done a z # + all 90° rotations
44359    +l b xy,$2% rgb2ycbcr sh. 0 *. $3 rm. endl # Blocs to compare, in YCbCr space
44360
44361    # For each bloc, find most similar bloc.
44362    $M,$N,1,1,-1
44363    f. ":
44364      i<0?(
44365        ind = x + y*w;
44366        ref(crop(#1,0,0,ind,w#1,h#1,1),S);
44367        kmin = 0;
44368        dmin = inf;
44369        repeat (wh,k, k!=ind?(
44370          d = norm"{1+$4}"(S - crop(#1,0,0,k,w#1,h#1,1));
44371          d<dmin?(dmin = d; kmin = k);
44372        ));
44373        xt = kmin%w;
44374        yt = int(kmin/w);
44375        i(xt,yt) = ind;
44376        kmin;
44377      ):i"
44378
44379    # Reconstruct image.
44380    {[$M,$N]*$S},1,{0,s}
44381    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)"
44382    k. r $w0,$h0,1,100%,0,0,0.5,0.5
44383  endl a c endl done
44384
44385#@gui Scanlines : fx_scanlines, fx_scanlines_preview(0)
44386#@gui : Amplitude = float(60,0,255)
44387#@gui : Bandwidth = float(2,1,300)
44388#@gui : Shape = choice(0,"Bloc","Triangle","Sine","Sine+","Random")
44389#@gui : Angle = float(0,0,360)
44390#@gui : Offset = float(0,0,500)
44391#@gui : sep = separator()
44392#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44393#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44394#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44395#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44396#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44397#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44398#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44399#@gui : Value Action = choice("None","Cut","Normalize")
44400#@gui : sep = separator()
44401#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44402#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44403#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44404#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44405#@gui : sep = separator()
44406#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/19/11</i>.</small>")
44407fx_scanlines :
44408  ac "scanlines ${1-5}",$6,$7
44409
44410fx_scanlines_preview :
44411  gui_split_preview "fx_scanlines $*",${-3--1}
44412
44413#@gui Self Glitching : fx_self_glitching, fx_self_glitching_preview(1)
44414#@gui : Multiplier = float(0,-5,5)
44415#@gui : Bias = float(0,-255,255)
44416#@gui : Negate = bool(0)
44417#@gui : Operator = choice("Add","Mul","And","Or","Xor","Pow","Reverse Pow","Mod","Reverse Mod")
44418#@gui : Shift Point = point(50,50,0,1)
44419#@gui : Boundary = choice(3,"Zero","Nearest","Periodic","Mirror")
44420#@gui : sep = separator()
44421#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44422#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44423#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44424#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44425#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44426#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44427#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44428#@gui : sep = separator()
44429#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44430#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44431#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44432#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44433#@gui : sep = separator()
44434#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/08/19</i>.</small>")
44435fx_self_glitching :
44436  ac "_fx_self_glitching ${1-7}",$8
44437
44438_fx_self_glitching :
44439  f "begin(
44440       shift = ([w,h]-1)*(50-[$5,$6])%;
44441       const sign = $3?-1:1;
44442       const boundary = $7;
44443     );
44444     val = sign*((2^$1)*j(shift) + $2);
44445     ($4==0?(val + i):
44446      $4==1?(val * i):
44447      $4==2?(val & i):
44448      $4==3?(val | i):
44449      $4==4?xor(val,i):
44450      $4==5?(val^i):
44451      $4==6?(i^val):
44452      $4==7?(val%i):
44453      (i%val)
44454     )%256;
44455  "
44456
44457fx_self_glitching_preview :
44458  gui_split_preview "fx_self_glitching $*",${-3--1}
44459
44460#@gui Streak : fx_streak,fx_streak(1)
44461#@gui : Mask Color = color(255,0,0,255)
44462#@gui : Step (%) = float(0,0,30)
44463#@gui : Angle = float(0,0,360)
44464#@gui : Propagation = choice(3,"Backward","Forward","Bidirectional [Sharp]","Bidirectional [Smooth]")
44465#@gui : sep = separator()
44466#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/12/22</i>.</small>")
44467fx_streak :
44468  repeat $! l[$>]
44469    to_rgba
44470    if !$4 R,G,B,A=0 else R,G,B,A=${1-4} fi +select_color 0,$R,$G,$B,$A
44471    if $7==3 srgb2rgb.. fi
44472    f.. "
44473      const step = max(1,$5%*min(w,h));
44474      const angle = $6*pi/180;
44475      const dx = step*cos(angle);
44476      const dy = step*sin(angle);
44477      if (!i(#-1),I,
44478        ixf = xf = x; iyf = yf = y; lf = 0;
44479        if ($7>=1, while (i(#-1,ixf=round(xf),iyf=round(yf)), ++lf; xf-=dx; yf-=dy)); # Forward
44480        ixb = xb = x; iyb = yb = y; lb = 0;
44481        if ($7!=1, while (i(#-1,ixb=round(xb),iyb=round(yb)), ++lb; xb+=dx; yb+=dy)); # Backward
44482        $7==0?I(ixb,iyb):
44483        $7==1?I(ixf,iyf):
44484        $7==2?(lf<lb?I(ixf,iyf):I(ixb,iyb)):
44485        (lb*lb*I(ixf,iyf) + lf*lf*I(ixb,iyb))/(lb^2+lf^2);
44486      )"
44487    if $7==3 rgb2srgb.. fi
44488    rm.
44489  endl done
44490
44491#@gui Visible Watermark : fx_watermark_visible, fx_watermark_visible(0)
44492#@gui : Text = text{"\\251 G'MIC"}
44493#@gui : Opacity = float(0.4,0.1,0.9)
44494#@gui : Size = int(50,13,128)
44495#@gui : Angle = float(25,0,360)
44496#@gui : Smoothness = float(0.5,0,5)
44497#@gui : Lightness = choice(1,"Darker","Brighter")
44498#@gui : sep = separator()
44499#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44500fx_watermark_visible : skip "${1= }"
44501  watermark_visible "$1",$2,$3,$4,$6,$5
44502
44503#@gui Warp by Intensity : fx_warp_by_intensity, fx_warp_by_intensity_preview(0)
44504#@gui : X-Factor = float(0.04,-6,6)
44505#@gui : Y-Factor = float(0.04,-6,6)
44506#@gui : sep = separator()
44507#@gui : X-Offset = float(128,0,255)
44508#@gui : Y-Offset = float(128,0,255)
44509#@gui : sep = separator()
44510#@gui : Correlated Channels = bool(0)
44511#@gui : Interpolation = choice(1,"Nearest Neighbor","Linear")
44512#@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror")
44513#@gui : sep = separator()
44514#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44515#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44516#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44517#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44518#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44519#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44520#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44521#@gui : sep = separator()
44522#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44523#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44524#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44525#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44526#@gui : sep = separator()
44527#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/02/09</i>.</small>")
44528fx_warp_by_intensity :
44529  if !$7 to_a fi
44530  ac "_fx_warp_by_intensity ${1-7}",$8
44531
44532_fx_warp_by_intensity :
44533  if $5 f "ni = norm2(R,G,B); J((ni-$3)*$1,(ni-$4)*$2,0,$6,$7)"
44534  else f "j((i-$3)*$1,(i-$4)*$2,0,0,$6,$7)"
44535  fi
44536
44537fx_warp_by_intensity_preview :
44538  gui_split_preview "fx_warp_by_intensity $*",${-3--1}
44539
44540#@gui ____<b>Details</b>
44541#------------------------
44542
44543#@gui Details Equalizer : fx_equalize_details, fx_equalize_details_preview(0)
44544#@gui : Base Scale = float(5,0,15)
44545#@gui : Detail Scale = float(0.5,0,5)
44546#@gui : note = note("<small><b>Coarse scale:</b></small>")
44547#@gui : Threshold = float(0,0,10)
44548#@gui : Smoothness = float(0,0,10)
44549#@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion")
44550#@gui : Gain = float(0,-4,4)
44551#@gui : sep = separator()
44552#@gui : note = note("<small><b>Medium scale:</b></small>")
44553#@gui : Threshold = float(0,0,10)
44554#@gui : Smoothness = float(0,0,10)
44555#@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion")
44556#@gui : Gain = float(0,-4,4)
44557#@gui : sep = separator()
44558#@gui : note = note("<small><b>Small scale:</b></small>")
44559#@gui : Threshold = float(0,0,10)
44560#@gui : Smoothness = float(0,0,10)
44561#@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion")
44562#@gui : Gain = float(0,-4,4)
44563#@gui : sep = separator()
44564#@gui : note = note("<small><b>Fine scale:</b></small>")
44565#@gui : Threshold = float(0,0,10)
44566#@gui : Smoothness = float(0,0,10)
44567#@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion")
44568#@gui : Gain = float(0,-4,4)
44569#@gui : sep = separator()
44570#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44571#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44572#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44573#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44574#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44575#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44576#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44577#@gui : Value Action = choice("None","Cut","Normalize")
44578#@gui : sep = separator()
44579#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
44580#@gui : "Sixteen Threads"), Spatial Overlap = int(32,0,256)
44581#@gui : sep = separator()
44582#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44583#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44584#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44585#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44586#@gui : sep = separator()
44587#@gui : note = note("<small>Author: <i>Jérome Boulanger</i> and <i>David Tschumperlé</i>.
44588#@gui :       Latest Update: <i>2015/11/11</i>.</small>")
44589_fx_equalize_details :
44590  repeat $! l[$>]
44591    split_details 5,{max(0.1,$1)},{max(0.1,$2)}
44592    __fx_equalize_details[1] ${3-6},8
44593    __fx_equalize_details[2] ${7-10},4
44594    __fx_equalize_details[3] ${11-14},2
44595    __fx_equalize_details[4] ${15-18},1
44596    + c 0,255
44597  endl done
44598
44599__fx_equalize_details :
44600  threshold $1,1
44601  if $3==0 b {$2*$5/2}
44602  elif $3==1
44603    if $2>0
44604      m={im} M={iM} n. 0,255
44605      repeat int($2/5) bilateral 15,{5*$5} done
44606      bilateral 15,{($2%5)*$5}
44607      *. {($M-$m)/255} +. $m
44608    fi
44609  else smooth {$2*50},0.2,0.8,$5,$5 fi
44610  * {10^$4}
44611
44612fx_equalize_details :
44613  ac "gui_parallel_overlap \"_fx_equalize_details ${1-18}\",$21,$22",$19,$20
44614
44615fx_equalize_details_preview :
44616  gui_split_preview "fx_equalize_details $*",${-3--1}
44617
44618#@gui Equalize Local Histograms : fx_equalize_local_histograms, fx_equalize_local_histograms_preview(0)
44619#@gui : Strength (%) = float(75,0,100)
44620#@gui : Mode = choice(2,"Raw","Hard","Soft")
44621#@gui : Radius = int(4,1,16)
44622#@gui : Sigma = float(100,0,256)
44623#@gui : Regularization = float(8,0,32)
44624#@gui : Reduce Halos = bool(1)
44625#@gui : sep = separator()
44626#@gui : Channel(s) = choice(16,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44627#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44628#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44629#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44630#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44631#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44632#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44633#@gui : sep = separator()
44634#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44635#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44636#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44637#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44638#@gui : sep = separator()
44639#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/31</i>.</small>")
44640fx_equalize_local_histograms :
44641  b0="normal" b1="overlay" b2="softlight"
44642  repeat $! l[$>]
44643    +ac "_fx_equalize_local_histograms ${1-6}",$7,1
44644    blend ${b$2},{$1%}
44645  endl done
44646
44647_fx_equalize_local_histograms :
44648  +n 0,511 round.
44649  f. "
44650    begin(
44651      const boundary = 1;
44652      const N = $3;
44653      const sigma = ($6?1:-1)*(0.1+$4);
44654      ref(vector512(),weights);
44655      repeat (size(weights),k, # Pre-compute exponentials
44656        weights[k] = sigma>=0?exp(-sqr(k/sigma)):1 - exp(-sqr(k/sigma))
44657      );
44658    );
44659
44660    ref(vector512(0),bins);
44661
44662    repeat (s,c,
44663      ref(crop(x - N,y - N,0,c,2*N + 1,2*N + 1,1,1),V);
44664      repeat (size(V),k, # Compute local weighted histogram
44665        val = V[k];
44666        diff = abs(val - V[size(V)/2]);
44667        bins[val]+=weights[diff];
44668      );
44669    );
44670
44671    sum = 0;
44672    repeat (size(bins),k,
44673      sum+=bins[k];
44674      bins[k] = sum;
44675    );
44676    bins/=max(1e-5,sum);
44677
44678    P = I;
44679    size(P)==1?(P = bins[P[0]]; 0):
44680    size(P)==2?(P = [ bins[P[0]], bins[P[1]] ]; 0):
44681    size(P)==3?(P = [ bins[P[0]], bins[P[1]], bins[P[2]] ]; 0):
44682    size(P)==4?(P = [ bins[P[0]], bins[P[1]], bins[P[2]], bins[P[3]] ]; 0);
44683    P"
44684  n. 0,255
44685  if $5 norm.. bilateral. ..,$5,{2+$5} fi
44686  k.
44687
44688fx_equalize_local_histograms_preview :
44689  gui_split_preview "fx_equalize_local_histograms $*",${-3--1}
44690
44691#@gui Freaky Details : fx_freaky_details, fx_freaky_details_preview(0)
44692#@gui : Amplitude = int(2,1,5)
44693#@gui : Scale = float(10,0,100)
44694#@gui : Iterations = int(1,1,4)
44695#@gui : sep = separator()
44696#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44697#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44698#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44699#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44700#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44701#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44702#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44703#@gui : sep = separator()
44704#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44705#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44706#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44707#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44708#@gui : sep = separator()
44709#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Patrick David</i>.
44710#@gui :       Latest Update: <i>2013/27/02</i>.</small>")
44711#@gui : sep = separator()
44712#@gui : note = note("This effect has been done following:")
44713#@gui : url = link("This tutorial from Patrick David",
44714#@gui : "https://patdavid.net/2013/02/calvin-hollywood-freaky-details-in-gimp.html")
44715fx_freaky_details :
44716  repeat $! l[$>] split_opacity l[0]
44717    repeat $3
44718      . +-. 255 *. -1
44719      repeat $1 bilateral. $2,{1.5*$2} done
44720      blend[-2,-1] vividlight blend overlay
44721    done
44722  endl a c endl done n 0,255
44723
44724fx_freaky_details_preview :
44725  gui_split_preview "fx_freaky_details $*",${-3--1}
44726
44727#@gui Local Normalization : fx_normalize_local, fx_normalize_local_preview(0)
44728#@gui : Amplitude = float(2,0,60)
44729#@gui : Radius = int(6,1,64)
44730#@gui : Neighborhood Smoothness = float(5,0,40)
44731#@gui : Average Smoothness = float(20,0,40)
44732#@gui : Constrain Values = bool(1)
44733#@gui : sep = separator()
44734#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44735#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44736#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44737#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44738#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44739#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44740#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44741#@gui : sep = separator()
44742#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44743#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44744#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44745#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44746#@gui : sep = separator()
44747#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44748fx_normalize_local :
44749  repeat $! l[$>]
44750    ac "normalize_local $1,$2,$3,$4,$5,0,255",$6
44751  endl done
44752
44753fx_normalize_local_preview :
44754  gui_split_preview "fx_normalize_local $*",${-3--1}
44755
44756#@gui Local Processing : fx_local_processing, fx_local_processing_preview(1)
44757#@gui : Action = choice("Normalize","Equalize")
44758#@gui : Strength (%) = float(75,0,100)
44759#@gui : Neighborhood Size (%) = float(10,1,100)
44760#@gui : Overlap (%) = float(50,0,75)
44761#@gui : Regularization (%) = float(20,0,100)
44762#@gui : Process Channels Individually = bool(0)
44763#@gui : sep = separator()
44764#@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44765#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44766#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44767#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44768#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44769#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44770#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44771#@gui : sep = separator()
44772#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44773#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44774#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44775#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44776#@gui : sep = separator()
44777#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/02/28</i>.</small>")
44778fx_local_processing :
44779  com0="n 0,255"
44780  com1="equalize 256,0,255 n 0,255"
44781  com=${com$1}
44782  if $6 com="s c "$com" a c" fi
44783  repeat $! l[$>]
44784    size={round(max(8,max(w,h)*$3%))}
44785    +ac "at \""$com"\","$size","$size",1,$4%,$4%",$7
44786    if $5 +norm[0] bilateral[1] .,{$5/20}%,{2+$5/4} rm. fi
44787    blend alpha,{$2%}
44788  endl done
44789
44790fx_local_processing_preview :
44791  gui_split_preview "fx_local_processing $*",${-3--1}
44792
44793#@gui Magic Details : fx_magic_details,fx_magic_details_preview(0)
44794#@gui : Amplitude = float(6,0,30)
44795#@gui : Spatial Scale = float(3,0,10)
44796#@gui : Value Scale = float(15,0,20)
44797#@gui : Edges = float(-0.5,-3,3)
44798#@gui : Smoothness = float(2,0,20)
44799#@gui : sep = separator()
44800#@gui : Channel(s) = choice(27,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44801#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44802#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44803#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44804#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44805#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44806#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]")
44807#@gui : sep = separator()
44808#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44809#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44810#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44811#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44812#@gui : sep = separator()
44813#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/10</i>.</small>")
44814fx_magic_details :
44815  ac "_fx_magic_details ${1-5}",$6,1
44816
44817_fx_magic_details :
44818  repeat $! l[$>]
44819    +bilateral $2,$3
44820    +gradient_norm.. +. 1
44821    pow. {$4>=0?3.1-$4:-3.1-$4}
44822    b. $5 n. 1,{1+$1}
44823    -... .. *[-3,-1] + c 0,255
44824  endl done
44825
44826fx_magic_details_preview :
44827  gui_split_preview "fx_magic_details $*",${-3--1}
44828
44829#@gui Mighty Details : fx_mighty_details, fx_mighty_details_preview(0)
44830#@gui : Amplitude = float(25,0,100)
44831#@gui : Details Amount = float(1,0,2)
44832#@gui : Details Scale = float(25,1,100)
44833#@gui : Details Smoothness = int(1,0,10)
44834#@gui : sep = separator()
44835#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44836#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44837#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44838#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44839#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44840#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44841#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44842#@gui : sep = separator()
44843#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44844#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44845#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44846#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44847#@gui : sep = separator()
44848#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/08/08</i>.</small>")
44849_fx_mighty_details :
44850  +smooth $3,0,1,0.5,0.5 -[1] [0]
44851  +abs. sign.. M={iM} ^. {2-$2} *. {$M/iM} *[-2,-1]
44852  +diffusiontensors[0] 0,1,0.5,0.5
44853  repeat $4 smooth[1] [2],20 done
44854  *[1] {-$1/5} +
44855
44856fx_mighty_details :
44857  ac "_fx_mighty_details ${1-4}",$5,1
44858  n 0,255
44859
44860fx_mighty_details_preview :
44861  gui_split_preview "fx_mighty_details $*",${-3--1}
44862
44863#@gui Sharpen [Deblur] : fx_deblur, fx_deblur_preview(0)
44864#@gui : Radius = float(2,0,20)
44865#@gui : Iterations = int(10,0,100)
44866#@gui : Time Step = float(20,0,50)
44867#@gui : Smoothness = float(0.1,0,10)
44868#@gui : Regularization = choice(1,"Tikhonov","Mean Curvature","Total Variation")
44869#@gui : sep = separator()
44870#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44871#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44872#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44873#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44874#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44875#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44876#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44877#@gui : sep = separator()
44878#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
44879#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
44880#@gui : sep = separator()
44881#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44882#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44883#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44884#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44885#@gui : sep = separator()
44886#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44887fx_deblur :
44888  ac "gui_parallel_overlap \"deblur ${1-5} c 0,255\",$7,$8",$6,1
44889
44890fx_deblur_preview :
44891  gui_split_preview "fx_deblur $*",${-3--1}
44892
44893#@gui Sharpen [Gold-Meinel] : fx_unsharp_goldmeinel, fx_unsharp_goldmeinel_preview(0)
44894#@gui : Sigma = float(1,0.5,10)
44895#@gui : Iterations = int(5,1,15)
44896#@gui : Acceleration = float(1,1,3)
44897#@gui : Blur = choice(1,"Exponential","Gaussian")
44898#@gui : Cut = bool(true)
44899#@gui : sep = separator()
44900#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44901#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44902#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44903#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44904#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44905#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44906#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44907#@gui : sep = separator()
44908#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
44909#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
44910#@gui : sep = separator()
44911#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44912#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44913#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44914#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44915#@gui : sep = separator()
44916#@gui : note = note("<small>Author: <i>Jérôme Boulanger</i>.      Latest Update: <i>2013/29/03</i>.</small>")
44917fx_unsharp_goldmeinel:
44918   ac "gui_parallel_overlap \"_fx_unsharp_goldmeinel $*\",$7,$8",$6,1
44919
44920_fx_unsharp_goldmeinel :
44921  deblur_goldmeinel $*
44922  if $5 c 0,255 else n 0,255 fi
44923
44924fx_unsharp_goldmeinel_preview:
44925  gui_split_preview "fx_unsharp_goldmeinel $*",${-3--1}
44926
44927#@gui Sharpen [Inverse Diffusion] : fx_sharpen_inversediff, fx_sharpen_inversediff_preview(0)
44928#@gui : Amplitude = float(50,1,300)
44929#@gui : Iterations = int(2,1,10)
44930#@gui : sep = separator()
44931#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44932#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44933#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44934#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44935#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44936#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44937#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44938#@gui : sep = separator()
44939#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44940#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44941#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44942#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44943#@gui : sep = separator()
44944#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
44945fx_sharpen_inversediff :
44946  ac "repeat $2 sharpen $1 c 0,255 done",$3,1
44947
44948fx_sharpen_inversediff_preview :
44949  gui_split_preview "fx_sharpen_inversediff $*",${-3--1}
44950
44951#@gui Sharpen [Multiscale] : fx_sharpen_multiscale, fx_sharpen_multiscale_preview(0)
44952#@gui : Strength (%) = float(15,0,100)
44953#@gui : Regularity (%) = float(20,0,100)
44954#@gui : sep = separator()
44955#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
44956#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
44957#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
44958#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
44959#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
44960#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
44961#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
44962#@gui : sep = separator()
44963#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
44964#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
44965#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
44966#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
44967#@gui : sep = separator()
44968#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/01/14</i>.</small>")
44969fx_sharpen_multiscale :
44970  ac "_fx_sharpen_multiscale $1,$2",$3
44971
44972fx_sharpen_multiscale_preview :
44973  gui_split_preview "fx_sharpen_multiscale $*",${-3--1}
44974
44975_fx_sharpen_multiscale :
44976  repeat $! l[$>]
44977    N={max(1,int(log2(min(w,h))-2))}
44978    +l repeat $N +r. 50%,50%,1,100%,2 +r. ..,..,1,100%,5 -[-3,-1] done endl # Decompose
44979    guided[0] 4,100 # Smooth guide image
44980
44981    # Process each scale.
44982    repeat $!-1 l[0,{$>+1}]
44983      +ri[0] [1],2
44984      +equalize.. 1024
44985      bilateral. ..,{2*$2%},100
44986      j[1] .,0,0,0,0,{$1%}
44987      k[0,1]
44988    endl done
44989    rm[0]
44990
44991    repeat $!-1 r. ..,..,1,100%,5 +[-2,-1] done # Recompose
44992    c 0,255
44993  endl done
44994
44995#@gui Sharpen [Octave Sharpening] : fx_unsharp_octave, fx_unsharp_octave_preview(0)
44996#@gui : Scales = int(4,1,10)
44997#@gui : Maximal Radius = float(5,0,20)
44998#@gui : Amount = float(3,0,10)
44999#@gui : Threshold = float(0,0,255)
45000#@gui : sep = separator()
45001#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45002#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45003#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45004#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45005#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45006#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45007#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45008#@gui : sep = separator()
45009#@gui : Parallel Processing = choice(1,"Auto","One Thread","Two Threads","Four Threads","Eight Threads",
45010#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
45011#@gui : sep = separator()
45012#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45013#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45014#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45015#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45016#@gui : sep = separator()
45017#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45018fx_unsharp_octave :
45019  ac "gui_parallel_overlap \"unsharp_octave $1,$2,$3,$4\",$6,$7",$5,1
45020
45021fx_unsharp_octave_preview :
45022  gui_split_preview "fx_unsharp_octave $*",${-3--1}
45023
45024#@gui Sharpen [Richardson-Lucy] : fx_unsharp_richardsonlucy, fx_unsharp_richardsonlucy_preview
45025#@gui : Sigma = float(1,0.5,10)
45026#@gui : Iterations = int(10,1,100)
45027#@gui : Blur = choice(1,"Exponential","Gaussian")
45028#@gui : Cut = bool(true)
45029#@gui : sep = separator()
45030#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45031#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45032#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45033#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45034#@gui : sep = separator()
45035#@gui : note = note("<small>Author: <i>Jérôme Boulanger</i>.      Latest Update: <i>2013/29/03</i>.</small>")
45036fx_unsharp_richardsonlucy :
45037  deblur_richardsonlucy $*
45038  if $4 c 0,255 else n 0,255 fi
45039
45040fx_unsharp_richardsonlucy_preview :
45041  gui_split_preview "fx_unsharp_richardsonlucy $*",${-3--1}
45042
45043#@gui Sharpen [Shock Filters] : fx_sharpen_shock, fx_sharpen_shock_preview(0)
45044#@gui : Amplitude = float(150,1,400)
45045#@gui : Edge Threshold = float(0.1,0,0.7)
45046#@gui : Gradient Smoothness = float(0.8,0,10)
45047#@gui : Tensor Smoothness = float(1.1,0,10)
45048#@gui : Iterations = int(1,1,10)
45049#@gui : sep = separator()
45050#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45051#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45052#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45053#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45054#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45055#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45056#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45057#@gui : sep = separator()
45058#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45059#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45060#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45061#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45062#@gui : sep = separator()
45063#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45064fx_sharpen_shock :
45065  ac "repeat $5 sharpen $1,$2,$3,$4 c 0,255 done",$6,1
45066
45067fx_sharpen_shock_preview :
45068  gui_split_preview "fx_sharpen_shock $*",${-3--1}
45069
45070#@gui Sharpen [Texture] : fx_sharpen_texture, fx_sharpen_texture_preview(0)
45071#@gui : Strength = float(1,0,4)
45072#@gui : Radius = float(4,0,32)
45073#@gui : sep = separator()
45074#@gui : Channel(s) = choice(16,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45075#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45076#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45077#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45078#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45079#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45080#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]")
45081#@gui : sep = separator()
45082#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45083#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45084#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45085#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45086#@gui : sep = separator()
45087#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/09</i>.</small>")
45088fx_sharpen_texture :
45089  ac "_fx_sharpen_texture ${1-2}",$3,1
45090
45091_fx_sharpen_texture :
45092  repeat $! l[$>]
45093    +rolling_guidance $2,5,0.5 -. [0] *. $1 - c 0,255
45094  endl done
45095
45096fx_sharpen_texture_preview :
45097  gui_split_preview "fx_sharpen_texture $*",${-3--1}
45098
45099#@gui Sharpen [Unsharp Mask] : fx_unsharp, fx_unsharp_preview(0)
45100#@gui : Sharpening Type = choice(1,"Gaussian","Bilateral")
45101#@gui : Spatial Radius = float(1.25,0,20)
45102#@gui : Bilateral Radius = float(10,0,60)
45103#@gui : Amount = float(2,0,10)
45104#@gui : Threshold = float(0,0,20)
45105#@gui : Darkness Level = float(1,0,4)
45106#@gui : Lightness Level = float(1,0,4)
45107#@gui : Iterations = int(1,1,10)
45108#@gui : Negative Effect = bool(0)
45109#@gui : sep = separator()
45110#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45111#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45112#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45113#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45114#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45115#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45116#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45117#@gui : sep = separator()
45118#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45119#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45120#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45121#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45122#@gui : note = note{"\n\n<small><b>Note: </b>
45123#@gui : This filter is inspired by the original GIMP <i>Unsharp Mask</i> filter, with additional parameters.
45124#@gui : </small>"}
45125#@gui : sep = separator()
45126#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45127_fx_unsharp :
45128  repeat $! repeat $8
45129    if $1==0 +b. $2 else +bilateral. $2,$3 fi
45130    -. .. *. -$4
45131    +norm. >=. $5% ri. .. *[-2,-1]
45132    if $9 *. -1 fi
45133    +c. 0,100% c.. -100%,0 *.. $6 *. $7 +[-2,-1]
45134    +[-2,-1] c. 0,255
45135  done mv. 0 done
45136
45137fx_unsharp :
45138  ac "_fx_unsharp $1,$2,$3,$4,$5,$6,$7,$8,$9",$10,1
45139
45140fx_unsharp_preview :
45141  gui_split_preview "fx_unsharp $*",${-3--1}
45142
45143#@gui Split Details [Alpha] : fx_split_details_alpha, fx_split_details_alpha_preview(0)
45144#@gui : Number of Levels = int(6,2,8)
45145#@gui : Base Scale = float(10,0,30)
45146#@gui : Details Scale = float(1,0,20)
45147#@gui : Opacity Gain = float(5,1,20)
45148#@gui : sep = separator()
45149#@gui : Preview Without Alpha = bool(0)
45150#@gui : sep = separator()
45151#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/22/04</i>.</small>")
45152fx_split_details_alpha :
45153  remove_opacity
45154  repeat $! l[$<]
45155    repeat $1-1
45156      s={$3+($2-$3)*$>/if($1-2>0,$1-2,1)}
45157      +_fx_split_details_alpha_blur. $s
45158      sub_alpha.. .,$4
45159    done
45160  endl done
45161
45162_fx_split_details_alpha_blur :
45163  if $1>=0.1 b. $1
45164  else
45165    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)
45166    else (1,2,1;2,4,2;1,2,1) fi
45167    normalize_sum. convolve.. . rm.
45168  fi
45169
45170fx_split_details_alpha_preview :
45171  repeat $! l[$>]
45172    fx_split_details_alpha ${1-4}
45173    if $5 remove_opacity[^-1] else to_rgba. fi
45174    N={int(sqrt($!))} N={round($!/$N,1,1)} r2dy {100/$N}%
45175    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
45176    to_rgba frame 1,1,0 frame 3,3,255 append_tiles ,
45177  endl done
45178
45179#@gui Split Details [Gaussian] : fx_split_details_gaussian, fx_split_details_gaussian_preview(0)
45180#@gui : Number of Scales = int(6,3,12)
45181#@gui : Base Scale = float(10,0,200)
45182#@gui : Details Scale = float(1,0,20)
45183#@gui : sep = separator()
45184#@gui : Sharpen Details in Preview = bool(0)
45185#@gui : sep = separator()
45186#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/22/01</i>.</small>")
45187fx_split_details_gaussian :
45188  remove_opacity repeat $! l[$>]
45189    nm=${-gui_layer_name}
45190    pos=${-gui_layer_pos}
45191    split_details $1,$2,$3
45192    +[^0] 128 c[^0] 0,255 round
45193    repeat $!-1 nm[{1+$>}] "mode(grainmerge), name"($nm" [scale ""#"{1+$>}"]), pos("$pos")" done
45194    nm[0] "name"($nm" [residual]), pos("$pos")"
45195    rv
45196  endl done
45197
45198fx_split_details_gaussian_preview :
45199  repeat $! l[$>]
45200    fx_split_details_gaussian $*
45201    if $4 equalize[^-1] 256 fi n[^-1] 0,255
45202    N={int(sqrt($!))} N={round($!/$N,1,1)} r2dy {100/$N}%
45203    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
45204    to_rgba frame 1,1,0 frame 3,3,255 append_tiles ,
45205  endl done
45206
45207#@gui Split Details [Wavelets] : fx_split_details_wavelets, fx_split_details_wavelets_preview(0)
45208#@gui : Number of Scales = int(6,2,12)
45209#@gui : Add Alpha Channels to Detail Scale Layers = _bool(0)
45210#@gui : sep = separator()
45211#@gui : Sharpen Details in Preview = bool(0)
45212#@gui : sep = separator()
45213#@gui : note = note{"<small><b>Note:</b> This filter decomposes an image into several detail scales,
45214#@gui : using wavelet atrous.
45215#@gui : It should provide similar results to the
45216#@gui : <a href="http://registry.gimp.org/node/11742">Wavelet Decompose Plug-in</a>
45217#@gui : (by Marco Rossini).
45218#@gui : </small>"}
45219#@gui : sep = separator()
45220#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/23/03</i>.</small>")
45221fx_split_details_wavelets :
45222  remove_opacity repeat $! l[$>]
45223    nm=${-gui_layer_name}
45224    pos=${-gui_layer_pos}
45225    split_details $1,0,0 rv +[^-1] 128 c[^-1] 0,255 round
45226    if $2 to_a[^-1] fi
45227    repeat $!-1 nm[$>] "mode(grainmerge), name"($nm" [scale ""#"{1+$>}"]), pos("$pos")" done
45228    nm. "name"($nm" [residual]), pos("$pos")"
45229  endl done
45230
45231fx_split_details_wavelets_preview :
45232  repeat $! l[$>]
45233    fx_split_details_wavelets $1,0
45234    if $3 equalize[^-1] 256 fi n[^-1] 0,255
45235    N={int(sqrt($!))} N={round($!/$N,1,1)} r2dy {100/$N}%
45236    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
45237    to_rgba frame 1,1,0 frame 3,3,255 append_tiles ,
45238  endl done
45239
45240#@gui Tone Mapping : fx_map_tones, fx_map_tones_preview(0)
45241#@gui : Threshold = float(0.5,0,1)
45242#@gui : Gamma = float(0.7,0,1)
45243#@gui : Smoothness = float(0.1,0,10)
45244#@gui : Iterations = int(30,0,500)
45245#@gui : sep = separator()
45246#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45247#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45248#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45249#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45250#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45251#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45252#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45253#@gui : sep = separator()
45254#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45255#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45256#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45257#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45258#@gui : sep = separator()
45259#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45260fx_map_tones :
45261  ac "map_tones ${1-4}",$5,1
45262  n 0,255
45263
45264fx_map_tones_preview :
45265  gui_split_preview "fx_map_tones $*",${-3--1}
45266
45267#@gui Tone Mapping [Fast] : fx_map_tones_fast, fx_map_tones_fast_preview(0)
45268#@gui : Radius = float(3,0,20)
45269#@gui : Power = float(0.5,0,1)
45270#@gui : sep = separator()
45271#@gui : Channel(s) = choice(11,"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>Authors: <i>Paul Nasca</i> and <i>David Tschumperlé</i>.
45285#@gui :       Latest Update: <i>2011/10/06</i>.</small>")
45286fx_map_tones_fast :
45287  ac "map_tones_fast $1,$2",$3,2
45288
45289fx_map_tones_fast_preview :
45290  gui_split_preview "fx_map_tones_fast ${^0}",${-3--1}
45291
45292#@gui ____<b>Frames</b>
45293#----------------------
45294
45295#@gui Droste : fx_droste, fx_droste_preview(1)
45296#@gui : note = note("<span color=\"red\">Upper-left coordinates :</span>")
45297#@gui : Point #0 = point(20,20,0,1,255,0,0)
45298#@gui : sep = separator()
45299#@gui : note = note("<span color=\"magenta\">Upper-right coordinates :</span>")
45300#@gui : Point #1 = point(80,20,0,1,255,0,255)
45301#@gui : sep = separator()
45302#@gui : note = note("<span color=\"blue\">Lower-right coordinates :</span>")
45303#@gui : Point #2 = point(80,80,0,1,0,128,255)
45304#@gui : sep = separator()
45305#@gui : note = note("<span color=\"cyan\">Lower-left coordinates :</span>")
45306#@gui : Point #3 = point(20,80,0,1,0,255,255)
45307#@gui : sep = separator()
45308#@gui : Iterations = int(1,1,10)
45309#@gui : X-Shift = float(0,-100,100)
45310#@gui : Y-Shift = float(0,-100,100)
45311#@gui : Angle = float(0,0,360)
45312#@gui : Zoom = float(1,0.1,5)
45313#@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes")
45314#@gui : Boundary = choice(1,"Transparent","Nearest","Periodic","Mirror")
45315#@gui : Drawing Mode = choice{"Replace","Replace (Sharpest)","Behind","Below"}
45316#@gui : View Outlines Only = bool(0)
45317#@gui : sep = separator()
45318#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/11/06</i>.</small>")
45319fx_droste :
45320  repeat $!
45321    if $16==1 100%,100%,1,1,'x' 100%,100%,1,1,'y' a[-2,-1] c fi
45322    repeat $9
45323      x0={round($1*w/100)} y0={round($2*h/100)} x1={round($3*w/100)} y1={round($4*h/100)}
45324      x2={round($5*w/100)} y2={round($6*h/100)} x3={round($7*w/100)} y3={round($8*h/100)}
45325      100%,100%,1,2,-32767 polygon. 4,$x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3,1,-65535
45326      sh. 0
45327      f. "if(i==-65535,
45328            x03 = "$x0"+(y-"$y0")/("$y3"-"$y0")*("$x3"-"$x0");
45329            x12 = "$x1"+(y-"$y1")/("$y2"-"$y1")*("$x2"-"$x1");
45330            (x-x03)/(x12-x03)*(w-1),i)"
45331      rm.
45332      sh. 1
45333      f. "if(i==-65535,
45334            y01 = "$y0"+(x-"$x0")/("$x1"-"$x0")*("$y1"-"$y0");
45335            y32 = "$y3"+(x-"$x3")/("$x2"-"$x3")*("$y2"-"$y3");
45336            (y-y01)/(y32-y01)*(h-1),i)"
45337      rm.
45338      xshift={w*$10/100} yshift={h*$11/100} alpha={-$12*pi/180}
45339      ca={cos($alpha)/$13} sa={sin($alpha)/$13} w2={w/2} h2={h/2}
45340      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))'
45341      if $14==0 sh. 0 f. 'if(i==-32767,x,i)' rm. sh. 1 f. 'if(i==-32767,y,i)' rm.
45342      elif $14==1 sh. 0 f. 'if(i==-32767,x,w-1-i)' rm. sh. 1 f. 'if(i==-32767,y,i)' rm.
45343      elif $14==2 sh. 0 f. 'if(i==-32767,x,i)' rm. sh. 1 f. 'if(i==-32767,y,h-1-i)' rm.
45344      else sh. 0 f. 'if(i==-32767,x,w-1-i)' rm. sh. 1 f. 'if(i==-32767,y,h-1-i)' rm.
45345      fi
45346      if $16<2 warp.. .,0,{$16==0},$15 rm.
45347      else
45348        +warp.. .,0,1,$15 rm..
45349        if $16==3 rv[-2,-1] fi
45350        blend[-2,-1] alpha
45351      fi
45352    done
45353    if $16==1 warp.. .,0,1,1 rm. fi
45354    mv. 0 done
45355
45356fx_droste_preview :
45357  if !$17 fx_droste $* else polygon 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,0.3,0,0,0,255 fi
45358  polygon 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,1,0xFFFFFFFF,0,0,0,255
45359
45360#@gui Frame [Blur] : fx_frame_blur, fx_frame_blur(1)
45361#@gui : Horizontal Size (%) = float(30,0,100)
45362#@gui : Vertical Size (%) = float(30,0,100)
45363#@gui : sep = separator()
45364#@gui : Crop = float(0,0,100)
45365#@gui : Blur = float(5,0,10)
45366#@gui : Roundness = float(0,0,1)
45367#@gui : Apply Color Balance = bool(0)
45368#@gui : Balance Color = color(128,128,128)
45369#@gui : Normalization = choice("None","Stretch","Equalize")
45370#@gui : sep = separator()
45371#@gui : Outline Size = float(5,0,50)
45372#@gui : Outline Color = color(255,255,255)
45373#@gui : X-Shadow = float(2,-10,10)
45374#@gui : Y-Shadow = float(2,-10,10)
45375#@gui : Shadow Smoothness = float(1,0,5)
45376#@gui : Shadow Contrast = float(0,0,100)
45377#@gui : X-Centering = float(0.5,0,1)
45378#@gui : Y-Centering = float(0.5,0,1)
45379#@gui : Angle = float(0,-180,180)
45380#@gui : sep = separator()
45381#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/19/01</i>.</small>")
45382fx_frame_blur :
45383  repeat $! l[$>] to_rgb
45384    sx={$1%*max(w,h)} sy={$2%*max(w,h)}
45385    +r {w+$sx},{h+$sy},1,100%,3 b[1] $4%
45386
45387    if $6 balance_gamma[1] ${7-9} fi
45388    if $10==1 n[1] 0,255 elif $10==2 n[1] 0,255 equalize[1] 256 fi
45389    rv
45390
45391    z[1] {$3/2}%,{$3/2}%,{100-$3/2}%,{100-$3/2}%
45392    to_rgba[1]
45393
45394    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)'
45395      v={min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2))}
45396      c. $v,{$v+0.5/max(w,h)} n. 0,255 rm. fi
45397
45398    s={$11%*max(w,h)}
45399    r[1] {w+$s},{h+$s},1,4,0,0,0.5,0.5
45400    i[1] 100%,100%,1,3 fc[1] ${12-14} blend[1,2] alpha to_a.
45401
45402    if $5 sh[1] 100% f. '1-(abs(x/w-0.5)^$r+abs(y/h-0.5)^$r)^(1/$r)'
45403      v={min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2))}
45404      c. $v,{$v+0.5/max(w,h)} n. 0,255 rm. fi
45405    rotate[1] $21,1,0
45406    r[1] [0],[0],1,4,0,0,$19,$20
45407    +channels[1] 100% b. $17%,0 c. 0,{max(1,100-$18)}% n. 0,255
45408    shift. {round(w*$15%)},{round(h*$16%)},0,0,0 /. -255 +. 1 *[0,-1]
45409
45410    blend alpha
45411  endl done
45412
45413#@gui Frame [Cube] : frame_cube, frame_cube(1)
45414#@gui : Depth = float(3,0,30)
45415#@gui : X-Center = float(0,-2,2)
45416#@gui : Y-Center = float(0,-2,2)
45417#@gui : Left Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY")
45418#@gui : Right Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY")
45419#@gui : Upper Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY")
45420#@gui : Lower Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY")
45421#@gui : sep = separator()
45422#@gui : note = note("<small>Author: <i>David Tschumperlé, Angelo Lama</i>.
45423#@gui :       Latest Update: <i>2012/29/01</i>.</small>")
45424
45425#@gui Frame [Fuzzy] : fx_frame_fuzzy, fx_frame_fuzzy(0)
45426#@gui : Horizontal Size (%) = float(5,0,100)
45427#@gui : Vertical Size (%) = float(5,0,100)
45428#@gui : Fuzzyness = float(10,0,40)
45429#@gui : Smoothness = float(1,0,5)
45430#@gui : Color = color(255,255,255,255)
45431#@gui : sep = separator()
45432#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45433fx_frame_fuzzy :
45434  repeat $! l[$>]
45435    sx={$1%*max(w,h)/2} sy={$2%*max(w,h)/2}
45436    frame_fuzzy $sx,$sy,${3-8}
45437  endl done
45438
45439#@gui Frame [Mirror] : fx_frame_mirror, fx_frame_mirror_preview(1)
45440#@gui : note = note("<b>Frame size:</b>")
45441#@gui : Horizontal (%) = float(10,0,100)
45442#@gui : Vertical (%) = float(10,0,100)
45443#@gui : sep = separator()
45444#@gui : note = note("<b>Image alignment:</b>")
45445#@gui : Horizontal (%) = float(50,0,100)
45446#@gui : Vertical (%) = float(50,0,100)
45447#@gui : sep = separator()
45448#@gui : note = note("<b>Frame dilation/shrinking:</b>")
45449#@gui : Left = float(0,-5,5)
45450#@gui : Right = float(0,-5,5)
45451#@gui : Up = float(0,-5,5)
45452#@gui : Bottom = float(0,-5,5)
45453#@gui : sep = separator()
45454#@gui : Preview Opacity (%) = float(0.75,0,1)
45455#@gui : sep = separator()
45456#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/08/20</i>.</small>")
45457fx_frame_mirror :
45458  repeat $! l[$>]
45459    {100+2*$1}%,{100+2*$2}%,1,100%,"
45460      const boundary = 3;
45461      const offx = (w - w#-1)*$3%;
45462      const offy = (h - h#-1)*$4%;
45463      const f_left = 2^$5;
45464      const f_right = 2^$6;
45465      const f_up = 2^$7;
45466      const f_bottom = 2^$8;
45467      x = x - offx;
45468      y = y - offy;
45469      x<0?(x*=-f_left):
45470      x>=w#-1?(x = w#-1 - 1 - f_right*(x - w#-1));
45471      y<0?(y*=-f_up):
45472      y>=h#-1?(y = h#-1 - 1 - f_bottom*(y - h#-1));
45473      I(#-1,x,y)"
45474  k. endl done
45475
45476fx_frame_mirror_preview :
45477  repeat $! l[$>]
45478    ws,hs={[w,h]} fx_frame_mirror $* wd,hd={[w,h]}
45479    rr2d $_preview_width,$_preview_height wp,hp={[w,h]}
45480    ws,hs={[$ws,$hs]*[$wp,$hp]/[$wd,$hd]}
45481
45482    coords={off=([$wp,$hp]-[$ws,$hs])*[$3,$4]%;[off,off+[$ws,$hs]-1]}
45483    split_opacity 100%,100%,1,1,$9 rectangle. $coords,1,1 *[0,-1] a c
45484    rectangle $coords,0.75,0xF0F0F0F0,255
45485    rectangle $coords,0.75,0x0F0F0F0F,0,0,0,255
45486  endl done
45487
45488#@gui Frame [Painting] : fx_frame_painting, fx_frame_painting_preview(1)
45489#@gui : Size (%) = float(10,0,100)
45490#@gui : Contrast = float(0.4,0,1)
45491#@gui : Smoothness = float(6,0,30)
45492#@gui : Color = color(225,200,120)
45493#@gui : sep = separator()
45494#@gui : Vignette Size = float(2,0,50)
45495#@gui : Vignette Contrast = float(400,0,1000)
45496#@gui : sep = separator()
45497#@gui : Defects Contrast = float(50,0,512)
45498#@gui : Defects Density = float(10,0,100)
45499#@gui : Defects Size = float(1,0,10)
45500#@gui : Defects Smoothness = float(0.5,0,20)
45501#@gui : sep = separator()
45502#@gui : Serial Number = int(123456,0,1000000)
45503#@gui : Frame as a New Layer = _bool(false)
45504#@gui : sep = separator()
45505#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/07/06</i>.</small>")
45506fx_frame_painting :
45507  if $14
45508    repeat $! 100%,100%,1,4 frame_painting. $1%,$2,$3%,${4-6},$7%,${8-13}
45509    rv[-2,-1] to_a. r. ..,..,1,4,0,0,0.5,0.5 mv[-2,-1] 0 done
45510  else frame_painting $1%,$2,$3%,${4-6},$7%,${8-13}
45511  fi
45512
45513fx_frame_painting_preview :
45514  frame_painting $1%,$2,$3%,${4-6},$7%,${8-13}
45515
45516#@gui Frame [Pattern] : fx_frame_pattern, fx_frame_pattern_preview(1)
45517#@gui : Tiles = int(10,3,30)
45518#@gui : Pattern = choice(1,"Top Layer","Self Image")
45519#@gui : Iterations = int(1,1,10)
45520#@gui : Constrain Image Size = _bool(1)
45521#@gui : sep = separator()
45522#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/01/08</i>.</small>")
45523fx_frame_pattern :
45524  if $2" || "$!==1 repeat $3 frame_pattern $1,$4 done
45525  else repeat $3 frame_pattern[^0] $1,[0],$4 done fi
45526
45527fx_frame_pattern_preview :
45528  fx_frame_pattern ${1-3},1
45529
45530#@gui Frame [Regular] : fx_frame, fx_frame(1)
45531#@gui : note = note("<b>Crop parameters :</b>")
45532#@gui : X-Start (%) = int(0,0,100)
45533#@gui : X-End (%) = int(100,0,100)
45534#@gui : Y-Start (%) = int(0,0,100)
45535#@gui : Y-End (%) = int(100,0,100)
45536#@gui : sep = separator()
45537#@gui : note = note("<b>Frame parameters :</b>")
45538#@gui : Width (%) = int(10,0,100)
45539#@gui : Height (%) = int(10,0,100)
45540#@gui : Color = color(0,0,0,255)
45541#@gui : Outline Size = int(1,0,100)
45542#@gui : Outline Color = color(255,255,255,255)
45543#@gui : sep = separator()
45544#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45545fx_frame :
45546 to_rgba repeat $!
45547   z. $1%,$3%,$2%,$4%
45548   frame. $11,$11,${12-15}
45549    sx={$5%*max(w,h)} sy={$6%*max(w,h)}
45550   frame. $sx,$sy,${7-10}
45551 mv. 0 done
45552
45553#@gui Frame [Round] : fx_frame_round, fx_frame_round(1)
45554#@gui : Sharpness = float(6,0.1,40)
45555#@gui : Size (%) = float(20,0,100)
45556#@gui : Smoothness = float(0.1,0,15)
45557#@gui : Shade = float(0,0,1)
45558#@gui : Color = color(255,255,255,255)
45559#@gui : Blur Frame = float(0,0,100)
45560#@gui : Blur Shade = float(0.1,0,1)
45561#@gui : Blur Amplitude = float(3,0,10)
45562#@gui : sep = separator()
45563#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45564fx_frame_round :
45565  frame_round ${1-8}
45566  if $9 frame_blur $1,{min(99,$1+$9)},$3,$10,$11% fi
45567
45568#@gui Frame [Smooth] : fx_frame_smooth, fx_frame_smooth(1)
45569#@gui : Width (%) = int(10,0,100)
45570#@gui : Height (%) = int(10,0,100)
45571#@gui : Roundness = float(0.25,0,1)
45572#@gui : sep = separator()
45573#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/25/04</i>.</small>")
45574fx_frame_smooth :
45575  repeat $! l[$>]
45576     sx={$1%*max(w,h)} sy={$2%*max(w,h)}
45577     100%,100%,1,1,0
45578     if $3 r={1+1/$3} f. '1-(abs(x/w-0.5)^$r+abs(y/h-0.5)^$r)^(1/$r)'
45579       v={min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2))} <=. $v
45580     fi
45581     frame $sx,$sy,1
45582     inpaint_pde[0] [1],100%,1,15
45583     rm.
45584  endl done c 0,255
45585
45586#@gui Old Photograph : fx_old_photo, fx_old_photo(1)
45587#@gui : Vignette Strength = float(200,0,255)
45588#@gui : Vignette Min Radius = float(50,0,100)
45589#@gui : Vignette Max Radius = float(85,0,100)
45590#@gui : sep = separator()
45591#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45592fx_old_photo :
45593  vignette ${1-3} old_photo
45594
45595#@gui Polaroid : fx_polaroid, fx_polaroid(1)
45596#@gui : Frame Size = int(10,0,400)
45597#@gui : Bottom Size = int(20,0,400)
45598#@gui : X-Shadow = float(0,-20,20)
45599#@gui : Y-Shadow = float(0,-20,20)
45600#@gui : Smoothness = float(3,0,5)
45601#@gui : Curvature = float(0,0,1)
45602#@gui : Angle = float(20,-180,180)
45603#@gui : Vignette Strength = float(50,0,255)
45604#@gui : Vignette Min Radius = float(70,0,100)
45605#@gui : Vignette Max Radius = float(95,0,100)
45606#@gui : sep = separator()
45607#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
45608fx_polaroid :
45609  vignette ${8-10} polaroid $1,$2 drop_shadow $3%,$4%,$5%,$6 rotate $7,1,0
45610
45611#@gui Tunnel : fx_tunnel, fx_tunnel(1)
45612#@gui : Depth = int(4,1,100)
45613#@gui : Factor = float(80,1,99)
45614#@gui : Center (%) = point(50,50)
45615#@gui : Opacity = float(0.2,0,1)
45616#@gui : Angle = float(0,-90,90)
45617#@gui : sep = separator()
45618#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/22/11</i>.</small>")
45619fx_tunnel :
45620  tunnel $1,$2%,{[${3,4}]%},${5-6}
45621
45622#@gui Vignette : fx_vignette, fx_vignette
45623#@gui : Strength = float(70,0,255)
45624#@gui : Min Radius = float(70,0,100)
45625#@gui : Max Radius = float(95,0,100)
45626#@gui : Color = color(0,0,0,255)
45627#@gui : sep = separator()
45628#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/24/10</i>.</small>")
45629fx_vignette :
45630  repeat $! to_rgb l[$>]
45631    to_rgba split_opacity
45632    =. 0 vignette. ${1-3} a c +fc ${4-7} rv blend alpha
45633  endl done
45634
45635#@gui ____<b>Frequencies</b>
45636#----------------------------
45637
45638#@gui Bandpass : fx_bandpass, fx_bandpass_preview(0)
45639#@gui : Low Frequency = float(0,0,100)
45640#@gui : High Frequency = float(100,0,100)
45641#@gui : sep = separator()
45642#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
45643#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
45644#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
45645#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
45646#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
45647#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
45648#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
45649#@gui : Value Action = choice(2,"None","Cut","Normalize")
45650#@gui : sep = separator()
45651#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
45652#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
45653#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
45654#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
45655#@gui : sep = separator()
45656#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45657fx_bandpass :
45658  repeat $! l[$>] split_opacity l[0]
45659    ac "bandpass $1%,$2%",$3,$4
45660  endl a c endl done
45661
45662fx_bandpass_preview :
45663  gui_split_preview "fx_bandpass $*",${-3--1}
45664
45665#@gui Fourier Analysis : fx_display_fft, fx_display_fft(1)
45666#@gui : sep = separator()
45667#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45668fx_display_fft :
45669  to_rgb display_fft
45670
45671#@gui Fourier Transform : fx_fourier, fx_fourier_preview(1) : *
45672#@gui : Magnitude / Phase = choice{2,"One Layer (Horizontal)","One Layer (Vertical)","Two Layers"}
45673#@gui : Discard Transparency = bool(1)
45674#@gui : sep = separator()
45675#@gui : note = note{"<small><b>Note:</b> Apply this filter once to get the direct FFT,
45676#@gui : and once again to get the reverse transform.</small>"}
45677#@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=3137dDa6P4s")
45678#@gui : sep = separator()
45679#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/06/16</i>.</small>")
45680fx_fourier : skip ${2=0}
45681  if $2 remove_opacity fi
45682  magic="GMICFFT"
45683
45684  i=0 for $i<$! ni={$i+1} nm={$i,n}
45685
45686    # Detect FFT/iFFT mode.
45687    is_ifft=0
45688    +columns[$i] 100%
45689    l.
45690      mag,m0,M0,m1,M1=${u\ {t}}
45691      if ['$mag']=='$magic' is_ifft=1 fi
45692    onfail endl rm.
45693    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
45694    if !$is_ifft
45695      +rows[$i] 100% l. mag,m0,M0=${u\ {t}} if ['$mag']=='$magic' is_ifft=3 fi onfail endl rm.
45696      if $is_ifft==3
45697        is_ifft=0 +rows[$ni] 100%
45698        l. mag,m1,M1=${u\ {t}} if ['$mag']=='$magic' is_ifft=3 fi onfail endl
45699        rm.
45700      fi
45701    fi
45702
45703    # Compute the transform.
45704    if !$is_ifft # FFT
45705      l[$i]
45706        fftpolar +.. 1 log.. m0,M0,m1,M1={[im#0,iM#0,im#1,iM#1]} n[-2,-1] 0,255
45707        if $1==0 ({'$magic,$m0,$M0,$m1,$M1'},0) y. a x
45708        elif $1==1 ({'$magic,$m0,$M0,$m1,$M1'},0) a y
45709        else ({'$magic,$m0,$M0'},0) a[-3,-1] y ({'$magic,$m1,$M1'},0) a[-2,-1] y
45710        fi
45711        nm $nm
45712        endl
45713
45714    else # iFFT
45715      if $is_ifft==1 columns[$i] 0,{$i,w-2} s[$i] x,2
45716      elif $is_ifft==2 rows[$i] 0,{$i,h-2} s[$i] y,2
45717      else rows[$i,$ni] 0,{$i,h-2}
45718      fi
45719      l[$i,{$i+1}] n[0] $m0,$M0 n[1] $m1,$M1 exp[0] -[0] 1 ifftpolar c 0,255 endl
45720    fi
45721    i+={$1<2" || "$is_ifft>2?1:2}
45722  done
45723
45724fx_fourier_preview :
45725  if $2 remove_opacity fi
45726  dfft
45727
45728#@gui Fourier Watermark : fx_watermark_fourier, _none_
45729#@gui : Text = text{"(c) G'MIC"}
45730#@gui : Size = int(53,13,128)
45731#@gui : sep = separator()
45732#@gui : note = note("<small><b>Note: </b> To make the watermark visible afterwards, use the
45733#@gui : 'Fourier Analysis' filter. </small>")
45734#@gui : sep = separator()
45735#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
45736fx_watermark_fourier :
45737  watermark_fourier "$1",$2 c 0,255
45738
45739#@gui ____<b>Layers</b>
45740#-----------------------
45741
45742#@gui Align Layers : fx_align_layers, fx_align_layers_preview : *
45743#@gui : Alignment Type = choice(0,"Rigid","Non-Rigid")
45744#@gui : Smoothness = float(0.7,0,1)
45745#@gui : Scales = choice(0,"Auto","1","2","3","4","5","6","7","8")
45746#@gui : Revert Layers = bool(0)
45747#@gui : sep = separator()
45748#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/01/11</i>.</small>")
45749fx_align_layers :
45750  to_colormode 0
45751  r ${-max_wh},1,100%,0,0,0.5,0.5
45752  if ${4=0} _fx_revert_layers fi
45753  remove_opacity
45754  if $1 register_nonrigid[^-1] .,$2,0.1,$3
45755  else register_rigid[^-1] .,$2
45756  fi
45757
45758fx_align_layers_preview :
45759  fx_align_layers $1,$2,0 blend_edges 0.1
45760
45761_fx_revert_layers :
45762  repeat int($!/2) rv[{2*$>},{2*$>+1}] done
45763
45764#@gui Blend [Average All] : fx_blend_average_all, fx_blend_average_all : *
45765#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
45766#@gui : sep = separator()
45767#@gui : note = note{"<small><b>Note:</b>
45768#@gui : This filter takes multiple layers as input and average them. Set the <i>Input layers</i> option
45769#@gui : to handle multiple input layers.
45770#@gui : </small>"}
45771#@gui : sep = separator()
45772#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/11/08</i>.</small>")
45773fx_blend_average_all :
45774  if $! to_rgba
45775    N=$! r ${-max_wh},1,100%,0,0,0.5,0.5
45776    _gb_fwd $1
45777    + / $N
45778    _gb_bwd $1
45779  fi
45780
45781_gb_fwd :
45782  to_color
45783  if $1==1 repeat $! l[$>] sh 0,2 srgb2rgb. rm. endl done
45784  elif $1==2 repeat $! l[$>] sh 0,2 srgb2rgb. rgb2lab. rm. endl done
45785  fi
45786
45787_gb_bwd :
45788  to_color
45789  if $1==1 repeat $! l[$>] sh 0,2 rgb2srgb. rm. endl done
45790  elif $1==2 repeat $! l[$>] sh 0,2 lab2rgb. rgb2srgb. rm. endl done
45791  fi
45792
45793#@gui Blend [Edges] : fx_blend_edges, fx_blend_edges(0) : *
45794#@gui : Opacity = float(1,0,1)
45795#@gui : Smoothness = float(0.8,0,5)
45796#@gui : Revert Layers = bool(0)
45797#@gui : sep = separator()
45798#@gui : note = note{"<small><b>Note:</b>
45799#@gui : This filter needs two layers to work properly. Set the <i>Input layers</i> option to handle
45800#@gui : multiple input layers.
45801#@gui : </small>"}
45802#@gui : sep = separator()
45803#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/21/01</i>.</small>")
45804fx_blend_edges :
45805  repeat int($!/2) l[$>,{$>+1}] if $3 rv fi +blend_edges[-2,-1] $2 rm... blend[-2,-1] alpha,$1 endl done
45806
45807#@gui Blend [Fade] : fx_blend_fade, fx_blend_fade(1) : +
45808#@gui : Preset = choice{1,"Custom","Linear","Circular","Wave","Keftales"}
45809#@gui : Offset = float(0,-1,1)
45810#@gui : Thinness = float(0,0,10)
45811#@gui : Sharpness = float(5,1,20)
45812#@gui : Sharpest = bool(0)
45813#@gui : Revert Layers = bool(0)
45814#@gui : Colorspace = choice("sRGB","Linear RGB","Lab")
45815#@gui : note = note{\n<small>
45816#@gui : The parameters below are used in most presets.
45817#@gui : </small>}
45818#@gui : 1st Parameter = float(0,-1,1)
45819#@gui : 2nd Parameter = float(0,-1,1)
45820#@gui : 3rd Parameter = float(0,-1,1)
45821#@gui : note = note{\n<small>
45822#@gui : The formula below is used for the <i>Custom</i> preset.
45823#@gui : </small>}
45824#@gui : Formula = text{"cos(4*pi*x/w) * sin(4*pi*y/h)"}
45825#@gui : note = note{"<small><b>Note:</b>
45826#@gui : This filter needs two layers to work properly. Set the <i>Input layers</i> option to handle
45827#@gui : multiple input layers.
45828#@gui : </small>"}
45829#@gui : sep = separator()
45830#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/21/01</i>.</small>")
45831fx_blend_fade :
45832  if $!==1 return fi
45833  to_colormode 4
45834  _gb_fwd $7
45835  if $1==0 [0],[0],1,1,"$11"
45836  else _fx_blend_fade$1 $8,$9,$10 r. [0],[0],1,1,3
45837  fi
45838  n. {-($!-2)*$3},{($!-2)*(1+$3)}
45839  -. {$2*(1+$3)*($!-2)}
45840  c. 0,{$!-2}
45841  if $6 rv[^-1] fi
45842  if $5 round. 1
45843  else roundify. $4
45844  fi
45845  blend_fade[^-1] . rm.
45846  _gb_bwd $7
45847  c 0,255
45848
45849_fx_blend_fade1 : [0],[0],1,1,"a=$1*pi/2; x*cos(a) + y*sin(a)"
45850_fx_blend_fade2 : [0],[0],1,1,0 =. 1,{($1+1)*50}%,{($2+1)*50}% distance. 1
45851_fx_blend_fade3 : [0],[0],1,1,0 =. 1,{($1+1)*50}%,{($2+1)*50}% distance. 1 *. {0.01+$3/2} cos.
45852_fx_blend_fade4 : [0],[0],1,1,"((x-w*($1+0.5))*(y-h*($2+0.5)))%(0.2*w*h*(1.001+$3))"
45853
45854#@gui Blend [Median] : fx_blend_median, fx_blend_median(0) : *
45855#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
45856#@gui : sep = separator()
45857#@gui : note = note{"<small><b>Note:</b>
45858#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
45859#@gui : multiple input layers.
45860#@gui : </small>"}
45861#@gui : sep = separator()
45862#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Iain Fergusson</i>.
45863#@gui :       Latest Update: <i>2014/16/12</i>.</small>")
45864fx_blend_median :
45865  _gb_fwd $1
45866  blend_median
45867  _gb_bwd $1
45868
45869#@gui Blend [Seamless] : fx_blend_seamless, fx_blend_seamless_preview(1) : *
45870#@gui : Mixed Mode = bool(0)
45871#@gui : Inner Fading = float(0,0,100)
45872#@gui : Outer Fading = float(25,0,100)
45873#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
45874#@gui : sep = separator()
45875#@gui : Output as Separate Layers = _bool(0)
45876#@gui : sep = separator()
45877#@gui : note = note{"<small><b>Note:</b>
45878#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
45879#@gui : multiple input layers.
45880#@gui : </small>"}
45881#@gui : sep = separator()
45882#@gui : url = link("Click here for a detailed description of this filter.",\
45883# "http://gimpchat.com/viewtopic.php?f=28&t=10204")
45884#@gui : url = link("+ Video tutorial 1","http://www.youtube.com/watch?v=Nu-S1HmOCgE")
45885#@gui : url = link("+ Video tutorial 2","http://www.youtube.com/watch?v=zsHgQY6025I")
45886#@gui : url = link("+ Video tutorial 3","http://www.youtube.com/watch?v=2e6FikWMkaQ")
45887#@gui : sep = separator()
45888#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/04/05</i>.</small>")
45889fx_blend_seamless :
45890  rv
45891  _gb_fwd $4
45892  to_a[^0] r[^0] [0],[0],1,100%,0
45893  repeat $! pos=${gui_layer_pos[$>]} shift[$>] ${u\ $pos},0,0 done
45894  if $5 # Output as separate layers
45895    +blend_seamless $1,$2%,$3%
45896    remove_opacity[0,-1] k[0,-1] rv sub_alpha[0] [1],1
45897  else
45898    blend_seamless $1,$2%,$3% # Output as a single layer.
45899  fi
45900  _gb_bwd $4
45901
45902fx_blend_seamless_preview :
45903  fx_blend_seamless ${1-4},0
45904
45905#@gui Blend [Standard] : fx_blend, fx_blend_preview : *
45906#@gui : Mode = choice{6,"Add","Alpha","And","Average","Blue","Burn","Custom formula","Darken","Difference",
45907#@gui : "Divide","Dodge","Edges","Exclusion","Freeze","Grain Extract","Grain Merge","Green","Hard Light",
45908#@gui : "Hard Mix","Hue","Interpolation","Lighten","Lightness","Linear Burn","Linear Light","Luminance",
45909#@gui : "Multiply","Negation","Or","Overlay","Pin Light","Red","Reflect","Saturation",
45910#@gui : "Shape Area Max","Shape Area Max0","Shape Area Min","Shape Area Min0","Shape Average","Shape Average0",
45911#@gui : "Shape Median","Shape Median0","Shape Min","Shape Min0","Shape Max","Shape Max0",
45912#@gui : "Soft Burn","Soft Dodge","Soft Light","Screen","Stamp","Subtract","Value","Vivid Light","Xor"}
45913#@gui : Process As = choice("Two-by-Two","Upper Layer is the Top Layer for All Blends",
45914#@gui : "Lower Layer is the Bottom Layer for All Blends")
45915#@gui : Opacity (%) = float(100,0,100)
45916#@gui : Preview All Outputs = bool(1)
45917#@gui : sep = separator()
45918#@gui : Custom Formula = text{"1/2 - 1/4*cos(pi*a) - 1/4*cos(pi*b)"}
45919#@gui : note = note{"<small><b>Note:</b> In custom formulas, <samp>a</samp> and <samp>b</samp> respectively stand for
45920#@gui : the values of the <i>base layer<i> and the <i>blend layer</i>,
45921#@gui : and are defined in value range [0,1].</small>"}
45922#@gui : sep = separator()
45923#@gui : note = note{"<small><b>Note:</b>
45924#@gui : This filter needs at least two layers to work properly. Do not forget to set the <i>Input layers</i> option
45925#@gui : below to handle multiple input layers.
45926#@gui : </small>"}
45927#@gui : url = link("Reference page for G'MIC blending modes",
45928#@gui : "https://github.com/dtschump/gmic-community/wiki/Blending-modes")
45929#@gui : sep = separator()
45930#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/03/08</i>.</small>")
45931fx_blend :
45932  mode=${arg\ 1+$1,add,alpha,and,average,blue,burn,custom_formula,darken,difference,\
45933       divide,dodge,edges,exclusion,freeze,grainextract,grainmerge,green,hardlight,\
45934       hardmix,hue,interpolation,lighten,lightness,linearburn,linearlight,luminance,\
45935       multiply,negation,or,overlay,pinlight,red,reflect,saturation,\
45936       shapeareamax,shapeareamax0,shapeareamin,shapeareamin0,\
45937       shapeaverage,shapeaverage0,shapemedian,shapemedian0,\
45938       shapemin,shapemin0,shapemax,shapemax0,\
45939       softburn,softdodge,softlight,screen,stamp,subtract,value,\
45940       vividlight,xor}
45941  m "_blend_custom_formula : f. \"a = i#0/255; b = i#1/255; 255*cut(($5),0,1)\""
45942  if $2==0 repeat int($!/2) l[$>,{$>+1}] rv blend $mode,{$3%} endl done # Two-by-two.
45943  elif $2==1" && "$!>1 blend[^0] [0],$mode,{$3%},0 rm[0]  # Top layer is top for all blends.
45944  elif $2==2" && "$!>1 blend[^-1] .,$mode,{$3%},1 rm. # Bottom layer is bottom for all blends.
45945  fi
45946  um _blend_custom_formula
45947
45948fx_blend_preview :
45949  fx_blend $"*"
45950  if $4 append_tiles , fi
45951
45952#@gui Colors to Layers : fx_split_colors, fx_split_colors_preview(1)
45953#@gui : Color Tolerance = float(50,0,256)
45954#@gui : Maximum Number of Output Layers = int(16,2,256)
45955#@gui : Minimal Area (%) = float(1,0,100)
45956#@gui : Autocrop Output Layers = bool()
45957#@gui : sep = separator()
45958#@gui : note = note{"<small><b>Note:</b> This filter decomposes an image into several layers each with
45959#@gui : a single color + a residual layer (if any).
45960#@gui : </small>"}
45961#@gui : sep = separator()
45962#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/11/03</i>.</small>")
45963fx_split_colors : skip ${2=0}
45964  to_rgb repeat $! l[$>]
45965    nm=${-gui_layer_name}
45966    min_area={max(1,w*h*$3%)}
45967    split_colors $1,$2,$min_area
45968    nm name($nm)
45969    if $4 gui_autocrop_layers fi
45970  endl done
45971
45972fx_split_colors_preview :
45973  repeat $! l[$>]
45974    +fx_split_colors ${1-4} drgba
45975    repeat $! l[$>] to ${arg\ {1+!!$>},"Original","#"$>},1,1,43,7,1,255 endl done
45976    frame 1,1,0 frame 3,3,255 to_rgba append_tiles ,
45977  endl done
45978
45979#@gui Fade Layers : fx_fade_layers, fx_fade_layers_preview : +
45980#@gui : Inter-Frames = _int(10,2,100)
45981#@gui : sep = separator()
45982#@gui : note = note{"<small><b>Note:</b>
45983#@gui : This filter needs at least two layers to work properly. Set the <i>Input layers</i> option to handle
45984#@gui : multiple input layers.
45985#@gui : </small>"}
45986#@gui : sep = separator()
45987#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/04/08</i>.</small>")
45988fx_fade_layers :
45989  if $!<2 return fi
45990  to_colormode 0
45991  r ${-max_wh},1,100%,0,0,0.5,0.5
45992  a z r 100%,100%,{(d-1)*$1+1},100%,3 s z
45993
45994fx_fade_layers_preview :
45995  if $!<2 return fi
45996  to_colormode 0
45997  r ${-max_wh},1,100%,0,0,0.5,0.5
45998  k[0,1] + / 2
45999
46000#@gui Layers to Tiles : append_tiles, fx_append_tiles_preview(1) : *
46001#@gui : X-Tiles = int(0,0,256)
46002#@gui : Y-Tiles = int(0,0,256)
46003#@gui : note = note("<small>For both parameters, <i>0</i> means <i>automatic</i>.</small>")
46004#@gui : sep = separator()
46005#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46006fx_append_tiles_preview :
46007  frame 1,1,0,0,0,255 append_tiles $1,$2
46008
46009#@gui Morph Layers : fx_morph_layers, gui_no_preview : *
46010#@gui : Inter-Frames = _int(10,2,100)
46011#@gui : Smoothness = _float(0.2,0,2)
46012#@gui : Precision = _float(0.1,0,2)
46013#@gui : Revert Layers = bool(0)
46014#@gui : sep = separator()
46015#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46016fx_morph_layers :
46017  if ${4=0} _fx_revert_layers fi
46018  to_rgb morph $1,$2,$3
46019
46020#@gui Multiscale Operator : fx_apply_multiscale, fx_apply_multiscale_preview(1)
46021#@gui : Number of Scales = int(4,2,16)
46022#@gui : sep = separator()
46023#@gui : Starting Scale (%) = float(25,0,400)
46024#@gui : Ending Scale (%) = float(100,0,400)
46025#@gui : Non-Linearity = float(0,-1,1)
46026#@gui : Rescaling = choice(3,"Bloc","Linear","Cubic","Lanczsos")
46027#@gui : sep = separator()
46028#@gui : X-Centering = float(0.5,0,1)
46029#@gui : Y-Centering = float(0.5,0,1)
46030#@gui : Angle = float(0,-180,180)
46031#@gui : sep = separator()
46032#@gui : Enable Interpolated Motion = bool(0)
46033#@gui : Ending X-Centering = float(0.5,0,1)
46034#@gui : Ending Y-Centering = float(0.5,0,1)
46035#@gui : Ending Angle = float(0,-180,180)
46036#@gui : sep = separator()
46037#@gui : G'MIC Operator = text("")
46038#@gui : Return Scaling = choice("None","Bloc","Linear","Cubic","Lanczos")
46039#@gui : Lock Return Scaling to Source Layer = bool(0)
46040#@gui : sep = separator()
46041#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/30/03</i>.</small>")
46042fx_apply_multiscale : skip "${13=}"
46043  repeat $! l[$<]
46044    w0={w} h0={h}
46045    apply_scales "$13",$1,$2%,$3%,{10^$4},{arg(1+$5,1,3,5,6)}
46046    if $8" || "($9" && "$8!=$12) to_a N=$! repeat $!
46047      angle={$9?$8+($12-$8)*$>/max($N-1,1):$8}
46048      rotate[$>] $angle
46049    done fi
46050    if $14
46051      if $15 siz=$w0,$h0 else siz=${-max_wh} fi
46052      r $siz,1,100%,{arg($14,1,3,5,6)}
46053      c 0,255
46054    fi
46055    w=${-max_w} h=${-max_h} N=$!
46056    repeat $!
46057      cx={$9?$6+($10-$6)*$>/max($N-1,1):$6}
46058      cy={$9?$7+($11-$7)*$>/max($N-1,1):$7}
46059      gui_set_layer_pos[$>] {$>,($w-w)*$cx},{$>,($h-h)*$cy}
46060    done
46061  endl done
46062
46063fx_apply_multiscale_preview :
46064  repeat $! l[$>]
46065    fx_apply_multiscale $"*"
46066    N={int(sqrt($!))} N={round($!/$N,1,1)} r2dy {100/$N}%
46067    to_rgba
46068    max_wh=${-max_wh}
46069    N=$! repeat $! l[$>]
46070      cx={$9?$6+($10-$6)*$>/max($N-1,1):$6}
46071      cy={$9?$7+($11-$7)*$>/max($N-1,1):$7}
46072      r $max_wh,1,100%,0,0,$cx,$cy
46073      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]
46074    endl done
46075    frame 1,1,0 frame 3,3,255 append_tiles ,
46076  endl done
46077
46078#@gui Pack : fx_pack, fx_pack_preview(1) : *
46079#@gui : Order By = choice(2,"Width","Height","Maximum Dimension","Area","Name")
46080#@gui : Tends to Be Square = bool(1)
46081#@gui : Force Transparency = bool(1)
46082#@gui : Add Image Label = bool(0)
46083#@gui : Font Height (px) = float(16,0,64)_0
46084#@gui : Font Colors = choice(1,"White on black","Black on white")_0
46085#@gui : sep = separator()
46086#@gui : Output Coordinates File = _bool(0)
46087#@gui : Output Folder = _folder()
46088#@gui : sep = separator()
46089#@gui : note = note{"<small>This filter tries to pack all input layers into a single image, while trying to
46090#@gui : minimize the empty areas.
46091#@gui : This problem being NP-hard, the algorithm finds (of course) a <b>non-optimal</b>, but often acceptable
46092#@gui : solution to this packing problem.</small>"}
46093#@gui : sep = separator()
46094#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/03/20</i>.</small>")
46095fx_pack : skip "${8=}"
46096  if $4 to_rgba repeat $! l[$>] nm0={n} gui_layer_name nm=${}
46097    0 t. {``$nm},3,0,$5,1,1 frame. 1,1,0
46098    if $6 *. -1 fi
46099    n. 0,255 to_rgba. r. {[w+2,h+1,1,4]},0,0,0,1
46100    rv a y,{w#0>w?0.5:0} nm $nm0
46101  endl done
46102  fi
46103  if $3 to_a fi
46104  repeat $! gui_layer_name[$>] nm$>=${} nm[$>] {`lowercase(['${nm$>}'])`} done
46105  c0="w" c1="h" c2="max(w,h)" c3="w*h" c4="n"
46106  pack $2,${c$1} coords=${}
46107  if $7
46108    repeat 256 filename "$8/gmic_pack.txt",$> filename=${} if isfile(['{/$filename}']) else break fi done
46109    if !narg($filename) filename="$8/gmic_pack.txt" fi
46110    l[] repeat narg($coords)/2
46111      x={arg(1+2*$>,$coords)} y={arg(2+2*$>,$coords)}
46112      ('"Image ""#"{1+$>}" ("${nm$>}"): "$x,$y\n')
46113    done a x ot $filename rm endl
46114  fi
46115  nm "name(G'MIC packing),pos(0,0),mode(normal)"
46116  if $4 autocrop fi
46117
46118fx_pack_preview : skip "${8=}"
46119  if !$! return fi
46120  w={w} h={h}
46121  filled=0 repeat $! filled={$>,$filled+w*h} done
46122  fx_pack $1,$2,$3,$4,$5,$6,0
46123  area={w*h}
46124  to_rgba rr2d $w,$h,0
46125  i[0] $w,16,1,4,255 t[0] "Filled: "{round(100*$filled/$area)}%,3,1,14,1,0,0,0,255
46126  a y,0.5
46127  u "{$1}{$2}{$3}{$4}"\
46128    "{$5}_"{2*$4}\
46129    "{$6}_"{2*$4}\
46130    "{$7}{$8}"
46131
46132#@gui Stroke : fx_stroke, fx_stroke_preview(0)
46133#@gui : Thickness (px) = int(3,1,256)
46134#@gui : Threshold (%) = float(50,0,100)
46135#@gui : Smoothness (px) = float(0,0,10)
46136#@gui : Shape = choice(2,"Square","Diamond","Round")
46137#@gui : Direction = choice(1,"Inward","Outward")
46138#@gui : sep = separator()
46139#@gui : Zoom (%) = float(100,1,300)
46140#@gui : X-Shift (px) = int(0,-256,256)
46141#@gui : Y-Shift (px) = int(0,-256,256)
46142#@gui : sep = separator()
46143#@gui : Starting Color = color(255,255,255,255)
46144#@gui : Ending Color = color(255,255,255,255)
46145#@gui : Inside Color = color(0,0,0,0)
46146#@gui : Outside Color = color(0,0,0,0)
46147#@gui : sep = separator()
46148#@gui : Output Stroke Layer On = choice(1,"Bottom","Top")
46149#@gui : Keep Original Image Size = bool(0)
46150#@gui : sep = separator()
46151#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/24/06</i>.</small>")
46152fx_stroke :
46153  to_a repeat $! l[$<] nm={n}
46154    if !$26" && "$5 expand_xy $1,0 is_frame1=0 else expand_xy 1,0 is_frame1=1 fi
46155    split_opacity +l.
46156      b $3
46157      if $6>=100
46158        shift $7,$8
46159        if $6!=100 wh={w},{h} r $6%,$6%,1,1,3 r $wh,1,1,0,0,0.5,0.5 fi
46160      else
46161        if $6!=100 wh={w},{h} r $6%,$6%,1,1,3 r $wh,1,1,0,0,0.5,0.5 fi
46162        shift $7,$8
46163      fi
46164      > {99.99-min(99.99,$2)}%
46165      distance $5,$4
46166      ($9^$10^$11^$12)
46167      if $1>1 ($13^$14^$15^$16) a[-2,-1] x r. $1,1,1,4,3 c. 0,255 fi
46168      i.. ($21^$22^$23^$24) ($17^$18^$19^$20) if $5 rv[-3,-1] fi
46169      a[-3--1] x map.. .,1 rm.
46170      nm $nm
46171    endl
46172    a[0,1] c
46173    if $is_frame1 shrink_xy 1 fi
46174    if $25 rv fi
46175  endl done
46176
46177fx_stroke_preview :
46178  repeat $! l[$>]
46179    fx_stroke $*
46180    nm foo
46181    gui_merge_layers
46182  endl done
46183
46184#@gui Tiles to Layers : split_tiles, fx_tiles2layers_preview(1)
46185#@gui : X-Tiles = int(3,1,100)
46186#@gui : Y-Tiles = int(3,1,100)
46187#@gui : Force Tiles to Have Same Size = _bool(false)
46188#@gui : sep = separator()
46189#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46190fx_tiles2layers_preview :
46191  split_tiles $1,$2,$3 to_rgba frame 1,1,0,0,0,255 frame 3,3,0,0,0,0 append_tiles ,
46192
46193#@gui Tones to Layers : fx_tones2layers, fx_tones2layers_preview(0)
46194#@gui : Number of Tones = int(3,2,10)
46195#@gui : Start of Mid-Tones = int(85,0,255)
46196#@gui : End of Mid-Tones = int(170,0,255)
46197#@gui : Smoothness = float(0.5,0,5)
46198#@gui : Alpha = choice("Binary","Scalar")
46199#@gui : sep = separator()
46200#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/05/04</i>.</small>")
46201fx_tones2layers :
46202  sval=$2 eval={max($2,$3)}
46203  remove_opacity repeat $! l[$<]
46204    +luminance rv
46205    repeat $1-1
46206      [1]
46207      val0={$sval+($eval-$sval)*$>/($1-2)}
46208      val1={$sval+($eval-$sval)*($>+1)/($1-2)-1}
46209      +ir[0] $val0,$val1
46210      if $5 *. [0] b. $4% n. 0,255  # Scalar alpha.
46211      else b. $4% n. 0,255  # Binary alpha.
46212      fi
46213      a[-2,-1] c
46214    done
46215    rm[0] rv
46216  endl done
46217
46218fx_tones2layers_preview :
46219  fx_tones2layers $* rv
46220  r {100/$!}%,{100/$!}%,1,100%,2
46221  to_rgba frame 1,1,0,0,0,255 frame 3,3,0,0,0,0 append_tiles ,
46222
46223#@gui ____<b>Lights & Shadows</b>
46224#---------------------------------
46225
46226#@gui Burn : fx_burn, fx_burn_preview(1)
46227#@gui : Amplitude = float(0.5,0,1)
46228#@gui : Scale = float(30,1,100)
46229#@gui : Smoothness = float(1,0,4)
46230#@gui : sep = separator()
46231#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46232#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46233#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46234#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46235#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46236#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46237#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46238#@gui : Value Action = choice("None","Cut","Normalize")
46239#@gui : sep = separator()
46240#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46241#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46242#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46243#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46244#@gui : sep = separator()
46245#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/24/11</i>.</small>")
46246_fx_burn :
46247  repeat $! l[$>]
46248    w,h={[w,h]}
46249    +norm
46250    fx_fourier. 2
46251    +rows. 0,{$h-1} r. $2%,$2%,1,100%,0,0,0.5,0.5 b. $3%
46252    j.. .,{($w-w)/2},{($h-h)/2} rm.
46253    fx_fourier[-2,-1] 2
46254    blend overlay,$1
46255  endl done
46256
46257fx_burn :
46258  ac "_fx_burn ${1-3}",$4,$5
46259
46260fx_burn_preview :
46261 gui_split_preview "fx_burn ${^0}",${-3--1}
46262
46263#@gui Contrast Swiss Mask : fx_contrast_swm , fx_contrast_swm(0)
46264#@gui : sep = separator()
46265#@gui : Blur the Mask = float(2,0.5,10)
46266#@gui : sep = separator()
46267#@gui : note = note ("Contrast Mask need the negative of the mask")
46268#@gui : Skip to Use the Mask to Boost = bool(false)
46269#@gui : note = note ("Uncheck for Contrast Mask,Check for Contrast Boost")
46270#@gui : sep = separator()
46271#@gui : note = note("Merge the Mask")
46272#@gui : Intensity = float(1,0,1)
46273#@gui : sep = separator()
46274#@gui : note = note("<small>Author: <i>PhotoComiX</i>.      Latest Update: <i>2011/01/01</i>.</small>")
46275#@gui : url = link("Filter explained here","http://www.gimpchat.com/viewtopic.php?f=9&t=864")
46276fx_contrast_swm :
46277  repeat $! l[$>] split_opacity l[0]
46278   +luminance to_rgb
46279    blur_xy[1] $1,$1
46280    if $2==0 negate[1] fi
46281    rv blend hardlight,$3
46282  endl a c endl done
46283
46284#@gui Drop Shadow : fx_drop_shadow, fx_drop_shadow(1)
46285#@gui : X-Shadow = float(3,-20,20)
46286#@gui : Y-Shadow = float(3,-20,20)
46287#@gui : Smoothness = float(1.8,0,5)
46288#@gui : Curvature = float(0,0,1)
46289#@gui : Corner Brightness = float(0,0,1)
46290#@gui : Angle = float(0,0,360)
46291#@gui : sep = separator()
46292#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/14/11</i>.</small>")
46293fx_drop_shadow :
46294  * -1 + 255 vignette {255*$5},80,95 * -1 + 255
46295  drop_shadow $1%,$2%,$3%,$4 rotate $6,1,0
46296
46297#@gui Drop Shadow 3D : fx_drop_shadow3d, fx_drop_shadow3d_preview(1)
46298#@gui : X-Angle = float(0,-90,90)
46299#@gui : Y-Angle = float(0,-90,90)
46300#@gui : Z-Angle = float(0,-90,90)
46301#@gui : Zoom = float(0,-100,100)
46302#@gui : X-Offset = float(1,-50,50)
46303#@gui : Y-Offset = float(1,-50,50)
46304#@gui : Perspective = float(2,0,10)
46305#@gui : Smoothness = float(0.5,0,5)
46306#@gui : Color = color(0,0,0,200)
46307#@gui : Preview Only Shadow = bool(0)
46308#@gui : sep = separator()
46309#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/02/07</i>.</small>")
46310fx_drop_shadow3d :
46311  repeat $! l[$<]
46312    +_fx_drop_shadow3d $*
46313  endl done
46314
46315fx_drop_shadow3d_preview :
46316  repeat $! l[$<]
46317    if $13 _fx_drop_shadow3d $*
46318    else +_fx_drop_shadow3d $* rv blend alpha
46319    fi
46320  endl done
46321
46322_fx_drop_shadow3d :
46323  point3d 0,0,1 r3d. 1,0,0,$1 r3d. 0,1,0,$2 r3d. 0,0,1,$3
46324  u={i(0,8)} v={i(0,9)} w={i(0,10)} rm.
46325  to_a channels 100% if im==iM return fi
46326  +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)'
46327  +*. 'y/h-0.5' *.. 'x/w-0.5' +.. {0.5-$5/100} +. {0.5-$6/100} *.. {w} *. {h}
46328  a[-2,-1] c warp[0] .,0,1,0 rm.
46329  b $8% n 0,$12 i.. ($9^$10^$11) r.. .,.,1,3 a[-2,-1] c
46330
46331#@gui Equalize Light : fx_equalize_light, fx_equalize_light_preview(1)
46332#@gui : Amount (%) = float(75,0,100)
46333#@gui : Mode = choice("Preserve range","Preserve covariance")
46334#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46335#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46336#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46337#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46338#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46339#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46340#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46341#@gui : sep = separator()
46342#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46343#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46344#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46345#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46346#@gui : sep = separator()
46347#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/03/23</i>.</small>")
46348fx_equalize_light :
46349  ac "_fx_equalize_light $1,$2",$3,1
46350
46351_fx_equalize_light :
46352  repeat $! l[$>] split_opacity l[0]
46353    . +b. {max(0.1,100-$1)}% -[-2,-1]
46354    if $2 transfer_pca. .. else n. ..,.. fi
46355    rm..
46356  endl a c endl done
46357
46358fx_equalize_light_preview :
46359  gui_split_preview "fx_equalize_light $*",${-3--1}
46360
46361#@gui Equalize Shadow : fx_equalize_shadow, fx_equalize_shadow_preview(1)
46362#@gui : Amplitude = float(1,0,1)
46363#@gui : sep = separator()
46364#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46365#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46366#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46367#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46368#@gui : sep = separator()
46369#@gui : note = note("<small>Authors: <i>Francois Grassard</i> and <i>David Tschumperlé</i>.
46370#@gui :       Latest Update: <i>2021/03/23</i>.</small>")
46371fx_equalize_shadow :
46372  repeat $! l[$>] +negate blend softlight,$1 endl done
46373
46374fx_equalize_shadow_preview :
46375  gui_split_preview "fx_equalize_shadow $1",${-3--1}
46376
46377#@gui Guided Light Rays : fx_guided_lightrays,fx_guided_lightrays_preview(1) : +
46378#@gui : Amplitude (%) = float(10,0,100)
46379#@gui : Ray Length = float(2,0,2)
46380#@gui : Mode = choice("Boundary","Dense")
46381#@gui : Density (%) = float(80,0,100)
46382#@gui : Smoothness (%) = float(0.1,0,5)
46383#@gui : Threshold (%) = float(50,0,100)
46384#@gui : Light Position = point(50,50,0,1,255,255,0,-128,1%)
46385#@gui : Light Color = color(255,255,255)
46386#@gui : Blend Mode = choice(7,"Add","Alpha","Grain Merge","Hard Light","Lighten","Lightness",
46387#@gui : "Luminance","Overlay","Soft Light","Value")
46388#@gui : Opacity (%) = float(100,0,100)
46389#@gui : sep = separator()
46390#@gui : Preview Light Shape = bool(1)
46391#@gui : sep = separator()
46392#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/04/06</i>.</small>")
46393fx_guided_lightrays :
46394  bmode=${"arg0 $12,add,alpha,grainmerge,hardlight,lighten,lchlightness,\
46395           luminance,overlay,softlight,value"}
46396  if $3 cond="i" else cond="i && !(j(-1) && j(1) && j(0,-1) && j(0,1))" fi
46397  if 0$_is_preview" && "$14 i[1] [0] gui_set_layer_opacity[1] 50 fi
46398
46399  l[0] # Process top layer only (light shape).
46400    if s==2" || "s==4 channels 100% else compose_channels max fi
46401    ge $6%
46402
46403    # Draw lightrays.
46404    100%,100%
46405    eval.. "*
46406      const L = $2<1?$2:$2^5;
46407      const Xl = $7<0?3*$7:$7>100?100+3*($7-100):$7;
46408      const Yl = $8<0?3*$8:$8>100?100+3*($8-100):$8;
46409      const xl = Xl*(w - 1)/100;
46410      const yl = Yl*(h - 1)/100;
46411      "$cond" && u<=$4%?(
46412        u = x - xl; v = y - yl;
46413        polygon(#-1,2,xl,yl,xl + L*u,yl + L*v,-1,255);
46414     )"
46415
46416    equalize. 65536,1,{iM} n. 0,1 power={10^(-$1%)} pow. {max(1e-2,$power)} b. $5% n. 0,255
46417    i[-2] (${9-11}:cyzx) r.. .,.,1,3 a[-2,-1] c
46418    k. gui_set_layer_mode $bmode gui_set_layer_opacity $13
46419  endl
46420  if 0$_is_preview gui_merge_layers
46421  elif 0$_output_mode k[0]
46422  fi
46423
46424fx_guided_lightrays_preview :
46425  _is_preview=1
46426  fx_guided_lightrays $*
46427
46428#@gui Illuminate 2D Shape : fx_illuminate_shape2d,fx_illuminate_shape2d_preview(1)+
46429#@gui : note = note("<small><b>Input / Output:</b></small>)
46430#@gui : Input Type = choice{"Single Opaque Shapes Over Transp. BG","Multiple Colored Shapes Over Transp. BG",
46431#@gui : "Bump Map","Normal Map"}
46432#@gui : Output Type = choice{"Illumination","Bump Map","Normal Map"}
46433#@gui : Input Guide Color = color(255,0,0,255)
46434#@gui : Keep Base Layer as Input Background = bool(1)
46435#@gui : Keep Transparency in Output = bool(1)
46436#@gui : sep = separator()
46437#@gui : note = note("<small><b>Shape:</b></small>)
46438#@gui : Minimal Shape Area = int(4,1,100)
46439#@gui : note = note{"<small>Parameter <i>Minimal shape area</i> is only active in <i>Multiple colored shapes</i>
46440#@gui : input mode.</small>"}
46441#@gui : Preview Detected Shapes = bool(0)
46442#@gui : Erosion / Dilation = float(0,-10,10)
46443#@gui : Smoothness = float(3,0,6)
46444#@gui : Bump Factor = float(1,-5,5)
46445#@gui : Avg / Max Weight = float(1,0,1)
46446#@gui : Resolution = choice{4,"Full (Slower)","2048","1024","512","256","128","64 (Faster)"}
46447#@gui : sep = separator()
46448#@gui : note = note("<small><b>Illumination:</b></small>)
46449#@gui : Blending Mode = choice(10,"Normal","Lighten","Screen","Dodge","Add","Darken","Multiply","Burn","Overlay",
46450#@gui : "Soft Light","Hard Light","Grain Merge")
46451#@gui : Opacity (%) = float(75,0,100)
46452#@gui : Ambient (%) = float(30,-100,100)
46453#@gui : Diffuse (%) = float(40,0,200)
46454#@gui : Specular (%) = float(40,0,300)
46455#@gui : Shininess = float(80,0,100)
46456#@gui : Smoothness = float(0.2,0,5)
46457#@gui : Flatness = float(1,0,3)
46458#@gui : Linearity = float(0,-100,100)
46459#@gui : Levels = int(0,0,16)
46460#@gui : Light-X = float(2,-20,20)
46461#@gui : Light-Y = float(-2,-20,20)
46462#@gui : Light-Z = float(2,0,20)
46463#@gui : Normalize Illumination = bool(0)
46464#@gui : sep = separator()
46465#@gui : Open Interactive Preview = button()
46466#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46467#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46468#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46469#@gui : sep = separator()
46470#@gui : note = note{"<small><b>Note:</b> This filter automatically adds illumination to an opaque shape defined
46471#@gui : over a transparent background.</small>"}
46472#@gui : sep = separator()
46473#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/05/18</i>.</small>")
46474fx_illuminate_shape2d :
46475  input_type,\
46476  output_type,\
46477  keep_input_bg,\
46478  preview_shapes,\
46479  blending_mode,\
46480  opacity=$1,$2,$7,$10,$16,$17
46481  blending_mode=${arg\ 1+$blending_mode,normal,lighten,screen,dodge,add,darken,multiply,burn,overlay,\
46482                  softlight,hardlight,grainmerge}
46483  keep_input_bg&={$!>1}
46484
46485  if $output_type # Output : bumpmap or normalmap
46486    repeat $!-$keep_input_bg _fx_illuminate_shape2d[$>] $* done
46487
46488  else # Output : illumination
46489    repeat $!-$keep_input_bg if $keep_input_bg sel=$>,-1 else sel=$> fi l[$sel]
46490      if !$keep_input_bg" && "$input_type>=2 # From bumpmap/normalmap w/o background
46491        _fx_illuminate_shape2d $*
46492      elif $keep_input_bg" && "$input_type>=2 # From bumpmap/normalmap w/ background
46493        _fx_illuminate_shape2d[0] $*
46494      elif !$keep_input_bg" && "$input_type<=1 # From shape w/o background
46495        +_fx_illuminate_shape2d $* rv
46496      else # From shape w/ background
46497        _fx_illuminate_shape2d[0] $*
46498      fi
46499      if !$preview_shapes" || "0$_is_preview!=1
46500        gui_set_layer_mode[0] $blending_mode
46501        gui_set_layer_opacity[0] $opacity
46502        if $!>1" && "(0$_output_mode==0" || "0$_is_preview==1)
46503          if $keep_input_bg" && "!0$_is_preview . fi
46504          gui_merge_layers[0,1]
46505        else k[0] fi
46506      fi
46507    endl done
46508  fi
46509
46510fx_illuminate_shape2d_preview :
46511  _is_preview=1
46512  input_type,\
46513  keep_input_bg,\
46514  preview_interactive=$1,$7,$-2
46515  keep_input_bg&={$!>1}
46516  if $preview_interactive fx_illuminate_shape2d_preview_interactive $* fi
46517  if $keep_input_bg
46518    repeat $!-1 l[$>,-1]
46519      fx_illuminate_shape2d $*
46520      rv to_colormode 0 a z
46521      gui_split_preview "slices 50%,100%",$-1
46522    endl done
46523  else
46524    gui_split_preview "fx_illuminate_shape2d $*",$-1
46525  fi
46526
46527fx_illuminate_shape2d_preview_interactive :
46528  _output_mode=0
46529  input_type,\
46530  keep_input_bg=$1,$7
46531  keep_input_bg&={$!>1}
46532  repeat $!-$keep_input_bg if $keep_input_bg sel=$>,-1 else sel=$> fi +l[$sel]
46533    to_rgba
46534    +_fx_illuminate_shape2d[0] $1,2,${3-6},0,1,""$9,0,${11-15},""${16-29},""0,0
46535    if $!>2 rm[0] elif $input_type>=2 sh[0] 0,2 f. 128 rm. fi
46536    siz=${fitscreen\ {[w,h,1]},256,640}
46537    wsiz0=${fitscreen\ $siz,1,30%,100%}
46538    wsiz=$wsiz0
46539    r $siz,1,100%,3
46540    s. c,-3 !=. 0 l.. - 128 / 127 s c,-2 / endl a[-2,-1] c # Gradient map
46541    rv s. c,-3
46542    (160,128;128,160) r. 16,16 r. ..,..,1,3,0,2
46543    30,30,1,1 circle. 50%,50%,15%,1,1 b. 4 n. 0,1
46544    100%,100%,1,3,[255,255,0]
46545    nm normal,rgb,alpha,background,light_alpha,light_rgb
46546
46547    w[] $wsiz,0,0,{rgb,([{*,u},{*,v}]-[$wsiz])/2},"[G'MIC] Illuminate 2D Shape"
46548    cursor 0
46549    x0,y0,ox,oy,ob,olightz=-1
46550    lightz=2 clicked=0
46551
46552    do
46553      x,y,b,mw={rgb,[{*,x},{*,y}]*[w,h]/[{*,w},{*,h}]},{*,b},{*,-o}
46554      lightz={cut($lightz-0.3*sign($mw)+($y0>=0?3*($y-$y0)/h),0.1,4)}
46555      if $x<0 x,y={rgb,ang=$|;(1+[cos(1.4*ang),sin(0.85*ang)])*[w,h]/2} fi
46556      if !$b" || "($b&1)
46557        if $b" && "!$clicked x0,y0=$x,$y
46558        elif !$b x0,y0=-1
46559        fi
46560        lightx,lighty={rgb,3.5*(2*[$x/w,$y/h]-1)}
46561        if [$ox,$oy,$ob,$olightz]!=[$x,$y,$b,$lightz]
46562          +fx_illuminate_shape2d[normal,rgb] 4,0,${3-6},1,1,""$9,0,${11-15},""${16-25},$lightx,$lighty,$lightz,$29,""0,0
46563          +j[background] .,0,0,0,0,1,[alpha],255 rm..
46564          +r2dx[light_alpha,light_rgb] {light_rgb,8+$lightz*(w-8)} j... .,{[$x,$y]-[w,h]/2},0,0,1,.. rm[-2,-1]
46565          r. $wsiz,1,100% to. "Light: ("{``{_round([$lightx,$lighty,$lightz],0.1)}}")",2,2,16
46566          w.  rm. wait 20
46567        else wait
46568        fi
46569        clicked=$b
46570      elif $b&2
46571        +j[background] [rgb],0,0,0,0,1,[alpha],255
46572        +r2dx[light_alpha,light_rgb] {light_rgb,8+$lightz*(w-8)} j... .,{[$x,$y]-[w,h]/2},0,0,1,.. rm[-2,-1]
46573        w. rm. wait
46574      fi
46575      if {*,CTRLLEFT}" && "{*,-D} w[] {1.5*[{*,w},{*,h}]} wsiz={*,w},{*,h}
46576      elif {*,CTRLLEFT}" && "{*,-C} w[] {0.75*[{*,w},{*,h}]} wsiz={*,w},{*,h}
46577      elif {*,CTRLLEFT}" && "{*,-R} w[] $wsiz0
46578      fi
46579      ox,oy,ob=$x,$y,$b
46580
46581    while {*}" && "!{*,ESC}" && "!{*,Q}
46582    w 0
46583  rm endl done
46584
46585_fx_illuminate_shape2d : # Input selection must contains a single image. Output is a single image.
46586  input_type,\
46587  output_type,\
46588  gR,gG,gB,gA,\
46589  keep_input_bg,\
46590  keep_output_transparency,\
46591  min_shape_area,\
46592  preview_shapes,\
46593  dilation,\
46594  shape_smoothness,\
46595  bump_factor,\
46596  weight_avg_max,\
46597  resolution,\
46598  blending_mode,\
46599  opacity,\
46600  ambient,\
46601  diffuse,\
46602  specular,\
46603  shininess,\
46604  light_smoothness,\
46605  flatness,\
46606  linearity,\
46607  levels,\
46608  lightx,\
46609  lighty,\
46610  lightz,\
46611  normalize_illumination,\
46612  preview_interactive,\
46613  preview_mode=${1-31}
46614
46615  # Generate 2D binary shape and corresponding bumpmap
46616  nm={n}
46617  if $input_type==0 # Single opaque shape
46618    to_rgba
46619    +channels. 100% >. 0 .
46620    select_color... 0,$gR,$gG,$gB,$gA
46621    mv... $! -[-2,-1]
46622
46623  elif $input_type==1 # Multiple colored shapes
46624    to_rgba
46625    +channels. 100% >. 0 *[-2,-1]
46626    if $min_shape_area>1 +quantize_area. {$min_shape_area^2} fi
46627    s. c,-{s-1} >. 0 rv[-2,-1]
46628    if s>1 f. "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm. round. 0.01 fi
46629    label. 0,0 f. "j(1)!=i || j(0,1)!=i" thinning. 1 ==. 0 *. ..
46630    select_color... 0,$gR,$gG,$gB,$gA
46631    mv... $! -[-2,-1]
46632
46633  elif $input_type==2 # Bump map
46634    to_a
46635    s c,-{s-1} >. 0 *.. . rv s. c S={$!-1} +[^0] /. $S
46636
46637  elif $input_type==3 # Normal map
46638    +channels 100% >. 0 *.. . rv
46639    f. "I==vector4(0)?[128,128,255,255]:I"
46640    channels. 0,2
46641
46642  else # Gradient map (hidden mode used by interactive preview)
46643    +channels 100% rv
46644  fi
46645
46646  if 0$_is_preview" && "$preview_shapes
46647    if $input_type==3 k[0] else k. fi
46648    +dilate. 3
46649    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.
46650    *. 255 a c
46651    return
46652  fi
46653
46654  if $input_type<=1
46655    shape2bump. {arg($resolution,2048,1024,512,256,128,64)},$weight_avg_max,{$dilation%*max(w,h)},\
46656                {$shape_smoothness*50}
46657  fi
46658  if $input_type<=2
46659    if $input_type==2" && "$shape_smoothness mM={[im,iM]} guided. ..,$shape_smoothness%,100 n. $mM fi
46660    *. $bump_factor
46661  fi
46662
46663  # Generate output.
46664  if $output_type==1 # Output as a bump map
46665    if $input_type<=2
46666       if $keep_output_transparency k[-2,-1] n 0,255 rv a c  # With transparency
46667       else k. n 0,255                                       # Without transparency
46668       fi
46669    else
46670      rm gui_error_preview "Cannot convert a normal map to a bump map." return
46671    fi
46672
46673  elif $output_type==2 # Output as a normal map
46674    if $input_type<=2 round 0.0001 bump2normal. f. "i(#-2)?I:[128,128,255]" fi
46675    if $keep_output_transparency k[-2,-1] rv *. 255 a c # With transparency
46676    else k.                                             # Without transparency
46677    fi
46678
46679  else # Output as illumination layer (phong model)
46680    if $input_type<=2 g. xy a[-2,-1] c
46681    elif $input_type==3 -. 128 /. 127 s. c,-2 /[-2,-1]
46682    fi
46683    f. "*
46684      begin(
46685        const flatness = "$flatness";                 # Surface flatness
46686        const ka = "$ambient"%;                       # Ambient
46687        const kd = "$diffuse"%;                       # Diffuse
46688        const ks = "$specular"%;                      # Specular
46689        const alpha = "$shininess";                   # Specularity
46690        const m1 = max(1,"$lightz");
46691        const mwh1 = max(w,h) - 1;
46692        light = [ "m1*$lightx,m1*$lighty,-$lightz" ]; # Light position
46693        camera = [ 0,0,-"$lightz" ];                  # Camera position
46694      );
46695      res = i#0?(
46696        P = [ 2*x/mwh1 - 1,2*y/mwh1 - 1,0 ];
46697        L = light - P;
46698        L/=norm(L);
46699        V = camera - P;
46700        V/=norm(V);
46701        N = -[ i0,i1,flatness ];
46702        N/=norm(N);
46703        R = 2*dot(N,L)*N - L;
46704        res = ka + kd*dot(L,N) + ks*max(dot(R,V),0)^alpha;
46705      ):0;
46706      [ res,0 ]"
46707    channels. 0 *. 255 c. 0,255
46708    if $light_smoothness" || "$linearity
46709      mM={[im,iM]}
46710      if $light_smoothness b. $light_smoothness% fi
46711      if $linearity n. 0,1 ^. {10^-($linearity%)} fi
46712      n. $mM
46713    fi
46714    if $levels quantize. $levels,1,1 fi
46715    if $normalize_illumination n. 0,255 fi
46716    rv[-2,-1] *. 255 a[-2,-1] c
46717    nm $nm
46718    if !$keep_output_transparency remove_opacity. fi
46719  fi
46720  nm $nm
46721
46722#@gui Light Glow : fx_lightglow, fx_lightglow_preview(0)
46723#@gui : Density = float(30,0,100)
46724#@gui : Amplitude = float(0.5,0,2)
46725#@gui : Mode = choice(8,"Burn","Dodge","Freeze","Grain Merge","Hard Light","Interpolation","Lighten","Multiply",
46726#@gui : "Overlay","Reflect","Soft Light","Stamp","Value")
46727#@gui : Opacity = float(0.8,0,1)
46728#@gui : sep = separator()
46729#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46730#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46731#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46732#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46733#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46734#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46735#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46736#@gui : sep = separator()
46737#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46738#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46739#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46740#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46741#@gui : sep = separator()
46742#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/21/02</i>.</small>")
46743_fx_lightglow :
46744  mode=${arg\ 1+$3,burn,dodge,freeze,grainmerge,hardlight,interpolation,lighten,multiply,overlay,reflect,\
46745         softlight,stamp,value}
46746  repeat $!
46747    +gradient_norm. >=. {100-$1}% distance. 1 ^. $2 *. -1 n. 0,255 blend $mode,$4
46748  mv. 0 done
46749
46750fx_lightglow :
46751  ac "_fx_lightglow ${1-4}",$5
46752
46753fx_lightglow_preview :
46754  gui_split_preview "fx_lightglow $*",${-3--1}
46755
46756#@gui Light Leaks : fx_light_leaks, fx_light_leaks_preview(1)
46757#@gui : Leak Type = int(0,0,70)
46758#@gui : Angle = float(0,-180,180)
46759#@gui : X-Scale = float(1,1,10)
46760#@gui : Y-Scale = float(1,1,10)
46761#@gui : Hue = float(0,-180,180)
46762#@gui : Opacity = float(0.85,0,1)
46763#@gui : Blend Mode = choice(2,"Normal","Lighten","Screen","Dodge","Add","Darken","Multiply","Burn","Overlay",
46764#@gui : "Soft Light","Hard Light","Difference","Subtract","Grain Extract","Grain Merge","Divide","Hue","Saturation",
46765#@gui : "Value")
46766#@gui : Output as Separate Layers = _bool(1)
46767#@gui : sep = separator()
46768#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46769#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46770#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46771#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46772#@gui : sep = separator()
46773#@gui : note = note{"<small>This filter uses the free light leaks dataset available at :</small>"}
46774#@gui : url = link{"Lomo Light Leaks","http://www.photoshoptutorials.ws/downloads/mockups-graphics/lomo-light-leaks/"}
46775#@gui : sep = separator()
46776#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/01/07</i>.</small>")
46777fx_light_leaks :
46778  filename=lightleak_${"padint $1",6}.cimgz
46779  input_cached data_lightleaks/$filename
46780  mode=${arg\ 1+$7,normal,lighten,screen,dodge,add,darken,multiply,burn,overlay,softlight,hardlight,difference,\
46781         subtract,grainextract,grainmerge,divide,hue,saturation,value}
46782  mv. 0
46783  repeat $!-1 l[0,{1+$<}]
46784    +r[0] {1,w},{1,h},1,3,5
46785    rotate. $2,1,1,50%,50%
46786    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
46787    c. 0,255
46788    if $5 rgb2hsv. sh. 0 +. $5 rm. hsv2rgb. fi
46789    if $8
46790      nm=${gui_layer_name[1]}
46791      nm. name($nm),opacity({$6*100}),mode($mode) rv[-2,-1]
46792    else blend[1,-1] $mode,$6 fi
46793  endl done
46794  rm[0]
46795
46796fx_light_leaks_preview :
46797  gui_split_preview "fx_light_leaks ${1--5},0",${-3--1}
46798
46799_fx_light_leaks :
46800  u="" repeat 71 if narg($u) u=$u, fi u=${u}lightleak_${"padint "$>,6} done
46801  u $u
46802
46803#@gui Light Patch : fx_light_patch, fx_light_patch(0)
46804#@gui : Density = int(5,2,30)
46805#@gui : Darkness = float(0.7,0,1)
46806#@gui : Lightness = float(2.5,1,4)
46807#@gui : sep = separator()
46808#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46809#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46810#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46811#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46812#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46813#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46814#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46815#@gui : sep = separator()
46816#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46817fx_light_patch :
46818  repeat $! l[$>] split_opacity l[0]
46819    ac "light_patch $1,$2,$3",$4
46820  endl a c endl done
46821
46822#@gui Light Rays : fx_lightrays, fx_lightrays(1)
46823#@gui : Density = float(80,0,100)
46824#@gui : Center (%) = point(50,50,0,1)
46825#@gui : Length = float(1,0,1)
46826#@gui : Attenuation = float(0.5,0,1)
46827#@gui : Transparency = bool(0)
46828#@gui : sep = separator()
46829#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/03/01</i>.</small>")
46830fx_lightrays :
46831  lightrays $1,$2%,$3%,$4,$5
46832  if $6 repeat $! r[$>] 100%,100%,1,{{$>,s}+({$>,s}%2)} done fi
46833
46834#@gui Pop Shadows : fx_pop_shadows, fx_pop_shadows_preview(1)
46835#@gui : Strength = float(0.75,0,1)
46836#@gui : Scale = float(5,0,20)
46837#@gui : Post-Normalize = bool(1)
46838#@gui : sep = separator()
46839#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
46840#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
46841#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
46842#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
46843#@gui : sep = separator()
46844#@gui : note = note("<small>Authors: <i>Morgan Hardwood</i> and <i>David Tschumperlé</i>.
46845#@gui :       Latest Update: <i>2017/03/05</i>.</small>")
46846fx_pop_shadows :
46847  repeat $! l[$>] split_opacity l[0]
46848    .x2
46849    luminance.. negate.. imM={-2,[im,iM]} b.. $2% n.. $imM
46850    blend[0,1] overlay,$1
46851    max
46852    if $3 n 0,255 fi
46853  endl a c endl done
46854
46855fx_pop_shadows_preview :
46856  gui_split_preview "fx_pop_shadows $*",${-3--1}
46857
46858#@gui Relief Light : fx_light_relief, fx_light_relief(1)
46859#@gui : Ambient Lightness = float(0.3,0,5)
46860#@gui : Specular Lightness = float(0.2,0,2)
46861#@gui : Specular Size = float(0.2,0,1)
46862#@gui : Darkness = float(0,0,1)
46863#@gui : Light Smoothness = float(1,0,5)
46864#@gui : XY-Light = point(50,50,0,1,255,255,128,200,10)
46865#@gui : Z-Light = float(5,0,20)
46866#@gui : Z-Scale = float(0.5,0,3)
46867#@gui : Opacity as Heightmap = bool(0)
46868#@gui : Image Smoothness = float(0,0,10)
46869#@gui : sep = separator()
46870#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46871fx_light_relief :
46872  b $11% light_relief ${1-5},{[$6,$7]%},${8-10}
46873
46874#@gui Shadow Patch : fx_shadow_patch, fx_shadow_patch(1)
46875#@gui : Opacity = float(0.7,0,1)
46876#@gui : sep = separator()
46877#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
46878#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
46879#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
46880#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
46881#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
46882#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
46883#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
46884#@gui : sep = separator()
46885#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46886fx_shadow_patch :
46887  repeat $! l[$>] split_opacity l[0]
46888    ac "shadow_patch $1",$2
46889  endl a c endl done
46890
46891#@gui Slice Luminosity : fx_slice_luminosity, fx_slice_luminosity_preview
46892#@gui : Luminosity Type = choice(1,"Average RGB","Luminance","Lightness","Value")
46893#@gui : Output As = _choice(1,"Mask","Masked Image")
46894#@gui : Preview Type = choice(2,"Mask","Mask + Background","Image","Image + Background")
46895#@gui : sep = separator()
46896#@gui : note = note{"<small><b>Slice 1</b> (shadows):</small>"}
46897#@gui : Activate Slice 1 = bool(1)
46898#@gui : Starting Value = int(0,0,255)
46899#@gui : Ending Value = int(64,0,255)
46900#@gui : Starting Feathering = int(0,0,255)
46901#@gui : Ending Feathering = int(0,0,255)
46902#@gui : sep = separator()
46903#@gui : note = note{"<small><b>Slice 2</b> (low midtones):</small>"}
46904#@gui : Activate Slice 2 = bool(1)
46905#@gui : Starting Value = int(64,0,255)
46906#@gui : Ending Value = int(128,0,255)
46907#@gui : Starting Feathering = int(0,0,255)
46908#@gui : Ending Feathering = int(0,0,255)
46909#@gui : sep = separator()
46910#@gui : note = note{"<small><b>Slice 3</b> (high midtones):</small>"}
46911#@gui : Activate Slice 3 = bool()
46912#@gui : Starting Value = int(128,0,255)
46913#@gui : Ending Value = int(192,0,255)
46914#@gui : Starting Feathering = int(0,0,255)
46915#@gui : Ending Feathering = int(0,0,255)
46916#@gui : sep = separator()
46917#@gui : note = note{"<small><b>Slice 4</b> (highlights):</small>"}
46918#@gui : Activate Slice 4 = bool()
46919#@gui : Starting Value = int(192,0,255)
46920#@gui : Ending Value = int(255,0,255)
46921#@gui : Starting Feathering = float(0,0,255)
46922#@gui : Ending Feathering = float(0,0,255)
46923#@gui : sep = separator()
46924#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/22/09</i>.</small>")
46925fx_slice_luminosity :
46926  remove_opacity repeat $! l[$<] to_rgb
46927    _fx_slice_luminosity $*
46928    if $2 i[0] [0] a[-2,-1] c fi
46929    rv
46930  endl done
46931
46932fx_slice_luminosity_preview :
46933  remove_opacity  repeat $! l[$>] to_rgb
46934    _fx_slice_luminosity $*
46935    if $3==0 rm[0] channels {s-1}
46936    elif $3==1 100%,100%,1,1,128 a[0,-1] c r. 100%,100%,1,4 blend alpha
46937    elif $3==2 a c
46938    else +. 96 c. 0,255 a c
46939    fi
46940  endl done
46941
46942_fx_slice_luminosity :
46943  if $1==0 +compose_channels + /. 3
46944  elif $1==1 +luminance
46945  elif $1==2 +srgb2lab8. channels. 0
46946  else +compose_channels max
46947  fi
46948  if $4 +apply_curve[1] 0,{$5-$7-0.1},0,$5,255,$6,255,{$6+$8+0.1},0,512,0 fi
46949  if $9 +apply_curve[1] 0,{$10-$12-0.1},0,$10,255,$9,255,{$11+$13+0.1},0,512,0 fi
46950  if $14 +apply_curve[1] 0,{$15-$17-0.1},0,$15,255,$16,255,{$16+$18+0.1},0,512,0 fi
46951  if $19 +apply_curve[1] 0,{$20-$22-0.1},0,$20,255,$21,255,{$21+$23+0.1},0,512,0 fi
46952  rm[1] max[^0]
46953
46954#@gui ____<b>Patterns</b>
46955#------------------------
46956
46957#@gui Bayer Filter : rgb2bayer, rgb2bayer(0)
46958#@gui : Starting Pattern = choice(0,"Red-Green","Blue-Green","Green-Red","Green-Blue")
46959#@gui : Keep Colors = bool(1)
46960#@gui : sep = separator()
46961#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
46962
46963#@gui Box Fitting : fx_boxfitting, fx_boxfitting_preview(0)
46964#@gui : Minimal Size = int(3,1,32)
46965#@gui : Maximal Size = int(0,0,32)
46966#@gui : note = note("<small><b>Note:</b> Set <i>Maximal size</i> to <i>0</i> to allow any size
46967#@gui : for the squares.</small>")
46968#@gui : Initial Density = float(0.1,0,1)
46969#@gui : Transparency = bool(0)
46970#@gui : sep = separator()
46971#@gui : note = note("<small><b>Note:</b> This filter has been highly inspired by the work of Jared Tarbell,
46972#@gui : described on the page:</small>")
46973#@gui : url = link("http://www.complexification.net/gallery/machines/boxFittingImg/")
46974#@gui : sep = separator()
46975#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/06/06</i>.</small>")
46976fx_boxfitting :
46977  boxfitting ${1-3},3
46978  if $4 to_rgba replace_color 0,0,0,0,0,255,0,0,0,0 fi
46979
46980fx_boxfitting_preview :
46981  boxfitting ${1-3},1
46982  if $4 to_rgba replace_color 0,0,0,0,0,255,0,0,0,0 fi
46983
46984#@gui Camouflage : fx_camouflage, fx_camouflage
46985#@gui : Scale = int(9,2,12)
46986#@gui : Levels = int(12,2,32)
46987#@gui : Coherence = float(100,0,1000)
46988#@gui : Color 1 = color(30,46,33)
46989#@gui : Color 2 = color(75,90,65)
46990#@gui : Color 3 = color(179,189,117)
46991#@gui : Color 4 = color(255,246,158)
46992#@gui : sep = separator()
46993#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/26/10</i>.</small>")
46994fx_camouflage :
46995  repeat $! l[$>] split_opacity l[0]
46996    channels 0 r {w+16},{h+16},1,1,0 rand 0,16
46997    amp=$3 do smooth {min(50,$amp)},0,1 amp-=50 while $amp>0
46998    shrink_xy. 8 n 1,$2 round
46999    repeat $1 +area 0,0 <. {1+2^$>} inpaint[0] [1],0,3 rm. done
47000    +colormap 0 n.. 0,{w-1}
47001    4,1,1,3,"col=[${4-15}];col[3*x,3]"
47002    r. ..,..,1,3,3 rm.. map.. . rm.
47003  endl a c endl done
47004
47005#@gui Canvas : fx_canvas, fx_canvas_preview(0)
47006#@gui : note = note{"<b>First direction :</b>"}
47007#@gui : Amplitude = float(70,0,300)
47008#@gui : Angle = float(45,0,180)
47009#@gui : Sharpness = float(400,0,2000)
47010#@gui : note = note{"\n<b>Second direction : </b>"}
47011#@gui : Activate Second Direction = bool(true)
47012#@gui : Amplitude = float(70,0,300)
47013#@gui : Angle = float(135,0,180)
47014#@gui : Sharpness = float(400,0,2000)
47015#@gui : sep = separator()
47016#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47017#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47018#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47019#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47020#@gui : sep = separator()
47021#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47022fx_canvas :
47023  repeat $! l.
47024    if $4
47025      ({cos($2*pi/180)}^{sin($2*pi/180)}) vector2tensor. r. ..,.. +smooth.. .,$1 rm.. sharpen. $3 c. 0,255
47026      ({cos($6*pi/180)}^{sin($6*pi/180)}) vector2tensor. r. ..,.. smooth... .,$5 rm. sharpen.. $7 c.. 0,255
47027      +[-2,-1] /. 2
47028    else
47029      ({cos($2*pi/180)}^{sin($2*pi/180)}) vector2tensor. r. ..,.. smooth.. .,$1 rm. sharpen. $3 c. 0,255
47030    fi
47031  endl mv. 0 done
47032
47033fx_canvas_preview :
47034  gui_split_preview "fx_canvas $*",${-3--1}
47035
47036#@gui Canvas Texture : texturize_canvas, texturize_canvas(0)
47037#@gui : Amplitude = float(20,0,256)
47038#@gui : Fibrousness = float(3,0,20)
47039#@gui : Emboss = float(0.6,0,1)
47040#@gui : sep = separator()
47041#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47042
47043#@gui Cracks : fx_cracks, fx_cracks_preview(0)
47044#@gui : Density (%) = float(30,0,100)
47045#@gui : Relief = bool(true)
47046#@gui : Color = color(255,255,255,128)
47047#@gui : sep = separator()
47048#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
47049#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
47050#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
47051#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
47052#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
47053#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
47054#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
47055#@gui : sep = separator()
47056#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47057#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47058#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47059#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47060#@gui : sep = separator()
47061#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/07</i>.</small>")
47062fx_cracks :
47063  ac "cracks $1,$2,{$6/255},${3-5},255",$7
47064
47065fx_cracks_preview :
47066  gui_split_preview "fx_cracks $*",${-3--1}
47067
47068#@gui Crystal : fx_crystal, fx_crystal_preview(0)
47069#@gui : Density = float(50,0,100)
47070#@gui : Smoothness = float(0.2,0,2)
47071#@gui : Edges = float(20,0,100)
47072#@gui : sep = separator()
47073#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47074#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47075#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47076#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47077#@gui : sep = separator()
47078#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/19/01</i>.</small>")
47079fx_crystal :
47080  repeat $! l[$>] split_opacity l[0]
47081    s={s}
47082    +gradient_norm >=. {(100-$3)/5} remove_pixels. {100-max(0.1,$1*$3%)}%,{is} *
47083    +norm !=. 0 a c
47084    sigma=0.5
47085    do
47086      +b. $sigma sigma*={(1+$2)}
47087      sh[0,-1] $s max. .. rm[-2,-1]
47088      f. 'W=i(x,y,z,$s);if(W<0.001||W>=1,0,if(c<$s,i/W,1))'
47089      if !iM rm[1] break fi
47090      sh. $s
47091      j[0] [1],0,0,0,0,1,[2] k[0]
47092    while 1
47093    channels 0,{$s-1}
47094  endl a c endl done
47095
47096fx_crystal_preview :
47097  gui_split_preview "fx_crystal $*",${-3--1}
47098
47099#@gui Crystal Background : fx_crystal_background, fx_crystal_background
47100#@gui : Iterations = int(10,1,32)
47101#@gui : Density (%) = float(25,0,100)
47102#@gui : Random Seed = int(0,0,65535)
47103#@gui : Opacity (%) = float(100,0,100)
47104#@gui : Color = bool(1)
47105#@gui : sep = separator()
47106#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/18/10</i>.</small>")
47107fx_crystal_background :
47108  repeat $! l[$>] split_opacity l[0]
47109    N={2*max(3,round((${"is_percent $2"}?4*wh*$2:$2)))}
47110    if $5 col="u([255,255,255])" else col="u(255)" fi
47111    srand $3 M={max(w,h)} 2,$N
47112    repeat $1 rand. {-$M/2},{3*$M/2} polygon.. $N,{^},{-$4%},{$col} done
47113    rm. n 0,255
47114  endl a c endl done
47115
47116#@gui Halftone : fx_halftone, fx_halftone_preview(0)
47117#@gui : note = note("<b><small>Image parameters :</small></b>")
47118#@gui : Brightness (%) = float(0,-100,100)
47119#@gui : Contrast (%) = float(0,-100,100)
47120#@gui : Gamma (%) = float(0,-100,100)
47121#@gui : Smoothness = float(0,0,10)
47122#@gui : sep = separator()
47123#@gui : note = note("<b><small>Halftone parameters :</small></b>")
47124#@gui : Number of Tones = int(5,2,32)
47125#@gui : Size for Dark Tones = int(8,2,256)
47126#@gui : Size for Bright Tones = int(8,2,256)
47127#@gui : Shape = choice{5,"Square","Diamond","Circle","Square (Inv.)","Diamond (Inv.)","Circle (Inv.)"}
47128#@gui : Smoothness = float(0.1,0,32)
47129#@gui : sep = separator()
47130#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47131#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47132#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47133#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47134#@gui : sep = separator()
47135#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/23/07</i>.</small>")
47136fx_halftone :
47137  adjust_colors ${1-3},0,0,0,255 b $4
47138  repeat $! l[$>] split_opacity
47139    halftone[0] ${5-9}
47140  a c endl done
47141
47142fx_halftone_preview :
47143  gui_split_preview "fx_halftone $*",${-3--1}
47144
47145#@gui Hearts : fx_hearts, fx_hearts_preview(0)
47146#@gui : Density = float(2,0,30)
47147#@gui : sep = separator()
47148#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
47149#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
47150#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
47151#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
47152#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
47153#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
47154#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
47155#@gui : sep = separator()
47156#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47157#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47158#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47159#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47160#@gui : sep = separator()
47161#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47162fx_hearts :
47163  ac "hearts $1",$2
47164
47165fx_hearts_preview :
47166  gui_split_preview "fx_hearts $*",${-3--1}
47167
47168#@gui Lava : fx_lava, fx_lava_preview(0)
47169#@gui : Perturbation = int(8,0,15)
47170#@gui : Smoothness = float(5,0,100)
47171#@gui : Scale = float(3,0,20)
47172#@gui : Sharpness = float(0,0,1000)
47173#@gui : sep = separator()
47174#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47175#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47176#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47177#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47178#@gui : sep = separator()
47179#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/26/11</i>.</small>")
47180fx_lava :
47181  repeat $! l[$>] split_opacity l[0] norm
47182    100%,100% plasma. 1,1,{16-$1} smooth. $2,0,1,$3,$3,0.8,90 *
47183    gradient_norm n 0,255
47184    equalize map 3
47185    sharpen $4
47186  endl a c endl done
47187
47188fx_lava_preview :
47189  gui_split_preview "fx_lava $*",${-3--1}
47190
47191#@gui Marble : fx_marble, fx_marble
47192#@gui : Image Weight = float(.5,0,30)
47193#@gui : Pattern Weight = float(1,0,30)
47194#@gui : Pattern Angle = float(0,0,360)
47195#@gui : Amplitude = float(0,0,1000)
47196#@gui : Sharpness = float(.4,0,5)
47197#@gui : Anisotropy = float(.6,0,1)
47198#@gui : Alpha = float(.6,0,20)
47199#@gui : Sigma = float(1.1,0,20)
47200#@gui : Cut Low = float(0,0,100)
47201#@gui : Cut High = float(100,0,100)
47202#@gui : sep = separator()
47203#@gui : note = note("<small>Author: <i>Preben Soeberg</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47204fx_marble :
47205  repeat $! l[$>] split_opacity l[0]
47206    marble $1/10,$2/10,$3,$4,$5,$6,$7,$8,$9%,$10%
47207  endl a c endl done
47208
47209#@gui Maze : fx_maze, fx_maze
47210#@gui : Cell Size = int(24,1,256)
47211#@gui : Thickness = int(1,1,10)
47212#@gui : Masking = choice("None","Render on Dark Areas","Render on White Areas")
47213#@gui : Preserve Image Dimension = bool(1)
47214#@gui : Maze Type = choice("Dark Walls","White Walls")
47215#@gui : sep = separator()
47216#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/02/09</i>.</small>")
47217fx_maze :
47218  repeat $! l[$>]
47219    w={w} h={h}
47220    r. {100/$1}%,{100/$1}%,1,100%,2
47221    if $3==0 f. 1
47222    elif $3==1 negate.
47223    fi
47224    maze_mask. $1 dilate. $2 *. 255
47225    if !$5 negate. fi
47226    if $4 r. $w,$h,100%,100% fi
47227  endl done
47228
47229#@gui Mineral Mosaic : fx_mineral_mosaic,fx_mineral_mosaic(0)
47230#@gui : Density = float(1,0,3)
47231#@gui : Area = float(2,0,32)
47232#@gui : Smoothness = float(1,0,10)
47233#@gui : Shade Strength = float(100,0,255)
47234#@gui : Shade Angle = float(0,0,360)
47235#@gui : sep = separator()
47236#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/01/02</i>.</small>")
47237fx_mineral_mosaic :
47238  repeat $! l[$>] to_rgb
47239    +b $3 segment_watershed. $1 +norm.
47240    area. 0 +<=. {$2^2} inpaint.. . rm. label.
47241    +f[0] 'if(c==0,x,y)' rv[-2,-1] +blend[-2,-1] shapeaverage,1,1
47242    -[-3,-1] rm[0,-2] channels. 0,1
47243    alpha={$5*pi/180} sh. 0 *. {cos($alpha)} rm. sh. 1 *. {sin($alpha)} rm. compose_channels. +
47244    normalize_local. 1000 n. -$4,$4
47245    + c 0,255
47246  endl done
47247
47248#@gui Mosaic : fx_mosaic, fx_mosaic_preview(0)
47249#@gui : Density (%) = float(50,0,100)
47250#@gui : sep = separator()
47251#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
47252#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
47253#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
47254#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
47255#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
47256#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
47257#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
47258#@gui : sep = separator()
47259#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47260#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47261#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47262#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47263#@gui : sep = separator()
47264#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/19/07</i>.</small>")
47265fx_mosaic :
47266  ac "repeat $! l[$>] split_opacity mosaic[0] $1 a c endl done",$2
47267
47268fx_mosaic_preview :
47269  gui_split_preview "fx_mosaic $*",${-3--1}
47270
47271#@gui Op Art : fx_shapes,fx_shapes_preview(0)
47272#@gui : Shape = choice{1,"Custom Layers","Circles","Squares","Diamonds","Triangles","Horizontal Stripes",
47273#@gui : "Vertical Stripes","Balls","Hearts","Stars","Arrows","Truchet","Circles (Outline)","Squares (Outline)",
47274#@gui : "Diamonds (Outline)","Triangles (Outline)","Hearts (Outline)","Stars (Outline)","Arrows (Outline)"}
47275#@gui : Number of Scales = int(16,2,24)
47276#@gui : Resolution = float(10,1,50)
47277#@gui : Zoom Factor = _int(2,1,8)
47278#@gui : Minimal Size = float(5,0,150)
47279#@gui : Maximal Size = float(90,0,150)
47280#@gui : Stencil Type = choice(0,"Black & White","RGB","Color")
47281#@gui : Allow Angle = choice("0 deg.","90 deg.","180 deg.")
47282#@gui : Negative = bool(1)
47283#@gui : Antialiasing = bool(1)
47284#@gui : sep = separator()
47285#@gui : note = note{"<small><b>Note:</b>
47286#@gui : If you set the parameter <i>Shape</i> to <i>Custom layers</i>, the different shapes used to map
47287#@gui : the pixel intensities will be defined as
47288#@gui : the <i>Number of scales</i> top layers of your image. Don't forget to set also <i>Input layers</i> to
47289#@gui : <i>All</i> to be sure
47290#@gui : these layers are passed to the filter.
47291#@gui : </small>"}
47292#@gui : sep = separator()
47293#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47294#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47295#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47296#@gui : sep = separator()
47297#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/16/12</i>.</small>")
47298fx_shapes :
47299  if $1 # Pre-defined shapes.
47300    remove_opacity repeat $! l[$>]
47301      if !$7 _fx_shapes $* * 255
47302      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
47303      else +_fx_shapes $* r[0] $3%,$3% r[0] [1],[1] *
47304      fi
47305    endl done
47306  else # Custom shapes.
47307    if $!<=$2
47308      error[] "Command '$0': Some layers are missing in 'Custom layers' mode ("{$2+1}" expected at least, "\
47309              $!" provided)." fi
47310    to_colormode[0-{$2-1}] ${max_s[0-{$2-1}]} remove_opacity[$2--1]
47311    repeat $!-$2 l[0-{$2-1},{$2+$>}]
47312      norm. w={w} h={h} r. $3%,$3%,1,1,2
47313      s={$4*max(round($w/w),round($h/h))}
47314      r0={$s*$5%} r1={$s*$6%}
47315      repeat $2 r={round($r0+$>*($r1-$r0)/($2-1))} if $r +r[$>] $r,$r,1,100%,3 else 1,1 fi done
47316      r[-$2--1] $s,$s,1,100%,0,0,0.5,0.5
47317      map_sprites[$2--1] $2,$8
47318    endl done rm[0-{$2-1}]
47319  fi
47320
47321fx_shapes_preview :
47322  if $1 repeat $! l[$>]
47323    w={w} h={h}
47324    gui_split_preview "fx_shapes ${1-3},1,${5--2}",$-1
47325    r $w,$h,1,100%,0,0,0.5,0.5
47326    endl done
47327  else
47328    if $!>$2 repeat $!-$2 l[0-{$2-1},{$2+$>}]
47329      w={w} h={h}
47330      +fx_shapes ${1-3},1,${5--2} rm..
47331      r. $w,$h,1,100%,0,0,0.5,0.5
47332    endl done rm[0-{$2-1}]
47333    else gui_warning_preview "Missing input layers!"
47334    fi
47335  fi
47336
47337_fx_shapes :
47338  norm w={w} h={h} r $3%,$3%,1,1,2
47339  s={(1+$10)*$4*max(round($w/w),round($h/h))}
47340  r0={$s*$5%} r1={$s*$6%}
47341  repeat $2 r={round($r0+$>*($r1-$r0)/($2-1))} if $r _fx_shapes{$1-1}[] $r,$s else 1,1 fi done
47342  r[-$2--1] $s,$s,1,1,0,0,0.5,0.5
47343  if $9 rv[-$2--1] *[-$2--1] -1 +[-$2--1] 1 fi
47344  map_sprites $2,$8
47345  if $10 r 50%,50%,1,1,2 fi
47346
47347_fx_shapes0 :
47348  shape_circle $1
47349
47350_fx_shapes1 :
47351  $1,$1,1,1,1
47352
47353_fx_shapes2 :
47354  $1,$1,1,1 = 1,50%,50% distance 1,1 < {$1/2}
47355
47356_fx_shapes3 :
47357  $2,$2,1,1,'x+y<=2*$1-1'
47358
47359_fx_shapes4 :
47360  $2,$1,1,1,1
47361
47362_fx_shapes5 :
47363  $1,$2,1,1,1
47364
47365_fx_shapes6 :
47366  ball $1,200 n 0,1
47367
47368_fx_shapes7 :
47369  shape_heart 65 r $1,$1,1,1,2 >= 50%
47370
47371_fx_shapes8 :
47372  shape_star $1
47373
47374_fx_shapes9 :
47375  arrow3d 0,0,0,1,0,0,15%,40%,30% col3d 1 *3d $1 c3d
47376  $2,$2 j3d. ..,50%,50%,0,1,2,0,0
47377  rm.. +mirror y max
47378
47379_fx_shapes10 :
47380  S={$2+1-($2%2)}
47381  $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)"
47382  +mirror xy max
47383
47384_fx_shapes11 :
47385  _fx_shapes0 $* expand_xy 1,0 +erode 3 -
47386
47387_fx_shapes12 :
47388  _fx_shapes1 $* expand_xy 1,0 +erode 3 -
47389
47390_fx_shapes13 :
47391  _fx_shapes2 $* expand_xy 1,0 +erode 3 -
47392
47393_fx_shapes14 :
47394  _fx_shapes3 $* expand_xy 1,0 +erode 3 -
47395
47396_fx_shapes15 :
47397  _fx_shapes7 $* expand_xy 1,0 +erode 3 -
47398
47399_fx_shapes16 :
47400  _fx_shapes8 $* expand_xy 1,0 +erode 3 -
47401
47402_fx_shapes17 :
47403  _fx_shapes9 $* expand_xy 1,0 +erode 3 -
47404
47405#@gui Pack Sprites : fx_pack_sprites, gui_no_preview
47406#@gui : Number of Scales = int(5,1,16)
47407#@gui : Minimal Scale (%) = float(25,1,100)
47408#@gui : Allow Angle = choice(3,"0 deg.","180 deg.","90 deg.","Any")
47409#@gui : Spacing = int(1,-16,16)
47410#@gui : Precision = int(7,1,32)
47411#@gui : sep = separator()
47412#@gui : Masking = choice("No Masking","Mask as Bottom Layer")
47413#@gui : Width = int(512,32,2048)
47414#@gui : Height = int(512,32,2048)
47415#@gui : note = note("<small><b>Notes:</b>\n - Parameters <i>Width</i> and <i>Height</i> are considered only when
47416#@gui : <i>No masking</i> mode is selected.\n
47417#@gui : - Set different sprites on different layers to pack multiple sprites at the same time.</small>")
47418#@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=bpg7CGH7vCM")
47419#@gui : sep = separator()
47420#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/24/06</i>.</small>")
47421fx_pack_sprites :
47422  if $6 # With mask.
47423    if $!<2 error[] "Command '$0': Masking requires at least two input layers !
47424                     (please check that 'Input Layers' is correctly set)." fi
47425    repeat $!-1 l[$>] to_rgba split_opacity +!=[1] 0 *[0] . a c autocrop 0 endl done
47426    remove_empty[0--2] +channels. 100% channels. -4,0 mv. 0
47427    pack_sprites[0--2] ${1-5}
47428  else # No masking
47429    repeat $! l[$>] to_rgba split_opacity +!=[1] 0 *[0] . a c autocrop 0 endl done
47430    remove_empty i[0] $7,$8,1,5 pack_sprites ${1-5}
47431  fi
47432  channels[0] 0,{0,s-2}
47433
47434#@gui Paper Texture : fx_paper, fx_paper_preview(0)
47435#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
47436#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
47437#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
47438#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
47439#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
47440#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
47441#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
47442#@gui : sep = separator()
47443#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47444#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47445#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47446#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47447#@gui : sep = separator()
47448#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47449fx_paper :
47450  ac "texturize_paper",$1
47451
47452fx_paper_preview :
47453  gui_split_preview "fx_paper $*",${-3--1}
47454
47455#@gui Plaid : fx_plaid_texture,fx_plaid_texture(1)
47456#@gui : Line = float(50,0,100)
47457#@gui : Number of Angles = int(2,1,8)
47458#@gui : Starting Angle = float(0,0,360)
47459#@gui : Angle Range = float(90,0,360)
47460#@gui : Smoothness = float(1,0,5)
47461#@gui : Sharpen = float(300,0,1000)
47462#@gui : sep = separator()
47463#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/16/05</i>.</small>")
47464fx_plaid_texture :
47465  repeat $! l[$>]
47466    w={w} h={h} s={s}
47467    rows $1%
47468    b $5% sharpen $6
47469    r $w,$h,1,$s,2
47470    +rotate[0] $3,1,2,50%,50%
47471    repeat $2-1 +rotate[0] {$3+$4*($>+1)/($2-1)},1,2,50%,50% +[-2,-1] done rm[0]
47472    / $2
47473  endl done
47474
47475#@gui Polka Dots : fx_polka_dots, fx_polka_dots(1)
47476#@gui : Size = float(80,0,100)
47477#@gui : Density = float(20,0.1,100)
47478#@gui : First Offset = float(50,0,100)
47479#@gui : Second Offset = float(50,0,100)
47480#@gui : Angle = float(0,0,180)
47481#@gui : Aliasing = float(0.5,0.1,1)
47482#@gui : Shading = float(0.1,0.1,1)
47483#@gui : Opacity = float(1,0,1)
47484#@gui : Color = color(255,0,0,255)
47485#@gui : sep = separator()
47486#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47487fx_polka_dots :
47488  to_rgba polka_dots {$1*$2/100},${2--1}
47489
47490#@gui Random Color Ellipses : fx_color_ellipses, fx_color_ellipses(1)
47491#@gui : Density = int(400,0,3000)
47492#@gui : Radius = float(8,0,30)
47493#@gui : Opacity = float(0.1,0.01,0.5)
47494#@gui : sep = separator()
47495#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47496fx_color_ellipses :
47497  color_ellipses $1,$2,$3
47498
47499#@gui Random Pattern : fx_random_pattern, fx_random_pattern_preview(1)
47500#@gui : Size = _int(1024,16,8192)
47501#@gui : Min Detail Level = float(2,0,20)
47502#@gui : Seed = float(4038,0,100000)
47503#@gui : Randomize Seed = button()
47504#@gui : sep = separator()
47505#@gui : Brightness (%) = float(0,-100,100)
47506#@gui : Contrast (%) = float(0,-100,100)
47507#@gui : Gamma (%) = float(0,-100,100)
47508#@gui : Hue (%) = float(0,-100,100)
47509#@gui : Saturation (%) = float(0,-100,100)
47510#@gui : sep = separator()
47511#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/10/08</i>.</small>")
47512_fx_random_pattern :
47513  if $4 srand _seed={_round(u(100000))} else _seed=$3 fi
47514  srand $_seed
47515  random_pattern $1,$1,$2
47516  adjust_colors. ${5-9}
47517  mv. 0
47518
47519fx_random_pattern :
47520  if 0$_output_mode rm fi
47521  _fx_random_pattern $*
47522
47523fx_random_pattern_preview :
47524  _fx_random_pattern {max($_preview_width,$_preview_height)},${2--1}
47525  k[0] rr2d $_preview_width,$_preview_height,2,2
47526  to "Seed: \#"$_seed,5,5,5%,2
47527  u "{$1}{$2}{"{$4?$_seed:$3}"}{0}{$5}{$6}{$7}{$8}{$9}"
47528
47529#@gui Resynthetize Texture [FFT] : syntexturize, fx_syntexturize_preview(1)
47530#@gui : Width = _int(1024,32,8192)
47531#@gui : Height = _int(1024,32,8192)
47532#@gui : Equalize Light = float(0,0,100)
47533#@gui : sep = separator()
47534#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47535#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47536#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47537#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47538#@gui : sep = separator()
47539#@gui : note = note{"<small><b>Note:</b> This filter tries to re-synthetize a <b>micro</b>-texture
47540#@gui : (given as the input image) onto an output (seamless) image with an arbitrary size.
47541#@gui : It uses a phase randomization technique, as described in:</small>"}
47542#@gui : url = link("Micro-Texture Synthesis by Phase Randomization","http://www.ipol.im/pub/art/2011/ggm_rpn/")
47543#@gui : note = note("<small>This filter is based on the work of <i>Bruno Galerne</i>, <i>Yann Gousseau</i> and
47544#@gui : <i>Jean-Michel Morel</i>.</small>")
47545#@gui : sep = separator()
47546#@gui : url = link("Click here for a detailed description of this filter.",\
47547# "http://gimpchat.com/viewtopic.php?f=28&t=10141")
47548#@gui : sep = separator()
47549#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Jérome Boulanger</i>.
47550#@gui :       Latest Update: <i>2014/09/04</i>.</small>")
47551fx_syntexturize :
47552  repeat $! l[$>]
47553    if $3 +b {20.5-$3/50}% -[0] [1] fc. ${average_colors.} + c 0,255 fi
47554    syntexturize $1,$2
47555  endl done
47556
47557fx_syntexturize_preview :
47558  gui_split_preview "fx_syntexturize 100%,100%,$3",${-3--1}
47559
47560#@gui Resynthetize Texture [Patch-Based] : syntexturize_matchpatch, fx_syntexturize_matchpatch_preview(1)
47561#@gui : Width = _int(512,32,8192)
47562#@gui : Height = _int(512,32,8192)
47563#@gui : Number of Scales = int(0,0,16)
47564#@gui : Patch Size = int(7,1,32)
47565#@gui : Blending Size = int(5,1,24)
47566#@gui : Precision = float(1,0,5)
47567#@gui : Equalize Light = float(0,0,100)
47568#@gui : sep = separator()
47569#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47570#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47571#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47572#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47573#@gui : sep = separator()
47574#@gui : note = note{"<small><b>Note:</b> This filter tries to re-synthetize an input texture image onto a
47575#@gui : bigger output image (with an arbitrary size).
47576#@gui : Beware, this filter is quite slow to compute!</small>"}
47577#@gui : sep = separator()
47578#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/22/10</i>.</small>")
47579_fx_syntexturize_matchpatch_preview :
47580  repeat $! l[$>]
47581    if $7 +b {20.5-$7/50}% -[0] [1] fc. ${average_colors.} + c 0,255 fi
47582    w={w} h={h}
47583    syntexturize_matchpatch 100%,100%,${3--1}
47584    to_rgba r $w,$h,1,4,0,0,0.5,0.5
47585  endl done
47586
47587fx_syntexturize_matchpatch_preview :
47588  gui_split_preview "_fx_syntexturize_matchpatch_preview ${1--2}",${-3--1}
47589
47590#@gui Rorschach : fx_rorschach, fx_rorschach
47591#@gui : Scale = float(3,0,10)
47592#@gui : Mirror = choice(1,"None","X-Axis","Y-Axis","XY-Axes")
47593#@gui : Stencil Type = choice(2,"Black & White","RGB","Color")
47594#@gui : sep = separator()
47595#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/12/03</i>.</small>")
47596fx_rorschach :
47597  repeat $! remove_opacity l[$>]
47598    if $3==0 norm rorschach $1%,$2 * 255
47599    elif $3==1 to_rgb rorschach $1%,$2 * 255
47600    else +norm rorschach. $1%,$2 blend shapeaverage0
47601    fi
47602  endl done
47603
47604#@gui Satin : fx_satin, fx_satin(1)
47605#@gui : Iterations = int(20,4,128)
47606#@gui : Smoothness (%) = float(1,0,5)
47607#@gui : Seed = int(0,0,65535)
47608#@gui : sep = separator()
47609#@gui : Dark Color = color(0,0,0,255)
47610#@gui : Light Color = color(255,255,255,255)
47611#@gui : Stretch Contrast = bool(0)
47612#@gui : sep = separator()
47613#@gui : Brightness (%) = float(0,-100,100)
47614#@gui : Contrast (%) = float(0,-100,100)
47615#@gui : Gamma (%) = float(-50,-100,100)
47616#@gui : Hue (%) = float(0,-100,100)
47617#@gui : Saturation (%) = float(0,-100,100)
47618#@gui : sep = separator()
47619#@gui : note = note{"This filter has been inspired by
47620#@gui : <a href="https://fence-post.deviantart.com/art/Satin-Texture-in-GIMP-46937633">this tutorial</a>
47621#@gui : from DeviantArt user <i>fence-post</i>."}
47622#@gui : sep = separator()
47623#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/11/27</i>.</small>")
47624fx_satin :
47625  ($4,$8^$5,$9^$6,$10^$7,$11) srgb2rgb. r. 256,1,1,4,3 rgb2srgb.
47626  repeat $!-1 l[$>]
47627    srand $3 channels 0 f 0
47628    repeat $1
47629      100%,100%,1,1,"begin(
47630           A = u([0,0],[w,h]-1);
47631           B = u([0,0],[w,h]-1);
47632           N = [0,-1,1,0]*(B - A);
47633           D = A + N;
47634           C = B + N;
47635           abc = solve([A,1,B,1,C,1,D,1],[0,255,255,0]);
47636         );
47637         dot(abc,[x,y,1])"
47638      c. 0,255 -- abs
47639    done
47640    b $2% gradient_norm negate n 0,255
47641    if $12 normalize_local , fi
47642    pass. 1 map.. . rm.
47643    sh. 0,2 adjust_colors. ${13-17} rm.
47644  endl done
47645  rm.
47646
47647#@gui Seamless Turbulence : fx_seamless_turbulence, fx_seamless_turbulence(0)
47648#@gui : Amplitude = float(15,0,30)
47649#@gui : Smoothness = float(20,0,40)
47650#@gui : Orientation = float(0,0,180)
47651#@gui : Deviation = float(1,0,1)
47652#@gui : Contrast = float(3,0,4)
47653#@gui : Color Rendering = bool(0)
47654#@gui : sep = separator()
47655#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/02/04</i>.</small>")
47656fx_seamless_turbulence :
47657  repeat $! l[$>]
47658    wh={w},{h} rm
47659    $wh,1,{if($6,3,1)} rand. 0,255
47660    $wh rand. {$3*pi/180-$4*10*pi},{$3*pi/180+$4*10*pi} +sin. cos.. a[-2,-1] c
47661    r[-2,-1] 130%,130%,1,100%,0,2,0.5,0.5 b. $2 orientation.
47662    vector2tensor.
47663    smooth.. .,$1,0.5,20 rm.
47664    r. $wh,1,100%,0,0,0.5,0.5
47665    if $5!=1 ia={ia} - $ia * $5 + $ia fi
47666  endl done
47667  c 0,255 n 0,255
47668
47669#@gui Shock Waves : fx_shockwaves, fx_shockwaves_preview
47670#@gui : Amplitude = float(10,0,100)
47671#@gui : Low Frequency = float(10,0,100)
47672#@gui : Frequency Range = float(20,0,100)
47673#@gui : sep = separator()
47674#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
47675#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
47676#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
47677#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
47678#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
47679#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
47680#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
47681#@gui : sep = separator()
47682#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47683#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47684#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47685#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47686#@gui : sep = separator()
47687#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/01/12</i>.</small>")
47688_fx_shockwaves :
47689  dct
47690  100%,100%,1,1,1 circle. 0,0,{$2+$3}%,1,{$1+1} circle. 0,0,$2%,1,1
47691  * idct c 0,255
47692
47693fx_shockwaves :
47694  ac "_fx_shockwaves ${1-3}",$4
47695
47696fx_shockwaves_preview :
47697  gui_split_preview "fx_shockwaves $*",${-3--1}
47698
47699#@gui Sponge : fx_sponge, fx_sponge_preview(0)
47700#@gui : Size = int(13,3,21)
47701#@gui : sep = separator()
47702#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
47703#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
47704#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
47705#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
47706#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
47707#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
47708#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
47709#@gui : sep = separator()
47710#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47711#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47712#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47713#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47714#@gui : sep = separator()
47715#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47716fx_sponge :
47717  ac "sponge $1",$2
47718
47719fx_sponge_preview :
47720  gui_split_preview "fx_sponge $*",${-3--1}
47721
47722#@gui Stained Glass : fx_stained_glass, fx_stained_glass_preview(0)
47723#@gui : Edges = float(20,0,100)
47724#@gui : Shading = float(0.1,0,0.5)
47725#@gui : Thin Separators = bool(1)
47726#@gui : sep = separator()
47727#@gui : Equalize = bool(1)
47728#@gui : Colors = float(1,0,3)
47729#@gui : Brightness (%) = float(0,-100,100)
47730#@gui : Contrast (%) = float(0,-100,100)
47731#@gui : Gamma (%) = float(0,-100,100)
47732#@gui : sep = separator()
47733#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47734#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47735#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47736#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47737#@gui : sep = separator()
47738#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/03</i>.</small>")
47739fx_stained_glass :
47740  repeat $! l[$>] split_opacity l[0] to_rgb
47741    stained_glass $1,$2,$3
47742    n 0,255
47743    if $4 equalize. fi
47744    rgb2lab. sh. 1,2 *. $5 rm. lab2rgb.
47745    adjust_colors. ${6-8}
47746  endl a c endl done
47747
47748fx_stained_glass_preview :
47749  gui_split_preview "fx_stained_glass $*",${-3--1}
47750
47751#@gui Stars : fx_stars, fx_stars(0)
47752#@gui : Density = float(10,0,200)
47753#@gui : Depth = float(0,0,5)
47754#@gui : Size = int(32,8,128)
47755#@gui : Branches = int(5,3,16)
47756#@gui : Thickness = float(0.38,0.1,1)
47757#@gui : Smoothness = float(0,0,10)
47758#@gui : Color = color(255,255,100,200)
47759#@gui : sep = separator()
47760#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/01/10</i>.</small>")
47761fx_stars :
47762  repeat $! l[$>] split_opacity rv
47763    stars $1%,$2,$3,$4,$5,$6%,${7-9},{$10/255}
47764  rv a c endl done
47765
47766#@gui Stencil : fx_stencil, fx_stencil_preview(0)
47767#@gui : Radius = float(3,0,10)
47768#@gui : Smoothness = float(0,0,30)
47769#@gui : Iterations = int(8,1,100)
47770#@gui : Aliasing = float(0,0,5)
47771#@gui : Stencil Type = choice(2,"Black & White","RGB","Color")
47772#@gui : Transparency = bool(0)
47773#@gui : sep = separator()
47774#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47775#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47776#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47777#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47778#@gui : sep = separator()
47779#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47780fx_stencil :
47781  if $5==0 norm stencil $1,$2,$3
47782  elif $5==1 stencil $1,$2,$3
47783  else repeat $!
47784    +norm. stencil. $1,$2,$3 >=. 50% blend[-2,-1] shapeaverage0
47785  mv. 0 done fi
47786  if $6 to_rgba replace_color 0,0,0,0,0,255,0,0,0,0 fi
47787  if $4 smooth {30*$4},0,1,1 fi
47788
47789fx_stencil_preview :
47790  gui_split_preview "fx_stencil $*",${-3--1}
47791
47792#@gui Tetris : fx_tetris, fx_tetris(0)
47793#@gui : Scale = int(10,1,20)
47794#@gui : sep = separator()
47795#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47796fx_tetris :
47797  tetris $1
47798
47799#@gui Triangular Pattern : fx_triangular_pattern,fx_triangular_pattern(1)+
47800#@gui : Random Seed = int(43,0,65535)
47801#@gui : sep = separator()
47802#@gui : Depth = int(7,0,16)
47803#@gui : Split Type-1 = int(4,0,20)
47804#@gui : Split Type-2 = int(4,0,20)
47805#@gui : Split Type-3 = int(4,0,20)
47806#@gui : Split Type-4 = int(0,0,20)
47807#@gui : Split Type-5 = int(0,0,20)
47808#@gui : Holes Probability (Type-5) (%) = float(0,0,100)
47809#@gui : sep = separator()
47810#@gui : Filling opacity (%) = float(100,0,100)
47811#@gui : Outline Color = color(0,0,0,160)
47812#@gui : Anti-aliasing = choice(1,"None","x1.5","x2","x3","x4")
47813#@gui : sep = separator()
47814#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/09/29</i>.</small>")
47815fx_triangular_pattern :
47816  if !$! 1024,1024,1,3 fi
47817  W,H={0,[w,h]} f={arg0($14,1,1.5,2,3,4)*100} r[0] $f%,$f%,1,100%
47818  1,1,1,6
47819  eval "
47820
47821    # Init parameters for random generator.
47822    srand($1);
47823    const pn1 = $3;
47824    const pn2 = $4 + pn1;
47825    const pn3 = $5 + pn2;
47826    const pn4 = $6 + pn3;
47827    const pn5 = $7 + pn4;
47828
47829    # Create set of subdivided triangles.
47830    C = [ w#0,h#0 ]/2;
47831    da_push([ C,0,h#0-1,0,0 ],
47832            [ C,w#0-1,0,0,0 ],
47833            [ C,w#0-1,0,w#0-1,h#0-1 ],
47834            [ C,0,h#0-1,w#0-1,h#0-1 ]);
47835    repeat($2,
47836      n = int(u(pn5))%pn5;
47837      n = n<pn1?0:n<pn2?1:n<pn3?2:n<pn4?3:4;
47838      is_hole = u<=$8%;
47839      repeat(da_size(),k,
47840        F = I[k]; P0 = F[0,2]; P1 = F[2,2]; P2 = F[4,2];
47841        n==0?(
47842          P = lerp(P1,P2,0.5);
47843          da_push([ P0,P,P1 ]);
47844          I[k] = [ P0,P,P2 ];
47845        ):n==1?(
47846          P = lerp(P0,P2,0.5);
47847          da_push([ P1,P,P0 ]);
47848          I[k] = [ P1,P,P2 ];
47849        ):n==2?(
47850          P = lerp(P0,P1,0.5);
47851          da_push([ P2,P,P0 ]);
47852          I[k] = [ P2,P,P1 ];
47853        ):n==3?(
47854          P = (P0 + P1 + P2)/3;
47855          da_push([ P,P0,P1 ]);
47856          da_push([ P,P1,P2 ]);
47857          I[k] = [ P,P2,P0 ];
47858        ):(
47859          P01 = lerp(P0,P1,0.5); P02 = lerp(P0,P2,0.5); P12 = lerp(P1,P2,0.5);
47860          da_push([ P0,P01,P02 ]);
47861          da_push([ P1,P12,P01 ]);
47862          is_hole?( # Hole
47863            I[k] = [ P2,P02,P12 ];
47864          ):( # No hole
47865            da_push([ P2,P02,P12 ]);
47866            I[k] = [ P01,P02,P12 ];
47867          );
47868        );
47869      );
47870    );
47871
47872    # Draw triangles.
47873    repeat(da_size(),k,
47874      F = I[k];
47875      polygon(#0,3,F,$9%,u([255,255,255]));
47876      polygon(#0,-3,F,$13%/3,0xFFFFFFFF,$10,$11,$12,255);
47877    )"
47878  rm.
47879  r[0] $W,$H,1,100%,2
47880
47881#@gui Truchet : fx_truchet, fx_truchet(0)
47882#@gui : Scale = int(32,1,256)
47883#@gui : Radius = int(5,1,64)
47884#@gui : Smoothness = float(1,0,10)
47885#@gui : Type = choice(1,"Straight","Curved")
47886#@gui : Color = choice("White on Black","Black on White","White on Transparent","Black on Transparent",
47887#@gui : "Transparent on White","Transparent on Black","Random")
47888#@gui : sep = separator()
47889#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/26/10</i>.</small>")
47890fx_truchet :
47891  repeat $! l[$>]
47892    100%,100% truchet $1,$2,$4 rm..
47893    if $5==1 * -1
47894    elif $5==2 i[0] 100%,100%,1,1,1
47895    elif $5==3 i[0] 100%,100%
47896    elif $5==4 * -1 i[0] 100%,100%
47897    elif $5==5 * -1 i[0] 100%,100%,1,1,-1
47898    elif $5==6 label 0,1 {iM+1},1,1,3 rand. 0,255 map.. . rm.
47899    fi
47900    a c b $3 n 0,255
47901  endl done
47902
47903#@gui Voronoi : fx_voronoi, fx_voronoi_preview(0)
47904#@gui : Threshold = float(160,0,255)
47905#@gui : Threshold on = choice(1,"Pixel values","Gradient values")
47906#@gui : Smoothness = float(0.5,0,10)
47907#@gui : Subsampling (%) = float(50,0,100)
47908#@gui : sep = separator()
47909#@gui : Flat color = choice(3,"Black","White","Transparent","Image")
47910#@gui : Outline thickness = int(1,0,8)
47911#@gui : Outline color = color(0,0,0,100)
47912#@gui : Centers radius = int(2,0,10)
47913#@gui : Centers color = color(255,255,255,40)
47914#@gui : sep = separator()
47915#@gui : Anti-aliasing = choice{1,"x1 (none)","x1.5","x2","x2.5"}
47916#@gui : sep = separator()
47917#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/04/30</i>.</small>")
47918fx_voronoi :
47919  repeat $! l[$>] wh={[w,h]}
47920    f={arg(1+$16,1,1.5,2,2.5)*100} r $f%,$f%,1,100%,3
47921
47922    if $2 +gradient_norm t={(255-$1)/4} else +s c med[^0] t={255-$1} fi
47923    if $3 mM={[im,iM]} b. $3 n. $mM fi
47924    >. $t
47925    f. "u<($4%)^4?i:0"
47926    label_fg. 0,1 voronoi.
47927
47928    # Flat.
47929    if $5<3
47930      1,1,1,4,"$5==0?[0,0,0,255]:$5==1?[255,255,255,255]:$5==2?[128,128,128,0]"
47931      r. [0],[0],1,4 rv[0,-1] rm.
47932    else
47933      blend[0] .,shapeaverage
47934    fi
47935
47936    # Outline.
47937    if $6" && "$10
47938      +f. "const boundary=1; i!=j(1) || i!=j(0,1)"
47939      dilate. $6
47940      1,1,1,4,"[${7-9},255]" r. [0],[0],1,4
47941      j[0] .,0,0,0,0,{$10/255},.. rm[-2,-1]
47942    fi
47943
47944    # Centers.
47945    if $11" && "$15
47946      1,{iM+1},1,3 eval.. "I[#-1,i]+=[x,y,1]" s. c,-2 /[-2,-1] rm..
47947      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])"
47948    fi
47949    rm.
47950    r $wh,1,100%,2
47951
47952  endl done
47953
47954fx_voronoi_preview :
47955  fx_voronoi ${1-15},{min(1,$16)}
47956
47957#@gui Weave : weave, weave(1)
47958#@gui : Density = int(6,1,32)
47959#@gui : Thickness = float(65,0,100)
47960#@gui : Shadow = float(0,0,100)
47961#@gui : Shading = float(0.5,0,3)
47962#@gui : Fibers Amplitude = float(0,0,255)
47963#@gui : Fibers Smoothness = float(0,0,10)
47964#@gui : Angle = choice("0 deg.","22.5 deg.","45 deg.","67.5 deg.")
47965#@gui : X-Curvature = float(0,-1,1)
47966#@gui : Y-Curvature = float(0,-1,1)
47967#@gui : sep = separator()
47968#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/18/01</i>.</small>")
47969
47970#@gui Whirls : fx_whirls, fx_whirls_preview(0)
47971#@gui : Density = int(7,3,20)
47972#@gui : Smoothness = float(2,0,10)
47973#@gui : Darkness = float(0.2,0,1)
47974#@gui : Lightness = float(1.8,1,3)
47975#@gui : sep = separator()
47976#@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
47977#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
47978#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
47979#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
47980#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
47981#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
47982#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
47983#@gui : sep = separator()
47984#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
47985#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
47986#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
47987#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
47988#@gui : sep = separator()
47989#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
47990fx_whirls :
47991  ac "whirls $1,$2,$3,$4",$5
47992
47993fx_whirls_preview :
47994  gui_split_preview "fx_whirls ${1-5}",${-3--1}
47995
47996
47997#@gui ____<b>Repair</b>
47998#-----------------------
47999
48000#@gui Bayer Reconstruction : bayer2rgb, gui_no_preview
48001#@gui : G/M Smoothness = _float(6,0,20)
48002#@gui : R/B Smoothness (Principal) = _float(6,0,20)
48003#@gui : R/B Smoothness (Secondary) = _float(4,0,20)
48004#@gui : sep = separator()
48005#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48006
48007#@gui Deinterlace : deinterlace, fx_deinterlace_preview(0)
48008#@gui : Algorithm = choice("Standard","Motion-Compensated")
48009#@gui : sep = separator()
48010#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48011#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48012#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48013#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48014#@gui : sep = separator()
48015#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48016fx_deinterlace :
48017  deinterlace 0 skip ${^0}
48018
48019fx_deinterlace_preview :
48020  gui_split_preview "fx_deinterlace $*",${-3--1}
48021
48022#@gui Inpaint [Holes] : fx_inpaint_holes, fx_inpaint_holes(0)
48023#@gui : Maximal Area = float(4,1,512)
48024#@gui : Tolerance = float(20,0,255)
48025#@gui : Connectivity = choice(1,"Low","High")
48026#@gui : sep = separator()
48027#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/27/05</i>.</small>")
48028fx_inpaint_holes :
48029  inpaint_holes {$1^1.5},$2,$3
48030
48031#@gui Inpaint [Morphological] : fx_inpaint_morpho, fx_inpaint_morpho_preview(1)
48032#@gui : Mask Color = _color(255,0,0,255)
48033#@gui : Mask Dilation = _int(0,0,32)
48034#@gui : sep = separator()
48035#@gui : note = note{"<small><b>Note:</b> It is strongly suggested to apply this filter only on a selection
48036#@gui : around the region to inpaint, to save computation time!</small>"}
48037#@gui : sep = separator()
48038#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/25/11</i>.</small>")
48039fx_inpaint_morpho :
48040  repeat $! l[$>]
48041    R=$1 G=$2 B=$3 A=$4
48042    if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color.
48043    +round select_color. 0,{round([$R,$G,$B,$A])}
48044    if $5 dilate. {1+2*$5} fi
48045    inpaint_morpho.. [1]
48046    rm.
48047  endl done
48048
48049fx_inpaint_morpho_preview :
48050  fx_inpaint_morpho ${1-4},{1+$5}
48051
48052#@gui Inpaint [Multi-Scale] : fx_inpaint_matchpatch, fx_inpaint_matchpatch_preview(1)
48053#@gui : Number of Scales = int(0,0,16)
48054#@gui : note = note{"<small>(Set <i>Number of scales</i> to <i>0</i> for automatic scale detection)</small>"}
48055#@gui : Patch Size = int(9,1,64)
48056#@gui : Number of Iterations per Scale = int(10,1,100)
48057#@gui : Blend Size = int(5,0,32)
48058#@gui : Allow Outer Blending = bool(1)
48059#@gui : Mask Color = color(255,0,0,255)
48060#@gui : Mask Dilation = int(0,0,32)
48061#@gui : sep = separator()
48062#@gui : Preview Progression While Running = _bool(0)
48063#@gui : sep = separator()
48064#@gui : note = note{"<small><b>Note:</b> Preview and final result may strongly differ.</small>"}
48065#@gui : sep = separator()
48066#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/25/11</i>.</small>")
48067fx_inpaint_matchpatch :
48068  repeat $! l[$>] nm={n}
48069    R=$6 G=$7 B=$8 A=$9
48070    if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color.
48071    +round select_color. 0,{round([$R,$G,$B,$A])}
48072    if $10 dilate. {1+2*$10} fi
48073    if $11
48074      visu_size=${fitscreen[]" "{0,[w,h,1]},25%,50%}
48075      w1.. $visu_size,0,"[Preview] G'MIC: Inpaint [multi-scale]"
48076    fi
48077    srand 0 inpaint_matchpatch.. [1],${1-5}
48078    rm. nm $nm
48079  endl done
48080
48081fx_inpaint_matchpatch_preview :
48082  fx_inpaint_matchpatch ${1-9},{1+$10},0
48083
48084#@gui Inpaint [Patch-Based] : fx_inpaint_patch, fx_inpaint_patch_preview
48085#@gui : Patch Size = _int(7,1,64)
48086#@gui : Lookup Size = _float(16,1,32)
48087#@gui : Lookup Factor = _float(0.1,0,1)
48088#@gui : Blend Size = _float(1.2,0,5)
48089#@gui : Blend Threshold = _float(0,0,1)
48090#@gui : Blend Decay = _float(0.05,0,0.5)
48091#@gui : Blend Scales = _int(10,1,20)
48092#@gui : Allow Outer Blending = _bool(1)
48093#@gui : Mask Color = _color(255,0,0,255)
48094#@gui : Mask Dilation = _int(0,0,32)
48095#@gui : Process by Blocs of Size = _choice("100%","75%","50%","25%","10%","5%","2%","1%")
48096#@gui : sep = separator()
48097#@gui : note = note("<small>A quick tutorial on how to use this filter can be found here:</small>")
48098#@gui : url = link("G'MIC Inpainting tutorial on Patrick David's blog.",
48099#@gui : "https://patdavid.net/2014/02/getting-around-in-gimp-gmic-inpainting.html")
48100#@gui : sep = separator()
48101#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Maxime Daisy</i>.
48102#@gui :       Latest Update: <i>2015/25/11</i>.</small>")
48103_fx_inpaint_patch :
48104  repeat $! l[$>]
48105    R=$9 G=$10 B=$11 A=$12
48106    if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color.
48107    +round select_color. 0,{round([$R,$G,$B,$A])}
48108    if $13 dilate. {1+2*$13} fi
48109    inpaint.. [1],$1,{$1*$2},$3,1,{$4*$1},${5-8}
48110    rm.
48111  endl done
48112
48113fx_inpaint_patch :
48114  repeat $! l[$>]
48115    if $14
48116      bs={max(16,min(w,h)*arg(1+$14,100,75,50,25,10,5,2,1)%)}
48117      at "_fx_inpaint_patch $*",$bs,$bs,1,25%,25%,0,2
48118    else _fx_inpaint_patch $*
48119    fi
48120  endl done
48121
48122fx_inpaint_patch_preview :
48123  fx_inpaint_patch ${1-12},{1+$13},100
48124
48125#@gui Inpaint [Transport-Diffusion] : fx_inpaint_pde, fx_inpaint_pde_preview(1)
48126#@gui : Smoothness (%) = float(75,0,100)
48127#@gui : Regularization = choice(1,"Isotropic","Delaunay-Guided","Edge-Oriented")
48128#@gui : Regularization Iterations = int(20,0,100)
48129#@gui : Mask Color = _color(255,0,0,255)
48130#@gui : Mask Dilation = _int(0,0,32)
48131#@gui : sep = separator()
48132#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/10/04</i>.</small>")
48133fx_inpaint_pde :
48134  repeat $! l[$>]
48135    R=$4 G=$5 B=$6 A=$7
48136    if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color.
48137    +select_color 0,$R,$G,$B,$A
48138    if $8 dilate. {1+2*$8} fi
48139    inpaint_pde.. [1],$1%,$2,$3
48140    rm.
48141  endl done c 0,255
48142
48143fx_inpaint_pde_preview :
48144  fx_inpaint_pde ${1-7},{1+$8}
48145
48146#@gui Red-Eye Attenuation : red_eye, red_eye
48147#@gui : Threshold = float(75,0,100)
48148#@gui : Smoothness = float(3.5,0,20)
48149#@gui : Factor = float(0.1,0,1)
48150#@gui : sep = separator()
48151#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48152
48153#@gui Remove Hot Pixels : fx_remove_hotpixels, fx_remove_hotpixels_preview(0)
48154#@gui : Mask Size = int(3,3,20)
48155#@gui : Threshold = float(10,0,200)
48156#@gui : sep = separator()
48157#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48158#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48159#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48160#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48161#@gui : sep = separator()
48162#@gui : note = note("<small>Author: <i>Jérome Boulanger</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48163fx_remove_hotpixels :
48164  remove_hotpixels $1,$2
48165
48166fx_remove_hotpixels_preview :
48167  gui_split_preview "fx_remove_hotpixels $*",${-3--1}
48168
48169#@gui Solidify : fx_solidify_td, fx_solidify_td_preview(1)
48170#@gui : Smoothness (%) = float(75,0,100)
48171#@gui : Regularization = choice(1,"Isotropic","Delaunay-Guided","Edge-Oriented")
48172#@gui : Regularization Iterations = int(20,0,100)
48173#@gui : Dilation / Erosion = int(0,-20,20)
48174#@gui : Colorspace = choice(1,"sRGB","Linear RGB")
48175#@gui : sep = separator()
48176#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48177#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48178#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48179#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48180#@gui : sep = separator()
48181#@gui : note = note{"<small><b>Note:</b>
48182#@gui : This filter reconstructs transparent regions of an image using a transport-diffusion algorithm.
48183#@gui : Useful only for images having an alpha-channel.
48184#@gui : </small>"}
48185#@gui : sep = separator()
48186#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/07/04</i>.</small>")
48187fx_solidify_td :
48188  repeat $! l[$>]
48189    to_rgba sh 0,{s-2} if $5 srgb2rgb. fi rm.
48190    if $4
48191      . sh. 100% if $4>0 erode. {1+2*$4} else dilate. {1-2*$4} fi rm.
48192      solidify. $1%,$2,$3
48193      rv blend alpha
48194    else
48195      solidify $1%,$2,$3
48196    fi
48197    if $5 rgb2srgb. fi
48198  endl done
48199
48200fx_solidify_td_preview :
48201  gui_split_preview "fx_solidify_td $*",${-3--1}
48202
48203#@gui Smooth [Anisotropic] : fx_smooth_anisotropic, fx_smooth_anisotropic_preview(0)
48204#@gui : Amplitude = float(60,0,1000)
48205#@gui : Sharpness = float(0.7,0,2)
48206#@gui : Anisotropy = float(0.3,0,1)
48207#@gui : Gradient Smoothness = float(0.6,0,10)
48208#@gui : Tensor Smoothness = float(1.1,0,10)
48209#@gui : Spatial Precision = float(0.8,0.1,2)
48210#@gui : Angular Precision = float(30,1,180)
48211#@gui : Value Precision = float(2,0.1,5)
48212#@gui : Interpolation = choice(0,"Nearest Neighbor","Linear","Runge-Kutta")
48213#@gui : Fast Approximation = bool(1)
48214#@gui : Iterations = int(1,1,10)
48215#@gui : sep = separator()
48216#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48217#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48218#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48219#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48220#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48221#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48222#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48223#@gui : sep = separator()
48224#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48225#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48226#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48227#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48228#@gui : sep = separator()
48229#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/08/27</i>.</small>")
48230fx_smooth_anisotropic :
48231  repeat $! l[$>]
48232    ac "repeat $11 smooth $1,$2,$3,$4,$5,$6,$7,$8,$9,$10 done",$12
48233  endl done c 0,255
48234
48235fx_smooth_anisotropic_preview :
48236  gui_split_preview "fx_smooth_anisotropic $*",${-3--1}
48237
48238#@gui Smooth [Antialias] : fx_smooth_antialias, fx_smooth_antialias_preview(0)
48239#@gui : Amplitude = float(5,0,100)
48240#@gui : Edge Threshold (%) = float(10,0,100)
48241#@gui : Smoothness = float(0.8,0,5)
48242#@gui : sep = separator()
48243#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48244#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48245#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48246#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48247#@gui : sep = separator()
48248#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/11/13</i>.</small>")
48249fx_smooth_antialias :
48250  repeat $! l[$>]
48251    +diffusiontensors 0,1,1,$3,$3
48252    +gradient_norm.. >=. $2% *[-2,-1]
48253    smooth.. .,{$1^1/3},0.5,120,2,1 rm.
48254  endl done
48255
48256fx_smooth_antialias_preview :
48257  gui_split_preview "fx_smooth_antialias $*",${-3--1}
48258
48259#@gui Smooth [Bilateral] : fx_smooth_bilateral, fx_smooth_bilateral_preview(0)
48260#@gui : Spatial Variance = float(10,0,100)
48261#@gui : Value Variance = float(7,0,100)
48262#@gui : Iterations = int(2,1,10)
48263#@gui : sep = separator()
48264#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48265#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48266#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48267#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48268#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48269#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48270#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48271#@gui : sep = separator()
48272#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48273#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48274#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48275#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48276#@gui : sep = separator()
48277#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
48278fx_smooth_bilateral : skip ${5=0},${6=0}
48279  ac "repeat $3 bilateral $1,$2 done",$4
48280
48281fx_smooth_bilateral_preview :
48282  gui_split_preview "fx_smooth_bilateral $*",${-3--1}
48283
48284#@gui Denoise : fx_denoise,fx_denoise_preview(0)
48285#@gui : Noise type = choice{"Soft","Heavy","Heavy (Faster)","Poisson + Gaussian"}
48286#@gui : Iterations = int(1,1,5)
48287#@gui : sep = separator()
48288#@gui : Update Neural Network = button()
48289#@gui : sep = separator()
48290#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48291#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48292#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48293#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48294#@gui : sep = separator()
48295#@gui : note = note{"<small>
48296#@gui : <b><span color="#EE5500">Note:</span></b> This filter uses a convolutional neural network (CNN)
48297#@gui : to denoise images. This filter does not take advantage of GPU computing, so expect it to be quite slow
48298#@gui : if you don't have many CPU cores available.
48299#@gui : </small>"}
48300#@gui : sep = separator()
48301#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/11/06</i>.</small>")
48302fx_denoise :
48303  if $3 delete ${-path_cache}gmic_denoise_cnn.gmz fi
48304  repeat $2
48305    denoise_cnn $1,64
48306    progress {round(($>+1)/$2*100)}
48307  done
48308
48309fx_denoise_preview :
48310  r2din 280,280,0,0,0.5,0.5
48311  gui_split_preview "fx_denoise $*",${-3--1}
48312
48313#@gui Smooth [Guided] : fx_smooth_guided, fx_smooth_guided_preview(0)
48314#@gui : Guide As = choice("Self","Top Layer","Bottom Layer")
48315#@gui : Radius = int(5,1,100)
48316#@gui : Smoothness = float(30,0,512)
48317#@gui : Iterations = int(1,1,10)
48318#@gui : sep = separator()
48319#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48320#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48321#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48322#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48323#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48324#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48325#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48326#@gui : sep = separator()
48327#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48328#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48329#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48330#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48331#@gui : sep = separator()
48332#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/10/02</i>.</small>")
48333fx_smooth_guided : skip ${6=0},${7=0}
48334  if $1" && "!narg($_guide)
48335    if $!<2 gui_warning_preview "Missing guide layer" return fi
48336    store[{$1==1?0:-1}] _guide
48337  fi
48338  if $1==0 # Self-guide
48339    ac "repeat $4 guided $2,$3 done",$5
48340  elif $1==1 # Guide as top layer
48341    ac "$_guide r. ..,..,1,100%,0,0,0.5,0.5 repeat $4 guided[0] [1],$2,$3 done rm.",$5
48342    if !narg($_is_preview) $_guide mv. 0 fi
48343  else # Guide as bottom layer
48344    ac "$_guide r. ..,..,1,100%,0,0,0.5,0.5 repeat $4 guided[0] [1],$2,$3 done rm.",$5
48345    if !narg($_is_preview) $_guide fi
48346  fi
48347
48348fx_smooth_guided_preview :
48349  if $1" && "!narg($_guide)
48350    if $!<2 gui_warning_preview "Missing guide layer" return fi
48351    store[{$1==1?0:-1}] _guide
48352  fi
48353  _is_preview=1
48354  gui_split_preview "fx_smooth_guided $*",${-3--1}
48355
48356#@gui Smooth [Diffusion] : fx_smooth_diffusion, fx_smooth_diffusion_preview(0)
48357#@gui : Sharpness = float(0.7,0,2)
48358#@gui : Anisotropy = float(0.3,0,1)
48359#@gui : Gradient Smoothness = float(0.6,0,10)
48360#@gui : Tensor Smoothness = float(1.1,0,10)
48361#@gui : Time Step = float(15,5,50)
48362#@gui : Iterations = int(8,1,100)
48363#@gui : sep = separator()
48364#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48365#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48366#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48367#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48368#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48369#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48370#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48371#@gui : sep = separator()
48372#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48373#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48374#@gui : sep = separator()
48375#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48376#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48377#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48378#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48379#@gui : sep = separator()
48380#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
48381fx_smooth_diffusion :
48382  ac "gui_parallel_overlap \"smooth $6,$1,$2,$3,$4,$5,0 c 0,255\",$8,$9",$7
48383
48384fx_smooth_diffusion_preview :
48385  gui_split_preview "fx_smooth_diffusion $*",${-3--1}
48386
48387#@gui Smooth [Mean-Curvature] : fx_smooth_meancurvature, fx_smooth_meancurvature_preview(0)
48388#@gui : Time Step = float(30,5,50)
48389#@gui : Iterations = int(4,1,30)
48390#@gui : Keep Iterations as Different Layers = bool(false)
48391#@gui : sep = separator()
48392#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48393#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48394#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48395#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48396#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48397#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48398#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48399#@gui : sep = separator()
48400#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48401#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48402#@gui : sep = separator()
48403#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48404#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48405#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48406#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48407#@gui : sep = separator()
48408#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
48409fx_smooth_meancurvature :
48410  ac "gui_parallel_overlap \"meancurvature_flow $2,$1,$3 c 0,255\",$5,$6",$4
48411
48412fx_smooth_meancurvature_preview :
48413  gui_split_preview "fx_smooth_meancurvature $*",${-3--1}
48414
48415#@gui Smooth [Median] : fx_smooth_median, fx_smooth_median_preview(0)
48416#@gui : Radius = int(3,1,20)
48417#@gui : Threshold = float(255,0,255)
48418#@gui : sep = separator()
48419#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48420#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48421#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48422#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48423#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48424#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48425#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48426#@gui : sep = separator()
48427#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48428#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48429#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48430#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48431#@gui : sep = separator()
48432#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48433fx_smooth_median :
48434  ac "median $1,$2",$3
48435
48436fx_smooth_median_preview :
48437  gui_split_preview "fx_smooth_median $*",${-3--1}
48438
48439#@gui Smooth [NL-Means] : fx_smooth_nlmeans, fx_smooth_nlmeans_preview(0)
48440#@gui : Patch Size = float(4,0.5,10)
48441#@gui : Spatial Bandwidth = int(4,3,13)
48442#@gui : Tonal Bandwidth = float(10,1,50)
48443#@gui : Patch Measure = choice(3,"Linf-Norm","L1-Norm","L2-Norm","Luminance","Lightness","RGB")
48444#@gui : sep = separator()
48445#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48446#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48447#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48448#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48449#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48450#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48451#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48452#@gui : sep = separator()
48453#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48454#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48455#@gui : sep = separator()
48456#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48457#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48458#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48459#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48460#@gui : sep = separator()
48461#@gui : note = note("<small>Author: <i>Jérôme Boulanger</i>.      Latest Update: <i>2015/01/07</i>.</small>")
48462fx_smooth_nlmeans:
48463  repeat $! l[$>]
48464    if s==1 nlmeans $1,$2,$3,-_fx_smooth_nlmeans$4 # Handle separately gray scale images.
48465    else ac "gui_parallel_overlap \"nlmeans $1,$2,$3,-_fx_smooth_nlmeans$4\",$6,$7",$5
48466    fi
48467  endl done
48468
48469_fx_smooth_nlmeans0 : s c abs max
48470_fx_smooth_nlmeans1 : s c abs +
48471_fx_smooth_nlmeans2 : norm
48472_fx_smooth_nlmeans3 : if s>=3 channels 0,2 luminance else norm fi
48473_fx_smooth_nlmeans4 : if s>=3 channels 0,2 srgb2rgb rgb2lab channels 0 else norm fi
48474_fx_smooth_nlmeans5 :
48475
48476fx_smooth_nlmeans_preview:
48477  gui_split_preview "fx_smooth_nlmeans $*",${-3--1}
48478
48479#@gui Smooth [Patch-Based] : fx_smooth_patch, fx_smooth_patch_preview(0)
48480#@gui : Spatial Variance = float(10,0.1,200)
48481#@gui : Patch Variance = float(10,0.1,200)
48482#@gui : Patch Size = int(3,2,21)
48483#@gui : Lookup Size = int(5,2,21)
48484#@gui : Patch Smoothness = float(0,0,4)
48485#@gui : Fast Approximation = bool(1)
48486#@gui : Iterations = int(1,1,10)
48487#@gui : sep = separator()
48488#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48489#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48490#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48491#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48492#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48493#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48494#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48495#@gui : sep = separator()
48496#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48497#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48498#@gui : sep = separator()
48499#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48500#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48501#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48502#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48503#@gui : sep = separator()
48504#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
48505fx_smooth_patch :
48506  ac "gui_parallel_overlap \"repeat $7 denoise $1,$2,$3,$4,$5,$6 done c 0,255\",$9,$10",$8
48507
48508fx_smooth_patch_preview :
48509  gui_split_preview "fx_smooth_patch $*",${-3--1}
48510
48511#@gui Smooth [Patch-PCA] : fx_smooth_patchpca, fx_smooth_patchpca_preview(0)
48512#@gui : Strength = float(4,0,16)
48513#@gui : Patch Size = int(7,2,21)
48514#@gui : Lookup Size = int(11,2,21)
48515#@gui : Spatial Sampling = int(7,1,16)
48516#@gui : sep = separator()
48517#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48518#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48519#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48520#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48521#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48522#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48523#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48524#@gui : sep = separator()
48525#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48526#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48527#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48528#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48529#@gui : sep = separator()
48530#@gui : note = note{"<small><b>Note:</b> Beware, this filter uses a very computationally intensive algorithm to
48531#@gui : denoise images. So, do not complain too much if you have less than 8 cores available for the computation :)
48532#@gui : </small>"}
48533#@gui : sep = separator()
48534#@gui : note = note("<small>Authors: <i>David Tschumperlé</i> and <i>Jérome Boulanger</i>.
48535#@gui :       Latest Update: <i>2016/24/03</i>.</small>")
48536fx_smooth_patchpca :
48537  ac "denoise_patchpca ${1-4} c 0,255",$5
48538
48539fx_smooth_patchpca_preview :
48540  gui_split_preview "fx_smooth_patchpca $*",${-3--1}
48541
48542#@gui Smooth [Perona-Malik] : fx_smooth_peronamalik, fx_smooth_peronamalik_preview(0)
48543#@gui : K-Factor = float(20,0,255)
48544#@gui : Time Step = float(5,5,50)
48545#@gui : Iterations = int(5,1,30)
48546#@gui : Keep Iterations as Different Layers = bool(false)
48547#@gui : sep = separator()
48548#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48549#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48550#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48551#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48552#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48553#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48554#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48555#@gui : sep = separator()
48556#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48557#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48558#@gui : sep = separator()
48559#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48560#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48561#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48562#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48563#@gui : sep = separator()
48564#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/26/11</i>.</small>")
48565fx_smooth_peronamalik :
48566  ac "gui_parallel_overlap \"peronamalik_flow $1,$3,$2,$4 c 0,255\",$6,$7",$5
48567
48568fx_smooth_peronamalik_preview :
48569  gui_split_preview "fx_smooth_peronamalik $*",${-3--1}
48570
48571#@gui Smooth [Selective Gaussian] : fx_smooth_selective, fx_smooth_selective_preview(0)
48572#@gui : Amplitude = float(5,0,20)
48573#@gui : Edges = float(0.5,0,2)
48574#@gui : Scales = int(5,1,10)
48575#@gui : Iterations = int(1,1,10)
48576#@gui : sep = separator()
48577#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48578#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48579#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48580#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48581#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48582#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48583#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48584#@gui : sep = separator()
48585#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48586#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48587#@gui : sep = separator()
48588#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48589#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48590#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48591#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48592#@gui : sep = separator()
48593#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
48594fx_smooth_selective :
48595  ac "gui_parallel_overlap \"repeat $4 blur_selective $1,$2,$3 done c 0,255\",$6,$7",$5
48596
48597fx_smooth_selective_preview :
48598  gui_split_preview "fx_smooth_selective $*",${-3--1}
48599
48600#@gui Smooth [Skin] : fx_smooth_skin, fx_smooth_skin_preview(1)
48601#@gui : note = note("<small><b>Step 1:</b> Skin detection</small>")
48602#@gui : Skin Estimation = choice(2,"None","Manual","Automatic")
48603#@gui : Tolerance = float(0.5,0,1)
48604#@gui : Smoothness = float(1,0,5)
48605#@gui : Threshold = float(1,0,10)
48606#@gui : Pre-Normalize Image = bool(1)
48607#@gui : X-Coordinate [Manual] = float(50,0,100)
48608#@gui : Y-Coordinate [Manual] = float(50,0,100)
48609#@gui : Radius [Manual] = float(5,0,25)
48610#@gui : sep = separator()
48611#@gui : note = note("<small><b>Step 2:</b> Medium scale smoothing</small>")
48612#@gui : Base Scale = float(2,0,10)
48613#@gui : Fine Scale = float(0.2,0,0.8)
48614#@gui : Smoothness = float(3,0,10)
48615#@gui : Smoothness Type = choice(1,"Gaussian","Bilateral")
48616#@gui : sep = separator()
48617#@gui : note = note("<small><b>Step 3:</b> Details enhancement</small>")
48618#@gui : Gain = float(0.05,0,0.5)
48619#@gui : sep = separator()
48620#@gui : Preview Data = choice{5,"Skin Mask","Base Scale","Medium Scale (Original)","Medium Scale (Smoothed)",
48621#@gui : "Fine Scale","Result Image"}
48622#@gui : sep = separator()
48623#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48624#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48625#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48626#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48627#@gui : sep = separator()
48628#@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=H8pQfq-ybCc")
48629#@gui : sep = separator()
48630#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/20/12</i>.</small>")
48631fx_smooth_skin :
48632  repeat $! l[$>] split_opacity l[0] to_rgb
48633
48634    # Skin detection step.
48635    if $5 +balance_gamma 128,128,128 else [0] fi
48636    if $1==0 channels. 0 f. 1 elif $1==2 detect_skin. $2 else detect_skin. $2,$6%,$7%,$8% fi
48637    M={iM} b. $3% *. {$M/iM} *. $4 c. 0,1
48638
48639    # Details smoothing step.
48640    split_details[0] 4,$9%,$10%
48641    +_fx_smooth_skin[2] $12,$11
48642    j[2] .,0,0,0,0,1,.. rm[-2,-1]
48643    *. {10^$13} + c 0,255
48644
48645  endl a c endl done
48646
48647_fx_smooth_skin :
48648  if $1==0 b {$2/8}%
48649  else
48650    if $2>0
48651      m={im} M={iM} n 0,255
48652      repeat int($2/5) bilateral 3%,{5*3} done
48653      bilateral 3%,{($2%5)*3}
48654      * {($M-$m)/255} + $m
48655    fi
48656  fi
48657
48658fx_smooth_skin_preview :
48659  if $-2==0
48660    gui_split_preview "if $5 balance_gamma 128,128,128 fi if $1==0 f 1 elif $1==2 detect_skin $2 "\
48661     "else detect_skin $2,$6%,$7%,$8% fi M={iM} b $3% * {255*$M/iM} * $4 c 0,255",${-3--1}
48662  elif $-2==1
48663    gui_split_preview "b $9%",${-3--1}
48664  elif $-2==2
48665    gui_split_preview "split_details 4,$9%,$10% k.. n 0,255",${-3--1}
48666  elif $-2==3
48667    gui_split_preview "split_details 4,$9%,$10% k.. _fx_smooth_skin $12,$11 n 0,255",${-3--1}
48668  elif $-2==4
48669    gui_split_preview "split_details 4,$9%,$10% k. n 0,255",${-3--1}
48670  else
48671    gui_split_preview "fx_smooth_skin $*",${-3--1}
48672  fi
48673
48674  if $1==1
48675    to_rgb
48676    circle $6%,$7%,$8%,0.2,0,255,0
48677    circle $6%,$7%,$8%,0.4,0xFFFFFFFF,0,255,0
48678    line {$6-0.25*$8}%,{$7-0.25*$8}%,{$6+0.25*$8}%,{$7+0.25*$8}%,0.8,255,255,0
48679    line {$6+0.25*$8}%,{$7-0.25*$8}%,{$6-0.25*$8}%,{$7+0.25*$8}%,0.8,255,255,0
48680  fi
48681
48682#@gui Smooth [Thin Brush] : fx_smooth_anisotropic, fx_smooth_anisotropic(0)
48683#@gui : Amplitude = float(60,0,1000)
48684#@gui : Sharpness = float(0.9,0,2)
48685#@gui : Anisotropy = float(0.64,0,1)
48686#@gui : Gradient Smoothness = float(3.1,0,10)
48687#@gui : Tensor Smoothness = float(1.10,0,10)
48688#@gui : Spatial Precision = float(0.8,0.1,2)
48689#@gui : Angular Precision = float(30,1,180)
48690#@gui : Value Precision = float(2,0.1,5)
48691#@gui : Interpolation = choice(0,"Nearest Neighbor","Linear","Runge-Kutta")
48692#@gui : Fast Approximation = bool(1)
48693#@gui : Iterations = int(1,1,10)
48694#@gui : Channel(s) = choice("RGB","Luminance","Blue & Red chrominances","Blue chrominance","Red chrominance")
48695#@gui : sep = separator()
48696#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48697#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48698#@gui : note = note{"\n<small><b>Note: </b>This set of anisotropic smoothing parameters has been suggested
48699#@gui : by PhotoComiX.</small>"}
48700#@gui : sep = separator()
48701#@gui : note = note("<small>Author: <i>PhotoComiX</i>.      Latest Update: <i>2010/26/12</i>.</small>")
48702
48703#@gui Smooth [Total Variation] : fx_smooth_tv, fx_smooth_tv_preview(0)
48704#@gui : Time Step = float(30,5,100)
48705#@gui : Iterations = int(10,1,40)
48706#@gui : Keep Iterations as Different Layers = bool(false)
48707#@gui : sep = separator()
48708#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48709#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48710#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48711#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48712#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48713#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48714#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48715#@gui : sep = separator()
48716#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48717#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48718#@gui : sep = separator()
48719#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48720#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48721#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48722#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48723#@gui : sep = separator()
48724#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/08</i>.</small>")
48725fx_smooth_tv :
48726  ac "gui_parallel_overlap \"tv_flow $2,$1,$3 c 0,255\",$5,$6",$4
48727
48728fx_smooth_tv_preview :
48729  gui_split_preview "fx_smooth_tv $*",${-3--1}
48730
48731#@gui Smooth [Wavelets] : fx_smooth_haar, fx_smooth_haar_preview(0)
48732#@gui : Threshold = float(1,0,10)
48733#@gui : Iterations = int(10,1,32)
48734#@gui : Scales = int(10,2,10)
48735#@gui : sep = separator()
48736#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
48737#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
48738#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
48739#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
48740#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
48741#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
48742#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
48743#@gui : sep = separator()
48744#@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads",
48745#@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256)
48746#@gui : sep = separator()
48747#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
48748#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
48749#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
48750#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
48751#@gui : sep = separator()
48752#@gui : note = note("<small>Author: <i>Jérome Boulanger and David Tschumperlé</i>.
48753#@gui :       Latest Update: <i>2013/27/08</i>.</small>")
48754fx_smooth_haar :
48755  remove_opacity
48756  ac "gui_parallel_overlap \"denoise_haar $1,$3,$2 c 0,255\",$5,$6",$4
48757
48758fx_smooth_haar_preview :
48759  gui_split_preview "fx_smooth_haar $*",${-3--1}
48760
48761#@gui Upscale [Diffusion] : fx_upscale_smart, fx_upscale_smart_preview(0)
48762#@gui : Width = text("200%")
48763#@gui : Height = text("200%")
48764#@gui : Smoothness = float(2,0,20)
48765#@gui : Anisotropy = float(0.4,0,1)
48766#@gui : Sharpness = float(50,0,100)
48767#@gui : sep = separator()
48768#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48769fx_upscale_smart :
48770  to_rgb upscale_smart $1,$2,1,$3,$4,$5 c 0,255
48771
48772fx_upscale_smart_preview :
48773  repeat $!
48774    +r. $1,$2,1,1,0
48775    if w<{-2,w}" || "h<{-2,h}  # Test for downscaling
48776      rm. /. 4
48777      0 t. "Downscaling is\nnot allowed!",5,5,20,1,255 r. ..,..,1,1,0,0,0.5,0.5
48778      -|[-2,-1]
48779    else
48780      z.. {50-50*{-2,w}/w}%,{50-50*{-2,h}/h}%,{50+50*{-2,w}/w}%,{50+50*{-2,h}/h}%
48781      rm. fx_upscale_smart. $1,$2,$3,$4,$5 c. 0,255
48782    fi
48783  mv. 0 done
48784
48785#@gui Upscale [Scale2x] : fx_scalenx, fx_scalenx_preview(0)
48786#@gui : Scaling Factor = choice("x 2","x 3","x 4","x 6","x 8","x 9","x 12","x 16","x 18","x 27")
48787#@gui : Colorbase = choice(0,"RGB","YCbCr","Lab")
48788#@gui : note = note{"\n<small><b>Note: </b>
48789#@gui : This filter re-implements the scaling algorithm described at :
48790#@gui : </small>"}
48791#@gui : url = link("http://scale2x.sourceforge.net")
48792#@gui : note = note{"<small>
48793#@gui : This filter is useful for resizing images that have very few colors
48794#@gui : (e.g. indexed images). It is generally useless for true colors images.
48795#@gui : </small>"}
48796#@gui : sep = separator()
48797#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
48798fx_scalenx :
48799  repeat $! l[$>] split_opacity
48800    if $2==1 rgb2ycbcr[0] round[0]
48801    elif $2==2 rgb2lab8[0] round[0]
48802    fi
48803    if $1==0 scale2x
48804    elif $1==1 scale3x
48805    elif $1==2 scale2x scale2x
48806    elif $1==3 scale3x scale2x
48807    elif $1==4 scale2x scale2x scale2x
48808    elif $1==5 scale3x scale3x
48809    elif $1==6 scale3x scale2x scale2x
48810    elif $1==7 scale2x scale2x scale2x scale2x
48811    elif $1==8 scale3x scale3x scale2x
48812    elif $1==9 scale3x scale3x scale3x
48813    fi
48814    if $2==1 ycbcr2rgb[0]
48815    elif $2==2 lab82rgb[0]
48816    fi
48817  a c endl done
48818
48819fx_scalenx_preview :
48820  z 40%,40%,60%,60%
48821  fx_scalenx $1,$2
48822
48823#@gui Upscale [DCCI2x]: fx_scale_dcci2x, fx_scale_dcci2x_preview(0)
48824#@gui : note = note("<i>Directional Cubic Convolution Interpolation</i>"), sep = separator()
48825#@gui : Threshold = float(1.15,1,2)
48826#@gui : Exponent = int(5,1,6)
48827#@gui : Extend 1px = _bool(0)
48828#@gui : sep = separator()
48829#@gui : note = note("<small>Author: <i>Garagecoder</i>.      Latest Update : <i>2015/11/07</i>.</small>")
48830#@gui : note = note{"\n<small><b>Note: </b>
48831#@gui : This filter re-implements the scaling algorithm described at :
48832#@gui : </small>"}
48833#@gui : url = link("wikipedia.org","https://en.wikipedia.org/wiki/Directional_Cubic_Convolution_Interpolation")
48834#@gui : note = note("<small>The algorithm is intended for enlarging images while avoiding</small>")
48835#@gui : note = note("<small>artifacts, e.g. staircase artifacts.</small>")
48836#@gui : note = note("\n<small>Threshold controls edge[lower] to texture[higher] balance.</small>")
48837#@gui : note = note("<small>Exponent controls texture edge sharpness[higher].</small>")
48838#@gui : note = note("<small>Warning: highly experimental...</small>")
48839fx_scale_dcci2x : skip ${1=1.15},${2=5},${3=0}
48840  repeat $! l[$>]
48841    split_opacity scale_dcci2x ${1-3} a c c 0,255
48842  endl done
48843
48844fx_scale_dcci2x_preview :
48845  z 25%,25%,75%,75% fx_scale_dcci2x $*
48846
48847#@gui ____<b>Rendering</b>
48848#-------------------------
48849
48850# Generic function to render a 3D image, with usual rendering parameters :
48851# $1 = Width
48852# $2 = Height
48853# $3 = Object size
48854# $4 = X-angle
48855# $5 = Y-angle
48856# $6 = Z-angle
48857# $7 = FOV
48858# $8 = X-light
48859# $9 = Y-light
48860# $10 = Z-light
48861# $11 = Specular lightness
48862# $12 = Specular shininess
48863# $13 = Rendering mode.
48864# $14 = Antialiasing (0 | 1)
48865fx_render3d : skip ${14=1}
48866  width={(1+$14)*$1} height={(1+$14)*$2}
48867  n3d c3d m3d {round($13)} f3d={0.5*max($width,$height)/tan($7*pi/360)}
48868  f3d $f3d l3d {$8*$f3d},{$9*$f3d},{$10*$f3d} sl3d $11 ss3d $12
48869  repeat $! l[$>]
48870    *3d {$3*max($width,$height)} r3d 0,0,1,{-$6} r3d 0,1,0,{-$5} r3d 1,0,0,{-$4}
48871    $width,$height,1,3,-1
48872    j3d. ..,50%,50% rm..
48873    to_rgba replace_color 0,0,-1,-1,-1,255,0,0,0,0
48874    if $14
48875      r $1,$2,1,100%,2 s c,-3 +. 1e-5 /[0] [1] *[0] 255 a c
48876    fi
48877  endl done
48878
48879#@gui 3D Blocks : fx_blocks3d, fx_blocks3d(1)
48880#@gui : Resolution = int(32,1,128)
48881#@gui : Smoothness = float(0,0,40)
48882#@gui : Elevation = float(4,-10,10)
48883#@gui : Size = float(1.5,0,3)
48884#@gui : Angle = float(30,0,360)
48885#@gui : Tilt = float(60,0,90)
48886#@gui : FOV = float(45,1,90)
48887#@gui : Centering (%) = point(50,50)
48888#@gui : sep = separator()
48889#@gui : X-Light = float(0,-100,100)
48890#@gui : Y-Light = float(-50,-100,100)
48891#@gui : Z-Light = float(-100,-100,0)
48892#@gui : Specular Lightness = float(0.5,0,1)
48893#@gui : Specular Shininess = float(0.7,0,3)
48894#@gui : Use Light = bool(1)
48895#@gui : Antialiasing = bool(1)
48896#@gui : Outline Color = color(0,0,0,128)
48897#@gui : sep = separator()
48898#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/10/02</i>.</small>")
48899fx_blocks3d :
48900  repeat $! l[$>]
48901    nm=${"-gui_layer_name"}
48902    W={w} H={h} M={max(w,h)}
48903    if w>h r2dx $1 else r2dy $1 fi
48904    w={w} h={h} m={max(w,h)}
48905    if $3>0 mirror y fi
48906    imageblocks3d $3,$2%
48907    -3d. {$w/2},{$h/2} f={$4*$M/($m*(2-$16))} *3d $f,$f,{$f*abs($3*$1/100)}
48908    if $3>0 r3d 1,0,0,180 fi
48909    r3d 0,0,1,$5 r3d 1,0,0,-$6
48910
48911    # Render object.
48912    if $16 {2*$M},{2*$M},1,4,-1 else $M,$M,1,4,-1 fi
48913    f3d={0.5*w/tan($7*pi/360)} f3d $f3d
48914    l3d {$10*$f3d},{$11*$f3d},{$12*$f3d} sl3d $13 ss3d $14
48915    j3d. [0],$8%,$9%,0,1,{if($15,3,2)},0,1
48916    sh. 100% +. 1 *. 255 rm.
48917
48918    # Render object outline
48919    if $20
48920      .,.,1,3,-1
48921      j3d. [0],$8%,$9%,0,1,3,0,1 rm[0]
48922      g. xy,1 +[-2,-1] norm. !=. 0
48923      +r. 100%,100%,1,3
48924      sh. 0 *. $17 rm.
48925      sh. 1 *. $18 rm.
48926      sh. 2 *. $19 rm.
48927      j[0] .,0,0,0,0,{$20/255},.. rm[-2,-1]
48928    else rm[0]
48929    fi
48930
48931    replace_color 0,0,-1,-1,-1,0,0,0,0,0
48932    if $16 r 50%,50%,1,4,2 fi
48933    c 0,255 nm name($nm)
48934  endl done
48935
48936#@gui 3D Colored Object : fx_coloredobject3d, fx_coloredobject3d_preview(1)
48937#@gui : Type = choice{1,"Plane","Box","Pyramid","Ellipsoid","Torus","Gyroid","Weird","Cup"}
48938#@gui : Color = color(128,128,128,255)
48939#@gui : sep = separator()
48940#@gui : Size-1 = float(0.5,0,3)
48941#@gui : Size-2 = float(0.5,0,3)
48942#@gui : Size-3 = float(0.5,0,3)
48943#@gui : X-Angle = float(57,0,360)
48944#@gui : Y-Angle = float(41,0,360)
48945#@gui : Z-Angle = float(21,0,360)
48946#@gui : FOV = float(45,1,90)
48947#@gui : X-Light = float(0,-100,100)
48948#@gui : Y-Light = float(0,-100,100)
48949#@gui : Z-Light = float(-100,-100,0)
48950#@gui : Specular Lightness = float(0.5,0,1)
48951#@gui : Specular Shininess = float(0.7,0,3)
48952#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
48953#@gui : Antialiasing = bool(1)
48954#@gui : sep = separator()
48955#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/16/05</i>.</small>")
48956_fx_coloredobject3d :
48957  to_rgb _fx_coloredobject3d$1$2 ${6-8} col3d. ${3-5}
48958  db3d 0
48959
48960fx_coloredobject3d :
48961  _fx_coloredobject3d "_",${1-4,6-8}
48962  repeat $!-1
48963    +fx_render3d. {$>,w},{$>,h},$6,${9--1}
48964    sh. 3 *. {$5/255} rm.
48965    blend[$>,-1] alpha
48966  done
48967  rm.
48968
48969fx_coloredobject3d_preview :
48970  _fx_coloredobject3d "_preview_",${1-4,6-8}
48971  repeat $!-1
48972    +fx_render3d. {$>,w},{$>,h},$6,${9--1}
48973    sh. 3 *. {$5/255} rm.
48974    blend[$>,-1] alpha
48975  done rm.
48976
48977_fx_coloredobject3d_0 : plane3d 1 *3d. $1,$2,1
48978_fx_coloredobject3d_1 : box3d 1 *3d. $1,$2,$3
48979_fx_coloredobject3d_2 : pyramid3d 1,1 *3d. $1,$2,$3
48980_fx_coloredobject3d_3 : sphere3d 1 *3d. 1,{2*$2},{2*$3}
48981_fx_coloredobject3d_4 : torus3d $1,{$2/2},100,50 *3d. $3,0.5,0.5
48982_fx_coloredobject3d_5 : gyroid3d 24 *3d. $1,$2,$3
48983_fx_coloredobject3d_6 : weird3d 32 *3d. $1,$2,$3
48984_fx_coloredobject3d_7 : cup3d 128 *3d. $1,$2,$3
48985_fx_coloredobject3d_preview_0 : plane3d 1 *3d. $1,$2,1
48986_fx_coloredobject3d_preview_1 : box3d 1 *3d. $1,$2,$3
48987_fx_coloredobject3d_preview_2 : pyramid3d 1,1 *3d. $1,$2,$3
48988_fx_coloredobject3d_preview_3 : sphere3d 1 *3d. 1,{2*$2},{2*$3}
48989_fx_coloredobject3d_preview_4 : torus3d $1,{$2/2},100,50 *3d. $3,0.5,0.5
48990_fx_coloredobject3d_preview_5 : gyroid3d 8 *3d. $1,$2,$3
48991_fx_coloredobject3d_preview_6 : weird3d 12 *3d. $1,$2,$3
48992_fx_coloredobject3d_preview_7 : cup3d 64 *3d. $1,$2,$3
48993
48994#@gui 3D Elevation : fx_elevation3d, fx_elevation3d_preview(1)
48995#@gui : Factor = float(100,-1000,1000)
48996#@gui : Smoothness = float(1,0,10)
48997#@gui : sep = separator()
48998#@gui : Width = _int(1024,8,4096)
48999#@gui : Height = _int(1024,8,4096)
49000#@gui : Size = float(0.8,0,3)
49001#@gui : X-Angle = float(25,0,360)
49002#@gui : Y-Angle = float(0,0,360)
49003#@gui : Z-Angle = float(21,0,360)
49004#@gui : FOV = float(45,1,90)
49005#@gui : X-Light = float(0,-100,100)
49006#@gui : Y-Light = float(0,-100,100)
49007#@gui : Z-Light = float(-100,-100,0)
49008#@gui : Specular Lightness = float(0.5,0,1)
49009#@gui : Specular Shininess = float(0.7,0,3)
49010#@gui : Rendering = choice(2,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
49011#@gui : Antialiasing = bool(1)
49012#@gui : sep = separator()
49013#@gui : Top Layer Defines Object Texture = bool(0)
49014#@gui : sep = separator()
49015#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/11/10</i>.</small>")
49016_fx_elevation3d :
49017  if $3" && "$!>1 # Textured object
49018    repeat $!-1 l[0,{1+$>}]
49019      +ri.. .,3
49020      n. 0,{abs($1)} *. {sign($1)} b. $2
49021      elevation3d.. . rm.
49022    endl done
49023  else  # Non-textured object
49024    repeat $! l[$>]
49025      +norm n. 0,{abs($1)} *. {sign($1)} b. $2
49026      elevation3d.. . rm.
49027    endl done
49028  fi
49029  db3d
49030
49031fx_elevation3d :
49032  _fx_elevation3d ${1-2},$17
49033  s0,s1=^,^0 fx_render3d[${s$17}] ${3--1}
49034
49035fx_elevation3d_preview :
49036  fx_elevation3d ${1-2},{w},{h},${5--1}
49037  if $17 rm[0] fi
49038
49039#@gui 3D Extrusion : fx_extrude3d, fx_extrude3d_preview(1)
49040#@gui : Depth = float(10,1,1024)
49041#@gui : Resolution = int(512,1,1024)
49042#@gui : Smoothness = float(0.6,0,3)
49043#@gui : sep = separator()
49044#@gui : Width = _int(1024,1,4096)
49045#@gui : Height = _int(1024,1,4096)
49046#@gui : Size = float(0.5,0,3)
49047#@gui : X-Angle = float(57,0,360)
49048#@gui : Y-Angle = float(41,0,360)
49049#@gui : Z-Angle = float(21,0,360)
49050#@gui : FOV = float(45,1,90)
49051#@gui : X-Light = float(0,-100,100)
49052#@gui : Y-Light = float(0,-100,100)
49053#@gui : Z-Light = float(-100,-100,0)
49054#@gui : Specular Lightness = float(0.5,0,1)
49055#@gui : Specular Shininess = float(0.7,0,3)
49056#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
49057#@gui : Antialiasing = bool(1)
49058#@gui : sep = separator()
49059#@gui : Top Layer Defines Object Texture = bool(0)
49060#@gui : sep = separator()
49061#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/11/10</i>.</small>")
49062_fx_extrude3d :
49063  if $4" && "$!>1 # Textured object
49064    repeat $!-1 l[0,{$>+1}] extrude3d. $1,$2,$3% t3d. .. endl done
49065  else extrude3d $1,$2,$3% # Non-textured object
49066  fi
49067  db3d 0
49068
49069fx_extrude3d :
49070  _fx_extrude3d ${1-3},$18
49071  s0,s1=^,^0 fx_render3d[${s$18}] ${4--1}
49072
49073fx_extrude3d_preview :
49074  fx_extrude3d ${1-3},{w},{h},${6--1}
49075  if $18 rm[0] fi
49076
49077#@gui 3D Image Object : fx_imageobject3d, fx_imageobject3d_preview(1)
49078#@gui : Type = choice{1,"Plane","Cube","Pyramid","Sphere","Torus","Gyroid","Weird","Cup","Rubik"}
49079#@gui : sep = separator()
49080#@gui : Width = _int(1024,1,4096)
49081#@gui : Height = _int(1024,1,4096)
49082#@gui : Size = float(0.5,0,3)
49083#@gui : X-Angle = float(57,0,360)
49084#@gui : Y-Angle = float(41,0,360)
49085#@gui : Z-Angle = float(21,0,360)
49086#@gui : FOV = float(45,1,90)
49087#@gui : X-Light = float(0,-100,100)
49088#@gui : Y-Light = float(0,-100,100)
49089#@gui : Z-Light = float(-100,-100,0)
49090#@gui : Specular Lightness = float(0.5,0,1)
49091#@gui : Specular Shininess = float(0.7,0,3)
49092#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
49093#@gui : Antialiasing = bool(1)
49094#@gui : sep = separator()
49095#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49096_fx_imageobject3d :
49097  to_rgb repeat $! l[$>] _fx_imageobject3d$1$2 endl done
49098  db3d 0
49099
49100fx_imageobject3d :
49101  _fx_imageobject3d "_",$1 fx_render3d ${2--1}
49102
49103fx_imageobject3d_preview :
49104  w={w} h={h} _fx_imageobject3d "_preview_",$1 fx_render3d $w,$h,${4--1}
49105
49106_fx_imageobject3d_0 : imageplane3d
49107_fx_imageobject3d_1 : imagecube3d
49108_fx_imageobject3d_2 : imagepyramid3d
49109_fx_imageobject3d_3 : imagesphere3d 128,64
49110_fx_imageobject3d_4 : torus3d 100,30,100,50 t3d. .. rm..
49111_fx_imageobject3d_5 : gyroid3d 24 t3d. .. rm..
49112_fx_imageobject3d_6 : weird3d 32 t3d. .. rm..
49113_fx_imageobject3d_7 : cup3d 128 t3d. .. rm..
49114_fx_imageobject3d_8 : imagerubik3d 5,5
49115_fx_imageobject3d_preview_0 : imageplane3d
49116_fx_imageobject3d_preview_1 : imagecube3d
49117_fx_imageobject3d_preview_2 : imagepyramid3d
49118_fx_imageobject3d_preview_3 : imagesphere3d 64,32
49119_fx_imageobject3d_preview_4 : torus3d 100,30,100,50 t3d. .. rm..
49120_fx_imageobject3d_preview_5 : gyroid3d 8 c3d. n3d. t3d. .. rm..
49121_fx_imageobject3d_preview_6 : weird3d 12 t3d. .. rm..
49122_fx_imageobject3d_preview_7 : cup3d 64 t3d. .. rm..
49123_fx_imageobject3d_preview_8 : imagerubik3d 3,3,5,5
49124
49125#@gui 3D Lathing : fx_lathing3d, fx_lathing3d_preview(1)
49126#@gui : Resolution = int(76,1,1024)
49127#@gui : Smoothness = float(2,0,5)
49128#@gui : Max Angle = float(361,0,361)
49129#@gui : sep = separator()
49130#@gui : Width = _int(1024,1,4096)
49131#@gui : Height = _int(1024,1,4096)
49132#@gui : Size = float(0.5,0,3)
49133#@gui : X-Angle = float(0,0,360)
49134#@gui : Y-Angle = float(0,0,360)
49135#@gui : Z-Angle = float(0,0,360)
49136#@gui : FOV = float(45,1,90)
49137#@gui : X-Light = float(0,-100,100)
49138#@gui : Y-Light = float(0,-100,100)
49139#@gui : Z-Light = float(-100,-100,0)
49140#@gui : Specular Lightness = float(0.5,0,1)
49141#@gui : Specular Shininess = float(0.7,0,3)
49142#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
49143#@gui : Antialiasing = bool(1)
49144#@gui : sep = separator()
49145#@gui : Top Layer Defines Object Texture = bool(0)
49146#@gui : sep = separator()
49147#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49148_fx_lathing3d :
49149  if $4" && "$!>1 # Textured object
49150    repeat $!-1 l[0,{$>+1}] lathe3d. $1,$2%,$3 t3d. .. endl done
49151  else lathe3d $1,$2%,$3 # Non-textured object
49152  fi
49153  db3d 0
49154
49155fx_lathing3d :
49156  _fx_lathing3d ${1-3},$18
49157  s0,s1=^,^0 fx_render3d[${s$18}] ${4--1}
49158  nm pos(0,0),mode(alpha)
49159
49160fx_lathing3d_preview :
49161  fx_lathing3d ${1-3},{w},{h},${6--1}
49162  if $18 rm[0] fi
49163
49164#@gui 3D Random Objects : fx_random3d, fx_random3d(1)
49165#@gui : Type = choice("Cube","Cone","Cylinder","Sphere","Torus")
49166#@gui : Density = int(50,1,300)
49167#@gui : Size = float(3,1,20)
49168#@gui : Z-Range = float(100,0,300)
49169#@gui : FOV = float(45,1,90)
49170#@gui : X-Light = float(0,-100,100)
49171#@gui : Y-Light = float(0,-100,100)
49172#@gui : Z-Light = float(-100,-100,0)
49173#@gui : Specular Lightness = float(0.5,0,1)
49174#@gui : Specular Shininess = float(0.7,0,3)
49175#@gui : Rendering = choice(3,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
49176#@gui : Opacity = float(1,0,1)
49177#@gui : sep = separator()
49178#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49179fx_random3d :
49180  repeat $! l[$>]
49181    f3d={0.5*max(w,h)/tan($5*pi/360)} f3d $f3d l3d {$6*$f3d},{$7*$f3d},{$8*$f3d} sl3d $9 ss3d $10
49182    to_rgb ({w},{h},{d},{s}) /. 2 repeat $2
49183    ({1,@0}) +. {1,@1} *. $3 /. 100 _fx_random3d$1 {^} rm..
49184    r3d. 1,1,0,{u(0,360)}
49185    ({u(-1,1)}) *. {1,@0} ({u(-1,1)}) *. {1,@1}
49186    +3d... {-2,^},{^},{u(-$4,$4)} rm[-2,-1]
49187    col3d. {u(255)},{u(255)},{u(255)} done +3d[2--1] j3d[0] .,50%,50%,0,$12,$11,0,1
49188    k[0]
49189  endl done
49190
49191_fx_random3d0 : box3d $1
49192_fx_random3d1 : ($1) /. 2 cone3d {^},$1 rm..
49193_fx_random3d2 : ($1) /. 2 cylinder3d {^},$1 rm..
49194_fx_random3d3 : sphere3d $1,2
49195_fx_random3d4 : ($1) /. 3 torus3d $1,{^} rm..
49196
49197#@gui Ball : fx_ball, fx_ball_preview(0)
49198#@gui : Radius = int(128,1,1024)
49199#@gui : Specular Light = float(0.8,0,8)
49200#@gui : Specular Size = float(1,0,8)
49201#@gui : Shadow = float(1.5,0,4)
49202#@gui : Color = color(255,0,255)
49203#@gui : sep = separator()
49204#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/27/11</i>.</small>")
49205fx_ball :
49206  ball $1,${5-7},${2-4}
49207  if $!>1 mv. 0 nm[0] "name(Ball),pos("{0,0.5*([${-max_wh}]-[w,h])}")" else nm[0] "name(Ball)" fi
49208
49209fx_ball_preview :
49210  fx_ball $*
49211  if $!>1 rv[-2,-1] blend[-2,-1] alpha fi
49212
49213#@gui Circle Art : fx_circle_art, fx_circle_art
49214#@gui : Type = choice(1,"Random","Lissajous spiral")
49215#@gui : Density = float(15,0,100)
49216#@gui : Radius = float(0.5,0,1)
49217#@gui : Modulo = int(8,2,16)
49218#@gui : Anti-Aliasing = bool(1)
49219#@gui : Random Colors = bool(1)
49220#@gui : sep = separator()
49221#@gui : note = note("<small><b>Lissajous parameters:</b></small>")
49222#@gui : Curve Length = float(15,0,50)
49223#@gui : Curve Angle = float(0,0,360)
49224#@gui : Minimal Radius = float(0,-5,5)
49225#@gui : Maximal Radius = float(0.5,-5,5)
49226#@gui : X-Dispersion = float(1,0,4)
49227#@gui : Y-Dispersion = float(1,0,4)
49228#@gui : X-Factor = int(1,0,16)
49229#@gui : Y-Factor = int(1,0,16)
49230#@gui : sep = separator()
49231#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/22/08</i>.</small>")
49232fx_circle_art :
49233  if !$2 f 0 return fi
49234  # Generate object coordinates.
49235  if $1==0 # Random.
49236    {round(2*($2^1.5))}
49237    rand. -1,1 +rand. -1,1 +rand. -$3,$3 a[-3--1] y
49238  else # Spiral.
49239    {max(1,round($2*$7))}
49240    t0={$8*2*pi/180}
49241    rows. 0,2
49242    f. "r = x/(w-1);
49243        t = 2*pi*x/$2;
49244        if(y==0,(r^$11)*cos("$t0"+$13*t),
49245        if(y==1,(r^$12)*sin("$t0"+$14*t),
49246                 max(0,$3*($9+($10-$9)*r))))"
49247  fi
49248
49249  # Convert to 3D object.
49250  l.
49251    transpose s x,-1 h={h}
49252    i[0] ({'CImg3d'},{2*$h},$h) # Header.
49253    ++... . -[-4,-2] i .. i[-3,-1] 1,100% a[-6--1] x # Vertices.
49254    1,$h,1,1,5 1,$h,1,1,2*y ++. 1 a[-3--1] x z. 0,5 # Primitives.
49255    3,$h,1,1,1 1,$h,1,1,-1 y a y # Colors + Opacities.
49256  endl
49257
49258  # Render object on selected images.
49259  repeat $!-1 l[$>,-1]
49260    s={0,max(w,h)} rm[0]
49261    if $5 {2*$s},{2*$s} +*3d[0] $s # Anti-aliasing.
49262    else $s,$s +*3d[0] {$s/2} # No anti-aliasing.
49263    fi
49264    j3d[1] [2],50%,50%,0,1,2,0,0 rm[2]
49265    %. $4
49266    if $6 i.. 100%,100%,1,3 rand.. 0,255 plasma.. 1,1 equalize.. 256 n.. 0,255 blend[-2,-1] shapeaverage fi
49267    rv
49268  endl done
49269  rm.
49270  n 0,255
49271  if $5 r 50%,50%,1,100%,2 fi
49272
49273#@gui Equation Plot [Parametric] : fx_equation_parametric, fx_equation_parametric
49274#@gui : X(t) = text{"sin(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)"}
49275#@gui : Y(t) = text{"cos(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)"}
49276#@gui : Min-t = float(0,-1000,1000)
49277#@gui : Max-t = float(100,-1000,1000)
49278#@gui : Resolution = int(4096,2,32768)
49279#@gui : Outline Opacity = float(1,0,1)
49280#@gui : Dot Size = int(0,0,16)
49281#@gui : Start Color = color(64,0,0)
49282#@gui : End Color = color(128,0,0)
49283#@gui : Colored Outline = bool(1)
49284#@gui : Antialiasing = bool(1)
49285#@gui : Decoration = bool(1)
49286#@gui : sep = separator()
49287#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/13/11</i>.</small>")
49288fx_equation_parametric :
49289  repeat $! l[$>]
49290    w={w} h={h} rm
49291    $5,1,1,2,"t=$3+x*($4-$3)/($5-1);if(c==0,$1,$2)"
49292    channels. 0,2
49293    ($8,$11^$9,$12^$10,$13) r. {-2,w},1,1,3,3 a c
49294    display_parametric $w,$h,{$6+$14*1.001},$7,$15,$16
49295  endl done
49296
49297#@gui Equation Plot [Y=f(X)] : fx_equation_plot, fx_equation_plot
49298#@gui : F(X) = text{"X*c+10*cos(X+c+u)"}
49299#@gui : X-Min = float(-10,-100,100)
49300#@gui : X-Max = float(10,-100,100)
49301#@gui : Resolution = int(100,2,1024)
49302#@gui : Channels = int(3,1,32)
49303#@gui : Plot Type = choice(2,"None","Lines","Splines","Bars")
49304#@gui : Vertex Type = choice(0,"None","Points","Crosses 1","Crosses 2","Circles 1","Circles 2","Square 1","Square 2")
49305#@gui : sep = separator()
49306#@gui : note = note("<small><b>Note</b> :
49307#@gui : Use variable <b>X</b> instead of <b>x</b> in the above equation to take care of the X-min/max settings.
49308#@gui : Variable <b>c</b> refers to the current channel number.
49309#@gui : Variable <b>u</b> refers to a uniformly distributed random value in [0,1].
49310#@gui : Reduce resolution to be able to view
49311#@gui : separate graph vertices.</small>")
49312#@gui : sep = separator()
49313#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49314fx_equation_plot :
49315  repeat $! l[$>]
49316    w={w} h={h} rm
49317    $4,1,1,$5,"X=$2+($3-$2)*x/($4-1);$1"
49318    dg $w,$h,$6,$7,$2,$3
49319  endl done
49320
49321#@gui Gradient [Corners] : fx_corner_gradient, fx_corner_gradient
49322#@gui : Color 1 (Up/Left Corner) = color(255,255,255,128)
49323#@gui : Color 2 (Up/Right Corner) = color(255,0,0,255)
49324#@gui : Color 3 (Bottom/Left Corner) = color(0,255,0,255)
49325#@gui : Color 4 (Bottom/Right Corner) = color(0,0,255,255)
49326#@gui : sep = separator()
49327#@gui : Colorspace = choice(1,"sRGB","Linear RGB","Lab")
49328#@gui : sep = separator()
49329#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49330fx_corner_gradient : skip ${17=0}
49331  repeat $! l[$>]
49332    wh={w},{h} rm
49333    ($1,$5;$9,$13^$2,$6;$10,$14^$3,$7;$11,$15^$4,$8;$12,$16)
49334    _gb_fwd $17
49335    r. $wh,1,100%,3
49336    _gb_bwd $17
49337  endl done
49338
49339#@gui Gradient [Custom Shape] : fx_custom_gradient, fx_custom_gradient_preview(1)
49340#@gui : note = note("<small><b>Shape selection:</b></small>")
49341#@gui : Select By = choice("Auto","Dark Pixels","Bright Pixels","Opaque Pixels")
49342#@gui : Smoothness = float(0,0,10)
49343#@gui : Threshold = float(0,0,100)
49344#@gui : Preview Shape = bool(1)
49345#@gui : note = note("<small><b>Note:</b> Shapes with small strokes may lead to incorrect previews.</small>")
49346#@gui : sep = separator()
49347#@gui : note = note("<small><b>Gradient parameters:</b></small>")
49348#@gui : Number of Colors = int(4,2,10)
49349#@gui : Cycles = float(1,1,16)
49350#@gui : Offset = float(0,0,100)
49351#@gui : Shading = float(128,1,256)
49352#@gui : Inner Length = float(100,0,100)
49353#@gui : Outer Length = float(100,0,100)
49354#@gui : Spatial Metric = choice(2,"Chebyshev","Manhattan","Euclidean")
49355#@gui : Color Metric = choice("RGB","HSV","Lab")
49356#@gui : Shade Back to First Color = bool(1)
49357#@gui : Preview Gradient = bool(0)
49358#@gui : Save Gradient As = _text("")
49359#@gui : sep = separator()
49360#@gui : note = note("<small><b>Color definitions:</b></small>")
49361#@gui : Colormap Type = choice(1,"Pre-Defined","User-Defined")
49362#@gui : Pre-Defined Colormap = int(0,0,65535)
49363#@gui : 1st Color = color(0,0,0,255)
49364#@gui : 2nd Color = color(255,0,0,255)
49365#@gui : 3rd Color = color(255,255,0,255)
49366#@gui : 4th Color = color(255,255,255,255)
49367#@gui : 5th Color = color(0,255,255,255)
49368#@gui : 6th Color = color(0,255,0,255)
49369#@gui : 7th Color = color(0,0,255,255)
49370#@gui : 8th Color = color(128,128,128,255)
49371#@gui : 9th Color = color(255,0,255,255)
49372#@gui : 10th Color = color(0,0,0,0)
49373#@gui : sep = separator()
49374#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2013/03/10</i>.</small>")
49375fx_custom_gradient_preview : skip "${15=}"
49376  repeat $! l[$>]
49377    if $4
49378      +_fx_custom_gradient1 ${1-14},"$15",${16--1} +erode. 3 -[-2,-1] +dilate. 5 a[-2,-1] c n. 0,255
49379      fx_custom_gradient[0] ${1-14},"$15",${16--1},-1 blend alpha
49380    else fx_custom_gradient ${1-14},"$15",${16--1},-1
49381    fi
49382    if $14
49383      +_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.
49384    fi
49385  endl done
49386
49387fx_custom_gradient : skip "${15=}"
49388  _fx_custom_gradient0 ${1-14},"$15",${16--1}
49389  if $-1>=0" && "narg("$15")
49390    dir_ggr=${-path_gimp}gradients
49391    0 nm. ${"normalize_filename \"$15\""} name_ggr={b} rm. output_ggr. $dir_ggr/$name_ggr.ggr,"$15"
49392  fi
49393  i.. (0^0^0^0) a[-2,-1] x
49394  repeat $!-1 l[$>,-1]
49395    _fx_custom_gradient1[0] ${1-14},"$15",${16--1}
49396    +distance[0] 1,$11 +distance[0] 0,$11 *. -1 +[0] 1 +[0,-2,-1]  # Signed distance function.
49397    m={$9%*{0,im}} M={$10%*{0,iM}}
49398    -[0] $m *[0] {1,(w-2)/($M-$m)} +[0] 1
49399    round[0] map[0] .
49400  endl done
49401  rm.
49402
49403# Create colormap.
49404_fx_custom_gradient0 :
49405  if $16 4,8,1,1,${18-56} permute. yzcx
49406  else 8,1,1,3 srand $17 rand. 0,255 to_rgba.
49407  fi
49408  z. 0,{$5-1}
49409  if $13
49410    r. {200*$6}%,1,1,4,0,2
49411    __fx_custom_gradient0. $12,$8
49412    shift. {-round(w*0.5*$7%)},0,0,0,2 z. 0,{w/2-1}
49413  else
49414    __fx_custom_gradient0. $12,$8
49415    r. {100*$6}%,1,1,4,0,2 shift. {-round(w*$7%)},0,0,0,2
49416    fi
49417
49418__fx_custom_gradient0 :
49419  if $1==1 sh. 0,2 rgb2hsv. rm.
49420  elif $1==2 sh. 0,2 srgb2rgb. rgb2lab. rm.
49421  fi
49422  r. {$2*w},1,1,4,3
49423  if $1==1 sh. 0,2 hsv2rgb. rm.
49424  elif $1==2 sh. 0,2 lab2rgb. rgb2srgb. rm.
49425  fi
49426
49427# Extract shape from image.
49428_fx_custom_gradient1 :
49429  b $2%
49430  if $1==0 # Auto-mode.
49431    to_a split_opacity
49432    if iM>im+32
49433      rm.. >=[0] {100-$3}%
49434    else
49435      rm. norm n 0,1
49436      if ia>0.5 <=[0] $3% else >=[0] {100-$3}% fi
49437    fi
49438  elif $1==1 # Dark pixels.
49439    remove_opacity norm <= $3%
49440  elif $1==2 # Bright pixels.
49441    remove_opacity norm >= {100-$3}%
49442  else # Opaque pixels.
49443    to_a channels 100% >= {100-$3}%
49444  fi
49445
49446#@gui Gradient [from Line] : fx_line_gradient, fx_line_gradient_preview(1)
49447#@gui : Starting Point (%) = point(0,0,0,1,255,0,0)
49448#@gui : Ending Point (%) = point(100,100,0,1,64,128,255)
49449#@gui : Sampling = float(100,0,100)
49450#@gui : Length = int(0,0,4096)
49451#@gui : note = note("<small><b>Note:</b> Set length to <i>0</i> to release gradient length constraints.</small>")
49452#@gui : Sort Colors = choice("Don't Sort","By Red Component","By Green Component","By Blue Component",
49453#@gui : "By Luminance","By Blue Chrominance","By Red Chrominance","By Lightness")
49454#@gui : Reverse Gradient = bool(0)
49455#@gui : sep = separator()
49456#@gui : Preview Gradient = bool(1)
49457#@gui : Save Gradient As = _text("")
49458#@gui : sep = separator()
49459#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/29/06</i>.</small>")
49460fx_line_gradient : skip "${10=}"
49461  _fx_line_gradient $*
49462  if narg("$10")
49463    dir_ggr=${-path_gimp}gradients
49464    0 nm. ${"normalize_filename \"$10\""} name_ggr={b} rm. output_ggr. $dir_ggr/$name_ggr.ggr,"$10"
49465  fi
49466  repeat $! r[$>] 100%,64,1,100% done
49467
49468fx_line_gradient_preview :
49469  repeat $! l[$>]
49470    to_rgba
49471    if $9 +_fx_line_gradient $* fi
49472    l[0]
49473      line $1%,$2%,$3%,$4%,1,0xF0F0F0F0,255,255,255,255
49474      line $1%,$2%,$3%,$4%,1,0x0F0F0F0F,0,0,0,255
49475    endl
49476    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
49477  endl done
49478
49479_fx_line_gradient :
49480  at_line $1%,$2%,0,$3%,$4%,0 r {max(0.1,$5)}%,1,1,100%,1
49481  m "feature1 : channels 0"
49482  m "feature2 : channels 1"
49483  m "feature3 : channels 2"
49484  m "feature4 : to_rgb luminance"
49485  m "feature5 : to_rgb rgb2ycbcr channels 1"
49486  m "feature6 : to_rgb rgb2ycbcr channels 2"
49487  m "feature7 : to_rgb srgb2rgb rgb2lab channels 0"
49488  if $7 repeat $! l[$>] +feature$7 rv a y sort +,x rows 1 endl done fi
49489  if $6 r $6,1,1,100%,3 fi
49490  if $8 mirror x fi
49491
49492#@gui Gradient [Linear] : fx_linear_gradient, fx_linear_gradient
49493#@gui : Starting Color = color(0,0,0,255)
49494#@gui : Ending Color = color(255,255,255,255)
49495#@gui : Swap Colors = bool(0)
49496#@gui : Angle = float(45,0,360)
49497#@gui : Fade Start = float(0,0,100)
49498#@gui : Fade End = float(100,0,100)
49499#@gui : sep = separator()
49500#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
49501#@gui : sep = separator()
49502#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
49503fx_linear_gradient : skip ${13=0}
49504  repeat $! l[$>]
49505    wh={w},{h} rm
49506    ($1^$2^$3^$4) ($5^$6^$7^$8)
49507    if $9 rv[-2,-1] fi
49508    r $wh
49509    _gb_fwd $13
49510    fade_linear $10,$11,$12
49511    _gb_bwd $13
49512  endl done
49513
49514#@gui Gradient [Radial] : fx_radial_gradient, fx_radial_gradient
49515#@gui : Starting Color = color(0,0,0,255)
49516#@gui : Ending Color = color(255,255,255,255)
49517#@gui : Swap Colors = bool(0)
49518#@gui : Fade Start = float(0,0,100)
49519#@gui : Fade End = float(100,0,100)
49520#@gui : Center (%) = point(50,50,0,1,255)
49521#@gui : sep = separator()
49522#@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab")
49523#@gui : sep = separator()
49524#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/29/06</i>.</small>")
49525fx_radial_gradient : skip ${14=0}
49526  repeat $! l[$>]
49527    wh={w},{h} rm
49528    ($1^$2^$3^$4) ($5^$6^$7^$8)
49529    if $9 rv[-2,-1] fi
49530    r $wh
49531    _gb_fwd $14
49532    100%,100% =. 1,$12%,$13% distance. 1 _fade $10,$11
49533    _gb_bwd $14
49534  endl done
49535
49536#@gui Gradient [Random] : fx_random_gradient, fx_random_gradient
49537#@gui : Density = int(32,1,1024)
49538#@gui : Seed = int(0,0,65535)
49539#@gui : Smoothness = float(0,0,10)
49540#@gui : Color Balance = color(128,128,128)
49541#@gui : Opacity = float(1,0,1)
49542#@gui : sep = separator()
49543#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/08/04</i>.</small>")
49544fx_random_gradient :
49545  repeat $! l[$>]
49546    to_rgba 100%,100% srand $2
49547    eval "repeat ($1,n,
49548            x = round(u(w-1));
49549            y = round(u(h-1));
49550            i(x,y) = 1;
49551            i(#0,x,y,0,0) = round(u(255));
49552            i(#0,x,y,0,1) = round(u(255));
49553            i(#0,x,y,0,2) = round(u(255));
49554            i(#0,x,y,0,3) = $7*255 + (1-$7)*round(u(255));
49555          )"
49556    if $7!=1 sh.. 100% n. 0,255 rm. fi
49557    ==. 0
49558    sh.. 0,2 srgb2rgb. rm.
49559    inpaint_pde.. [1],100%,1 rm.
49560    b $3% n 0,255
49561    sh 0,2 rgb2srgb. balance_gamma. ${4-6} rm.
49562  endl done
49563
49564#@gui Hypotrochoid : fx_hypotrochoid, fx_hypotrochoid(1)
49565#@gui : Periods = int(37,1,100)
49566#@gui : Outer Radius (%) = float(100,0,300)
49567#@gui : Inner Radius (%) = float(74,0,300)
49568#@gui : Distance to center (%) = float(80,0,300)
49569#@gui : Thickness (%) = float(0.5,0,5)
49570#@gui : Color = color(255,255,255,255)
49571#@gui : Anti-aliasing = bool(1)
49572#@gui : sep = separator()
49573#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/01/25</i>.</small>")
49574fx_hypotrochoid :
49575  {0,[w,h]*($10?1.5:1)}
49576  eval "
49577    const M = min(w,h)/2;
49578    const A = M*$2%;
49579    const B = A*$3%;
49580    const H = B*$4%;
49581    const S = M*$5%;
49582    const F = (A - B)/max(1e-5,B);
49583    const AmB = A - B;
49584    hypotrochoid(t) = (_t = t; round([ w/2 + AmB*cos(_t) + H*cos(F*_t), h/2 + AmB*sin(_t) - H*sin(F*_t) ]));
49585
49586    oX = hypotrochoid(t);
49587    dt = 1;
49588    for (t = 0, t<$1*2*pi,
49589      do (
49590        X = hypotrochoid(t + dt);
49591        dist = abs(X[0] - oX[0]) | abs(X[1] - oX[1]);
49592        !dist?(dt*=2):
49593        dist>1?(dt/=1.25):
49594        (t+=dt),
49595        dist!=1;
49596      );
49597      S<1?(I(X) = $9):ellipse(X,S,S,0,1,$9);
49598      oX = X;
49599    )"
49600  r. [0],[0],1,1,2
49601  channels. -3,0 sh. 0,2 fc. ${6-8} rm.
49602  blend[0,-1] alpha
49603
49604#@gui Lightning : fx_lightning, fx_lightning_preview
49605#@gui : note = note{"<small><b>Global parameters:</b></small>"}
49606#@gui : Number of Streaks = int(20,1,1024)
49607#@gui : Size (%) = float(90,0,150)
49608#@gui : Resolution = int(256,2,4096)
49609#@gui : Randomness = float(3,0,16)
49610#@gui : Smoothness = float(1.5,0,10)
49611#@gui : Balance = float(0.75,0,1)
49612#@gui : Color = color(255,255,255,255)
49613#@gui : Seed = int(0,0,65535)
49614#@gui : sep = separator()
49615#@gui : note = note{"<small><b>Initial streak:</b></small>"}
49616#@gui : XY-Coordinates (%) = point(50,5,0,1)
49617#@gui : Angle (deg) = float(0,-180,180)
49618#@gui : Thickness (px) = int(6,1,64)
49619#@gui : Blur = float(0.2,0,3)
49620#@gui : sep = separator()
49621#@gui : note = note{"<small><b>Auxiliary streaks:</b></small>"}
49622#@gui : Min Offset (%) = float(25,0,100)
49623#@gui : Max Offset (%) = float(60,0,100)
49624#@gui : Min Length (%) = float(95,0,200)
49625#@gui : Max Length (%) = float(100,0,200)
49626#@gui : Min Angle Deviation (deg) = float(30,0,180)
49627#@gui : Max Angle Deviation (deg) = float(40,0,180)
49628#@gui : Thickness Factor = float(-0.25,-1,1)
49629#@gui : Blur Factor = float(-0.1,-1,1)
49630#@gui : Opacity Factor = float(-0.20,-1,1)
49631#@gui : sep = separator()
49632#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/27/11</i>.</small>")
49633fx_lightning :
49634  repeat $! l[$<]
49635    100%,100% l. fact={max(w,h)/$3*$2%} srand $11
49636    repeat $1
49637      if $!<=1
49638        i=0
49639        new_level=1
49640        new_length=$3
49641        new_x=0
49642        new_y=0
49643        new_angle=$14
49644      else
49645        i={round(u(1,max(1,($!-1)*$6)))}
49646        level={$i,@-2}
49647        angle={$i,@-1}
49648        nb_points={$i,i[6]}
49649        p={round(($nb_points-2)*u($17%,$18%))}
49650        new_level={$level+1}
49651        new_length={max(2,round(($nb_points-$p)*u($19%,$20%)))}
49652        new_x={$i,i[8+3*$p]}
49653        new_y={$i,i[9+3*$p]}
49654        new_angle={$angle+u($21,$22)*if(u>0.5,1,-1)}
49655      fi
49656
49657      _fx_lightning $new_length,$4,$5
49658      r3d. 0,0,1,$new_angle
49659      +3d. $new_x,$new_y
49660      +*3d. $fact [0],[0] j3d. ..,$12%,$13%,0,1,1,0,0 rm..
49661
49662      dilation={$15*(if($23>0,1.5,10)^($23*($new_level-1)))}
49663      blur={max(0,-1+(1+$16)*(if($24>0,2,5)^($24*($new_level-1))))}
49664      opacity={min(1,$10/255*(2^($25*($new_level-1))))}
49665
49666      dilate. $dilation b. $blur% n. 0,1 *. $opacity max[0,-1]
49667      ($new_level;$new_angle) a[-2,-1] y
49668      progress {($>*100)/($1-1)}
49669    done
49670    k[0] * 255 i[0] 100%,100%,1,3 fc[0] ${7-9} a c
49671    endl
49672    rv
49673  endl done
49674
49675fx_lightning_preview :
49676  repeat $! l[$>]
49677    fx_lightning $* rv blend alpha
49678  endl done
49679
49680_fx_lightning :
49681  l[]
49682    ({'CImg3d'},$1,{$1-1})
49683    1,$1 noise. $2,1 cumulate. b. $3 shift. 0,1 1,100%,1,1,y 1,100% a[-3--1] x
49684    1,{h-1},1,1,2 +f. y ++. 1 a[-3--1] x
49685    4,100%,1,1,1
49686    y a y
49687  endl
49688
49689#@gui Lissajous : fx_lissajous, fx_lissajous(1)
49690#@gui : Resolution = int(4096,2,8192)
49691#@gui : sep = separator()
49692#@gui : X-Size = float(0.9,0,2)
49693#@gui : Y-Size = float(0.9,0,2)
49694#@gui : Z-Size = float(3,1,10)
49695#@gui : sep = separator()
49696#@gui : X-Multiplier = float(8,0,32)
49697#@gui : Y-Multiplier = float(7,0,32)
49698#@gui : Z-Multiplier = float(0,0,32)
49699#@gui : sep = separator()
49700#@gui : X-Offset = float(0,0,1)
49701#@gui : Y-Offset = float(0,0,1)
49702#@gui : Z-Offset = float(0,0,1)
49703#@gui : sep = separator()
49704#@gui : X-Angle = float(0,0,360)
49705#@gui : Y-Angle = float(0,0,360)
49706#@gui : Z-Angle = float(0,0,360)
49707#@gui : sep = separator()
49708#@gui : Thickness = float(0,0,50)
49709#@gui : Color = color(255,255,255,255)
49710#@gui : sep = separator()
49711#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/04</i>.</small>")
49712fx_lissajous :
49713  repeat $! l[$>] to_rgba
49714    {w},{h}
49715    f3d {0.5*max(w,h)/tan($4*pi/360)}
49716    lissajous3d $1,$5,$8,$6,$9,$7,$10
49717    r3d. 0,0,1,$13 r3d. 0,1,0,$12 r3d. 1,0,0,$11
49718    *3d. {0.5*$2*{-2,w}},{0.5*$3*{-2,h}},{0.5*$4*max({-2,w},{-2,h})}
49719    col3d. 1 j3d.. .,50%,50%,0,1,1,0,0 rm.
49720    distance. 1 >. $14% *.. . ==. 0
49721    r. 100%,100%,1,4
49722    sh. 0 *. $15 rm.
49723    sh. 1 *. $16 rm.
49724    sh. 2 *. $17 rm.
49725    sh. 3 *. $18 rm.
49726    +[-2,-1]
49727  endl done
49728
49729#@gui Mandelbrot - Julia Sets : fx_mandelbrot, fx_mandelbrot_preview
49730#@gui : X0 = value(-2)
49731#@gui : Y0 = value(-2)
49732#@gui : X1 = value(2)
49733#@gui : Y1 = value(2)
49734#@gui : note = note{"<small><b>Fractal Type:</b></small>"}
49735#@gui : Fractal Set = choice("Mandelbrot","Julia")
49736#@gui : Iterations = int(1024,16,65535)
49737#@gui : X-Seed (Julia) = float(0.317,-2,2)
49738#@gui : Y-Seed (Julia) = float(0.03,-2,2)
49739#@gui : sep = separator()
49740#@gui : note = note{"<small><b>Colormap:</b></small>"}
49741#@gui : Number of Colors = int(16,2,2048)
49742#@gui : Smoothness = int(8,1,256)
49743#@gui : Seed = int(255,0,65536)
49744#@gui : sep = separator()
49745#@gui : note = note{"<small><b>Navigation:</b></small>"}
49746#@gui : Zoom Center = point(50,50,0,0,255,255,255,200)
49747#@gui : Zoom Factor = float(0.25,0,1)
49748#@gui : Zoom In = button()
49749#@gui : Center = button()
49750#@gui : Zoom Out = button()
49751#@gui : Display Coordinates = bool(0)
49752#@gui : sep = separator()
49753#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/06/27</i>.</small>")
49754fx_mandelbrot :
49755  if !narg($_size) _size={max(w,h)} fi
49756  rm $_size,$_size
49757  mandelbrot ${1-4},$6,{$5?[1,$7,$8]:[0,0,0]}
49758  srand $11 $9,1,1,3 rand. 0,255 r. {$9*$10},1,1,3,3 point. 0 map.. .,3 rm.
49759
49760fx_mandelbrot_preview :
49761  _size={min(${-gui_preview_wh})}
49762  if "$15 || $16 || $17"
49763    x0,y0,x1,y1={"P0 = [${1,2}];
49764                  dP = [${3,4}] - P0;
49765                  C = P0 + [${12,13}]%*dP;
49766                  zfact = $14*($15?1:$16?0:-2);
49767                  dC = 0.5*dP*(1 - 0.98*zfact);
49768                  [C - dC,C + dC]"}
49769    status=\{$x0\}\{$y0\}\{$x1\}\{$y1\}\{$5\}\{$6\}\{$7\}\{$8\}\{$9\}\{$10\}\{$11\}\
49770           \{50,50\}\{$14\}\{0\}\{0\}\{0\}\{$18\}
49771    px,py=50
49772  else
49773    x0,y0,x1,y1=${1-4}
49774    status=
49775    px,py=${12,13}
49776  fi
49777  fx_mandelbrot $x0,$y0,$x1,$y1,${5--1}
49778
49779  x0r,y0r,x1r,y1r={"C = ["$px,$py"]%*w; dC = 0.5*w*(1 - 0.98*$14); round([C - dC, C + dC - 1])"}
49780  rectangle $x0r,$y0r,$x1r,$y1r,0.7,0xF0F0F0F0,255,255,255,255
49781  rectangle $x0r,$y0r,$x1r,$y1r,0.7,0x0F0F0F0F,0,0,0,255
49782  if $18 to "Z0 = ( "{_$x0}" , "{_$y0}" )\nZ1 = ( "{_$x1}" , "{_$y1}" )",2,2,16 fi
49783  u $status
49784
49785#@gui Neon Lightning : fx_neon_lightning, fx_neon_lightning(1)
49786#@gui : Source (%) = point(50,50)
49787#@gui : R0 = float(0,0,100)
49788#@gui : Destination (%) = point(50,50)
49789#@gui : R1 = float(100,0,100)
49790#@gui : sep = separator()
49791#@gui : Density = int(50,1,512)
49792#@gui : Glow = float(0.7,0,5)
49793#@gui : Thickness = float(3,0,20)
49794#@gui : sep = separator()
49795#@gui : Color = color(130,80,50)
49796#@gui : Color Dispersion = float(0.25,0,1)
49797#@gui : Transparency = float(0,0,1)
49798#@gui : sep = separator()
49799#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/30/06</i>.</small>")
49800fx_neon_lightning :
49801  d={$13*255}
49802  repeat $! l[$>]
49803    100%,100%,1,4 rm[0]
49804    repeat $7
49805      x0={max(0,min(w,$1+u(-$3,$3)))} y0={max(0,min(h,$2+u(-$3,$3)))}
49806      x1={max(0,min(w,$4+u(-$6,$6)))} y1={max(0,min(h,$5+u(-$6,$6)))}
49807      u0={u(0,100)} v0={u(0,100)} u1={u(0,100)} v1={u(0,100)}
49808      R={max(0,min(255,u($10-$d,$10+$d)))}
49809      G={max(0,min(255,u($11-$d,$11+$d)))}
49810      B={max(0,min(255,u($12-$d,$12+$d)))}
49811      spline $x0%,$y0%,$u0%,$v0%,$x1%,$y1%,$u1%,$v1%,1,$R,$G,$B,1
49812    done
49813    s c,-3
49814    b[0] 3%
49815    distance. 1 *. -1 c. -{$9+1e-5},0 n. 0,1 sqrt.
49816    +b. $8%,1 n. 0,1 sqrt. n[-2,-1] 0,255 max[-2,-1]
49817    . blend[0,1] value
49818    smooth 5,0,1,0.5,2,10,0
49819    /. 255 ^. $14 *. 255
49820    a c c 0,255
49821  endl done
49822
49823#@gui Newton Fractal : fx_newton_fractal, fx_newton_fractal_preview
49824#@gui : X0 = value(-2)
49825#@gui : Y0 = value(-2)
49826#@gui : X1 = value(2)
49827#@gui : Y1 = value(2)
49828#@gui : note = note{"<span color="#EE5500"><b>Fractal Type:</b></span>"}
49829#@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")
49830#@gui : p(z) = text{"rot(35°)*z^^3 - z^^2 + 1"}_1
49831#@gui : p'(z) = text{"3*z^^2 - 2*z"}_1
49832#@gui : p''(z) = text{"6*z - 2"}_1
49833#@gui : Descent method = choice(1,"Secant","Newton","Householder")
49834#@gui : Max iterations = int(200,16,1024)
49835#@gui : Precision = float(2,0,12)
49836#@gui : sep = separator()
49837#@gui : note = note{"<span color="#EE5500"><b>Rendering:</b></span>"}
49838#@gui : Coloring = choice(1,"By Custom Expression","By Iteration","By Value")
49839#
49840# Color by iteration
49841#
49842#@gui : Number of Colors = int(16,2,2048)
49843#@gui : Smoothness = int(8,1,256)
49844#@gui : Seed = int(255,0,65536)
49845#
49846# Color by value
49847#
49848#@gui : Colorspace = choice(2,"HSI","HSL","HSV")_0
49849#@gui : Hue min (%) = float(100,0,500)_0
49850#@gui : Hue max (%) = float(150,0,500)_0
49851#@gui : Lightness min (%) = float(20,0,500)_0
49852#@gui : Lightness max (%) = float(400,0,500)_0
49853#
49854# Custom coloring
49855#
49856#@gui : Colorspace = choice(3,"RGB,"HSI","HSL","HSV","Lab")_0
49857#@gui : Pre-Process = choice(2,"None","Equalize","Normalize","Equalize and Normalize")_0+
49858#@gui : note = note{"<small><span color="#EE5500"><b>Tips for Custom expressions:</b></span>\n
49859#@gui : - Variables <b>i0,i1</b> stand for the real and imaginary parts of the iterated complex number.\n
49860#@gui : - Variable <b>i2</b> is the number of iterations required for convergence.\n
49861#@gui : - Variable <b>z</b> is the complex number with value <b>[ i0,i1 ]</b>.\n
49862#@gui : - Functions <b>p(z), dp(z)</b> and <b>d2p(z)</b> are the expressions used for computing the fractal.
49863#@gui : </small>"}
49864#@gui : Channel #1 = text{"carg(-z)"}_0
49865#@gui : Channel #2 = text{"(i0 + i1)/2"}_0
49866#@gui : Channel #3 = text{"10*(i2^0.4)"}_0
49867#@gui : Post-Process = choice(0,"None","Equalize","Normalize","Equalize and Normalize")_0
49868#
49869# Basic color adujstment
49870#
49871#@gui : Brightness (%) = float(0,-100,100)
49872#@gui : Contrast (%) = float(0,-100,100)
49873#@gui : Gamma (%) = float(0,-100,100)
49874#@gui : Hue (%) = float(0,-100,100)
49875#@gui : Saturation (%) = float(0,-100,100)
49876#@gui : Equalization (%) = float(0,0,100)
49877#@gui : Anti-aliasing = choice(2,"x1","x1.5","x2","x2.5","x3","x3.5","4")
49878#@gui : note = note{"<small><b>Note:</b> Anti-aliasing is applied on final rendering only, not on preview.</small>"}
49879#@gui : antialias_note = value(0)_2-
49880#@gui : sep = separator()
49881#
49882# Navigation
49883#
49884#@gui : note = note{"<span color="#EE5500"><b>Navigation:</b></span>"}
49885#@gui : Zoom Center = point(50,50,0,0,255,255,255,200)
49886#@gui : Zoom Factor = float(0.5,0,1)
49887#@gui : Angle = float(0,-180,180)
49888#@gui : Zoom In = button()
49889#@gui : Center = button()
49890#@gui : Zoom Out = button()
49891#@gui : Reset View = button()
49892#@gui : Display Coordinates on Preview Window = bool(1)
49893#@gui : Preview subsampling = choice(2,"None","x1.5","x2","x2.5","x3","x3.5","x4")
49894#@gui : sep = separator()
49895#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/01/09</i>.</small>")
49896fx_newton_fractal : skip "${6=},${7=},${8=},${22=},${25=},${28=}"
49897  repeat $! l[$>]
49898    if !narg($_size) _size={max(w,h)} fi
49899    rm
49900    antialias={arg(1+$33,1,1.5,2,2.5,3,3.5,4)}
49901    {$antialias*[$_size,$_size]}
49902    if $5==1
49903      pz="z^^2 - 1" dpz="2*zn" d2pz="2"
49904    elif $5==2
49905      pz="z^^3 - 1" dpz="3*z^^2" d2pz="6*z"
49906    elif $5==3
49907      pz="z^^5 - 1" dpz="5*z^^4" d2pz="20*z^^3"
49908    elif $5==4
49909      pz="z^^6 + z^^3 - 1" dpz="6*z^^5 + 3*z^^2" d2pz="30*z^^4 + 6*z"
49910    elif $5==5
49911      pz="z^^8 + 15*z^^4 - 1" dpz="8*z^^7 + 60*z^^3" d2pz="56*z^^6 + 180*z^^2";
49912    else
49913      pz="$6" dpz="$7" d2pz="$8"
49914    fi
49915    if !narg($pz) pz="[1,0]" fi
49916    if !narg($dpz) dpz="[1,0]" fi
49917    if !narg($d2pz) d2pz="[1,0]" fi
49918
49919    newton_fractal ${1-4},$38,$9,$10,{10^-$11},$pz,$dpz,$d2pz
49920
49921    if $12==1 # Color by iteration
49922      channels 100%
49923      srand $15 $13,1,1,3 rand. 0,255 r. {$13*$14},1,1,3,3 point. 0 map.. .,3 rm.
49924
49925    elif $12==2 # Color by value
49926      f "[ atan2(i1,i0),1,i2 ]" s c n... {[$17,$18]*360%} n. {[$19,$20]%} c. 0,1 a c
49927      ${"arg 1+$16,hsi,hsl,hsv"}2rgb
49928
49929    else # Custom coloring
49930
49931      if $22 # Pre-process values
49932        s c,-2
49933        if $22&1 equalize 1024 fi
49934        if $22&2 /[-2] {-2,max(1e-5,abs(im),abs(iM))} n. 0,1 fi
49935        a c
49936      fi
49937
49938      f "*begin(
49939            p(z) = ("$pz");
49940            dp(z) = ("$dpz");
49941            d2p(z) = ("$d2pz");
49942          );
49943          z = [ i0,i1 ];
49944          [ (0;$23),(0;$24),(0;$25) ]"
49945
49946      if $26 # Post-process values
49947        s c
49948        if $26&1 equalize 1024 fi
49949        if $26&2 normalize 0,1 fi
49950        a c
49951      fi
49952
49953      * 255 mod 256
49954      if $21 ${"arg $21,hsi8,hsl8,hsv8,lab8"}2rgb fi # Convert to RGB colors
49955    fi
49956
49957    r2dx $_size
49958
49959    if $32 ac "+equalize 1024 j.. .,0,0,0,0,{$32%} rm.",ycbcr_y fi
49960    adjust_colors ${27-31},0,0,0,255
49961  endl done
49962
49963fx_newton_fractal_preview : skip "${6=},${7=},${8=},${22=},${25=},${28=}"
49964  is_custom_expression={$5==0?2:1}
49965  is_color_by_custom={$12==0?2:0}
49966  is_color_by_iter={$12==1?2:0}
49967  is_color_by_value={$12==2?2:0}
49968  _size0={min(${-gui_preview_wh})}
49969  _size={$_size0/arg(1+$44,1,1.5,2,2.5,3,3.5,4)}
49970
49971  angle=$38
49972  if "$39 || $40 || $41"
49973    x0,y0,x1,y1={"P0 = [${1,2}];
49974                  dP = [${3,4}] - P0;
49975                  M = P0 + 0.5*dP;
49976                  C = P0 + [${35,36}]%*dP;
49977                  C = M + rot(-$38°)*(C - M);
49978                  zfact = $37*($39?1:$40?0:-2);
49979                  dC = 0.5*dP*(1 - 0.98*zfact);
49980                  [ C - dC, C + dC ]"}
49981    px,py=50
49982  elif $42
49983    x0,y0,x1,y1=-2,-2,2,2
49984    px,py=50
49985    angle=0
49986  else
49987    x0,y0,x1,y1=${1-4}
49988    px,py=${35,36}
49989  fi
49990  fx_newton_fractal $x0,$y0,$x1,$y1,$5,"$6","$7","$8",${9-22},"$23","$24","$25",${26-32},0,${34-37},$angle,${39--1}
49991
49992  repeat $! l[$>]
49993    r2dx $_size0,1
49994    x0r,y0r,x1r,y1r={"C = [ "$px,$py" ]%*w; dC = 0.5*w*(1 - 0.98*$37); round([ C - dC, C + dC - 1 ])"}
49995    rectangle $x0r,$y0r,$x1r,$y1r,0.7,0xF0F0F0F0,255,255,255,255
49996    rectangle $x0r,$y0r,$x1r,$y1r,0.7,0x0F0F0F0F,0,0,0,255
49997    if $43 to "Z0 = ( "{_$x0}" , "{_$y0}" )\nZ1 = ( "{_$x1}" , "{_$y1}" )",2,2,16 fi
49998  endl done
49999
50000  u "{"$x0"}{"$y0"}{"$x1"}{"$y1"}{$5}"\
50001    "{$6}_"$is_custom_expression\
50002    "{$7}_"$is_custom_expression\
50003    "{$8}_"$is_custom_expression\
50004    "{$9}{$10}{$11}{$12}"\
50005    "{$13}_"$is_color_by_iter\
50006    "{$14}_"$is_color_by_iter\
50007    "{$15}_"$is_color_by_iter\
50008    "{$16}_"$is_color_by_value\
50009    "{$17}_"$is_color_by_value\
50010    "{$18}_"$is_color_by_value\
50011    "{$19}_"$is_color_by_value\
50012    "{$20}_"$is_color_by_value\
50013    "{$21}_"$is_color_by_custom\
50014    "{$22}_"$is_color_by_custom\
50015    "{$23}_"$is_color_by_custom\
50016    "{$24}_"$is_color_by_custom\
50017    "{$25}_"$is_color_by_custom\
50018    "{$26}_"$is_color_by_custom\
50019    "{$27}{$28}{$29}{$30}{$31}{$32}{$33}"\
50020    "{$34}"_{$33==0?0:2}\
50021    "{"$px,$py"}{$37}{"$angle"}{0}{0}{0}{0}{$43}{$44}"
50022
50023#@gui Plasma : fx_plasma, fx_plasma(0)
50024#@gui : Alpha = float(0.5,0,5)
50025#@gui : Beta = float(0,0,100)
50026#@gui : Scale = int(8,2,10)
50027#@gui : Randomize = bool(0)
50028#@gui : Transparency = bool(0)
50029#@gui : Color Balance = color(128,128,128)
50030#@gui : sep = separator()
50031#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/20/03</i>.</small>")
50032fx_plasma :  skip ${4=0},${5=0}
50033  if $5 to_rgba else to_rgb fi
50034  if $4 rand 0,255 fi
50035  plasma $1,$2,$3 n 0,255
50036  balance_gamma ${6-8}
50037
50038#@gui Quick Copyright : fx_quick_copyright, fx_quick_copyright(0)
50039#@gui : Text = text{"\\251 G'MIC"}
50040#@gui : Size = int(27,13,128)
50041#@gui : Color = color(255,255,255,128)
50042#@gui : Outline = int(1,0,4)
50043#@gui : Position = choice(3,"Up-Left","Up-Right","Bottom-Left","Bottom-Right")
50044#@gui : Offset = int(5,0,40)
50045#@gui : Orientation = choice(1,"-90 deg.","0 deg.","+90 deg.","+180 deg.")
50046#@gui : sep = separator()
50047#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50048fx_quick_copyright :
50049  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
50050  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}
50051  rotate[0,1] {90*($10-1)}
50052  repeat $!-2
50053  if $8==0 j. [0],$9,$9,0,0,{$6/255},[1]
50054  elif $8==1 j. [0],{w-1-{0,w}-$9},$9,0,0,{$6/255},[1]
50055  elif $8==2 j. [0],$9,{h-1-{0,h}-$9},0,0,{$6/255},[1]
50056  else j. [0],{w-1-{0,w}-$9},{h-1-{0,h}-$9},0,0,{$6/255},[1]
50057  fi
50058  mv. 2 done
50059  rm[0,1]
50060
50061#@gui Rainbow : fx_rainbow, fx_rainbow
50062#@gui : Left Position = float(80,0,100)
50063#@gui : Right Position = float(80,0,100)
50064#@gui : Left Slope = float(175,0,400)
50065#@gui : Right Slope = float(175,0,400)
50066#@gui : Thinness = float(3,0.1,8)
50067#@gui : Opacity = float(80,0,199)
50068#@gui : sep = separator()
50069#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50070fx_rainbow :
50071  repeat $! l[$>]
50072    100%,100% spline. 0,$1%,100,{-$3}%,100%,$2%,100,$4%,1,1
50073    flood. 0,0,0,0,0,1,1 flood. {w-1},0,0,0,0,1,1
50074    distance. 0 c. 0,255 n. 0,{$5*255}
50075    palette rainbow +luminance. c. 0,{min(100,200-$6)}% n. 0,255 a[-2,-1] c
50076    map.. . rm.
50077    if $6<100 sh. 3 *. {$6/100} rm. fi
50078    blend alpha
50079  endl done
50080
50081#@gui Shade Bobs : fx_shadebobs, fx_shadebobs
50082#@gui : note = note("<small>Bobs parameters :</small>")
50083#@gui : Density = int(50,1,200)
50084#@gui : Radius = int(5,1,100)
50085#@gui : Duration = int(200,1,500)
50086#@gui : Velocity = float(1,0,10)
50087#@gui : sep = separator()
50088#@gui : note = note("<small>Curve parameters :</small>")
50089#@gui : Rx = float(-1,-3,3)
50090#@gui : Ry = float(2,-3,3)
50091#@gui : Rz = float(1,-3,3)
50092#@gui : Rt = float(0.8,-3,3)
50093#@gui : Rcx = float(0,-3,3)
50094#@gui : Colormap = choice(8,"Grayscale","Standard","HSV","Lines","Hot","Cool","Jet","Flag","Cube")
50095#@gui : sep = separator()
50096#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/18/04</i>.</small>")
50097fx_shadebobs :
50098  channels 0 f 0
50099  repeat $! l[$>]
50100    t=0
50101    repeat $3
50102      repeat $1
50103        r={$6+$5*cos(6*$7*$t)+(1-$5)*sin(6*$8*$t)}
50104        a={(360*sin($7*$t)+30*$6*$>)*pi/180}
50105        ax={2*$>*pi/$1+$t}
50106        cx={(1+$9*cos($ax)+$r*cos($a))*w/2}
50107        cy={(1+$9*sin($ax)+$r*sin($a))*h/2}
50108        ellipse. $cx,$cy,$2%,$2%,0,-1,1
50109      done
50110      t+={$4%}
50111    done
50112  endl done
50113  & 255 if $10 map {$10-1} fi
50114
50115#@gui Sine Curve : fx_sine_curve, fx_sine_curve_preview
50116#@gui : note = note("<span color="#EE5500"><b>Curve parameters:</b></span>")
50117#@gui : Preset = choice{1,"Default (Circle)","Alien Rasta","All Round","Carnivorous Plant","Cat Pad","Flower",
50118#@gui : "Flower Cushion","Fly Karateka","Hearts","Moving Leaf","Radioactive Flower","Rosace","Spaceship",
50119#@gui : "Transformer","Tubular Waves","Twisted Heart","Twisted Heart 2","Twisted Tunnel","Waterslide"}
50120#@gui : Previous Preset = value(-1)
50121#@gui : Resolution (%) = float(75,0,100)
50122#@gui : Periods = float(1,0,3)
50123#@gui : sep = separator()
50124#@gui : Parameter Settings = choice(1,"Ratios","Multipliers","Offsets","Exponents","Signs","3D Angles")
50125#@gui : note = note("<small><span color="#EE0055"><b>Ratios:</b></span></small>")
50126#@gui : Xa/Xb = float(0.5,0,1)_0-
50127#@gui : Ya/Yb = float(0.5,0,1)_0
50128#@gui : Za/Zb = float(0.5,0,1)_0
50129#@gui : note = note("<small><span color="#EE0055"><b>Multipliers:</b></span></small>")
50130#@gui : Xa-Multiplier = int(1,0,1024)_2-
50131#@gui : Ya-Multiplier = int(1,0,1024)_2
50132#@gui : Za-Multiplier = int(0,0,1024)_2
50133#@gui : Xb-Multiplier = int(800,0,1024)_2
50134#@gui : Yb-Multiplier = int(800,0,1024)_2
50135#@gui : Zb-Multiplier = int(1,0,1024)_2
50136#@gui : note = note("<small><span color="#EE0055"><b>Offsets:</b></span></small>")
50137#@gui : Xa-Offset (deg.) = float(90,0,360)_0-
50138#@gui : Ya-Offset (deg.) = float(0,0,360)_0
50139#@gui : Za-Offset (deg.) = float(0,0,360)_0
50140#@gui : Xb-Offset (deg.) = float(90,0,360)_0
50141#@gui : Yb-Offset (deg.) = float(0,0,360)_0
50142#@gui : Zb-Offset (deg.) = float(0,0,360)_0
50143#@gui : note = note("<small><span color="#EE0055"><b>Exponents:</b></span></small>")
50144#@gui : Xa-Exponent = float(1,0,32)_0-
50145#@gui : Ya-Exponent = float(1,0,32)_0
50146#@gui : Za-Exponent = float(1,0,32)_0
50147#@gui : Xb-Exponent = float(1,0,32)_0
50148#@gui : Yb-Exponent = float(1,0,32)_0
50149#@gui : Zb-Exponent = float(1,0,32)_0
50150#@gui : note = note("<small><span color="#EE0055"><b>Signs:</b></span></small>")
50151#@gui : Xa-Sign = choice("Preserve","Invert","Negative","Positive")_0-
50152#@gui : Ya-Sign = choice("Preserve","Invert","Negative","Positive")_0
50153#@gui : Za-Sign = choice("Preserve","Invert","Negative","Positive")_0
50154#@gui : Xb-Sign = choice("Preserve","Invert","Negative","Positive")_0
50155#@gui : Yb-Sign = choice("Preserve","Invert","Negative","Positive")_0
50156#@gui : Zb-Sign = choice("Preserve","Invert","Negative","Positive")_0
50157#@gui : note = note("<small><span color="#EE0055"><b>3D Angles:</b></span></small>")
50158#@gui : X-Angle (deg.) = float(0,-180,180)_0-
50159#@gui : Y-Angle (deg.) = float(0,-180,180)_0
50160#@gui : Z-Angle (deg.) = float(0,-180,180)_0
50161#@gui : Zoom = float(1,0,10)_0
50162#@gui : Focale = int(8,1,20)_0
50163#@gui : sep = separator()
50164#@gui : note = note("<span color="#EE5500"><b>Rendering parameters:</b></span>")
50165#@gui : Center = point(50,50,0,1,0,238,85,-170,10)_0
50166#@gui : Old X-Center = value(50)
50167#@gui : Old Y-Center = value(50)
50168#@gui : Radius = point(68,68,0,1,238,0,85,-170,10)_0
50169#@gui : Angle = point(75,50,0,1,238,85,0,-170,10)_0
50170#@gui : Old X-Angle = value(75)
50171#@gui : Old Y-Angle = value(50)
50172#@gui : Primary radius (%) = float(3,0,100)
50173#@gui : Secondary radius (%) = float(2,0,100)
50174#@gui : Opacity (%) = float(40,0,100)
50175#@gui : Color = color(255,255,255)
50176#@gui : Anti-aliasing = choice(2,"None","× 1.25","× 1.5","× 2","× 3")
50177#@gui : sep = separator()
50178#@gui : Preview background = choice(1,"Image","Black","White")
50179#@gui : sep = separator()
50180#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2021/03/04</i>.</small>")
50181fx_sine_curve :
50182
50183  # Get parameters as named variables.
50184  preset,previous_preset,\
50185  resolution,periods,dp,\
50186  ratx,raty,ratz,\
50187  mxa,mya,mza,mxb,myb,mzb,\
50188  oxa,oya,oza,oxb,oyb,ozb,\
50189  pxa,pya,pza,pxb,pyb,pzb,\
50190  sxa,sya,sza,sxb,syb,szb,\
50191  rotx,roty,rotz,zoom,focale,\
50192  xc,yc,prev_xc,prev_yc,xr,yr,xa,ya,prev_xa,prev_ya,\
50193  radius1,radius2,opacity,\
50194  colR,colG,colB,\
50195  antialiasing,\
50196  preview_background=$*
50197
50198  if !narg($_is_preview) _is_preview=0 fi
50199  if [$prev_xc,$prev_yc]!=[$xc,$yc]
50200    xr,yr,xa,ya,prev_xa,prev_ya+={d=[$xc,$yc]-[$prev_xc,$prev_yc];[d,d,d]}
50201  fi
50202  if [$prev_xa,$prev_ya]!=[$xa,$ya]
50203    delta_a={"
50204      a = [ "$xa" - "$xc", "$ya" - "$yc" ];
50205      b = [ "$prev_xa" - "$xc", "$prev_ya" - "$yc" ];
50206      (atan2(a[1],a[0]) - atan2(b[1],b[0]))*180/pi;
50207    "}
50208    xr,yr={[$xc,$yc]+rot($delta_a°)*[$xr-$xc,$yr-$yc]}
50209  else delta_a=0 fi
50210  if [$colR,$colG,$colB]==[0,0,0]" && "$preview_background==1 colR,colG,colB=255
50211  elif [$colR,$colG,$colB]==[255,255,255]" && "$preview_background==2 colR,colG,colB=0
50212  fi
50213
50214  # Manage presets.
50215  update_params=0
50216  if $preset!=$previous_preset
50217
50218    # Set default parameters for presets ('Default (Circle)').
50219    periods=1
50220    ratx,raty,ratz=0.5,0.5,0
50221    mxa,mxb,mya,myb,mza,mzb=1,1,1,1,0,1
50222    oxa,oxb,oya,oyb,oza,ozb=90,90,0,0,0,0
50223    pxa,pxb,pya,pyb,pza,pzb=1
50224    sxa,sxb,sya,syb,sza,szb=0
50225    rotx,roty,rotz,zoom,focale=0,0,0,2,8
50226
50227    # Set specific values for each preset.
50228
50229    # Default (circle)
50230    if $preset==0
50231      ratx,raty,ratz=0 zoom=1
50232    # Alien Rasta
50233    elif $preset==1
50234      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
50235    # All Round
50236    elif $preset==2
50237      mxa,mxb,mya,myb=1,200,1,150 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50238    # Carnivorous Plant
50239    elif $preset==3
50240      mxa,mxb,mya,myb=9,512,1024,9 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50241    # Cat Pad
50242    elif $preset==4
50243      mxa,mxb,mya,myb=80,1,80,1 pxa,pxb,pya,pyb=1,3,1,3
50244    # Flower
50245    elif $preset==5
50246      ratz=0.8 mza,mzb=7,1024 pza,pzb=1.6,2 rotz=45 zoom=1 focale=4
50247    # Flower Cushion
50248    elif $preset==6
50249      mxa,mxb,mya,myb=80,1,1,80 pxa,pxb,pya,pyb=1,3,1,3
50250    # Fly Karateka
50251    elif $preset==7
50252      mxa,mxb,mya,myb=150,1,1,100 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50253    # Hearts
50254    elif $preset==8
50255      mxa,mxb,mya,myb=1,80,80,80 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50256    # Moving Leaf
50257    elif $preset==9
50258      mxa,mxb,mya,myb=2,200,200,1 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50259    # Radioactive Flower
50260    elif $preset==10
50261      mxa,mxb,mya,myb=1,800,1,800 pxa,pxb,pya,pyb=1,3,1,3
50262    # Rosace
50263    elif $preset==11
50264      mxa,mxb,mya,myb=1,10,1,10
50265    # Spaceship
50266    elif $preset==12
50267      mxa,mxb,mya,myb=1,400,1,200 pxa,pxb,pya,pyb=1,3,1,3 sxa,sxb,sya,syb=1,1,0,0
50268    # Transformer
50269    elif $preset==13
50270      mxa,mxb,mya,myb=1,800,800,2 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50271    # Tubular Waves
50272    elif $preset==14
50273      mxa,mxb,mya,myb=1,30,1,60 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50274    # Twisted Heart
50275    elif $preset==15
50276      mxa,mxb,mya,myb=500,1,1,500 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50277    # Twisted Heart 2
50278    elif $preset==16
50279      mxa,mxb,mya,myb=1,80,80,1 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50280    # Twisted Tunnel
50281    elif $preset==17
50282      mxa,mxb,mya,myb=1,80,1,40 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2
50283    # Waterslide
50284    elif $preset==18
50285      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
50286    fi
50287  fi
50288
50289  # Change unit for some variables.
50290  W,H={w#0?[w#0,h#0]:0$_is_preview" && "0$_preview_width?[0$_preview_width,0$_preview_height]:[1024,1024]}
50291  nresolution={max(1,round($periods*1000000*($resolution%)^2))}
50292  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)}
50293  noxa,noxb,noya,noyb,noza,nozb={[$oxa,$oxb,$oya,$oyb,$oza,$ozb]*pi/180}
50294  nantialiasing={arg0($antialiasing,1,1.25,1.5,2,3)}
50295  rW,rH={[$W,$H]*$nantialiasing} # Size of the non-antialiased rendering
50296
50297  # Generate and render curve.
50298  l[]
50299
50300    # Compute curve coordinates.
50301    $nresolution,1,1,2,"*
50302      begin(
50303        const is_rot = "$rotx" || "$roty" || "$rotz";
50304        ref(rot(1,0,0,"$rotx"°),Rx);
50305        ref(rot(0,1,0,"$roty"°),Ry);
50306        ref(rot(0,0,1,"$rotz"°),Rz);
50307        R = mul(Rz,mul(Ry,Rx,3),3); # 3D rotation
50308
50309        # Variables to manage 2D rotation.
50310        const ang = atan2("$ya" - "$yc","$xa" - "$xc");
50311        const cosa = cos(ang);
50312        const sina = sin(ang);
50313
50314        # Variables to manage aspect ratio.
50315        const c = 35;
50316        const dxr0 = "$xr" - "$xc";
50317        const dyr0 = "$yr" - "$yc";
50318        const dxr = cosa*dxr0 + sina*dyr0;
50319        const dyr = -sina*dxr0 + cosa*dyr0;
50320        const _dx = abs(dxr)/c; const dx = 2.5*c*(_dx<1?_dx:_dx^3);
50321        const _dy = abs(dyr)/c; const dy = 2.5*c*(_dy<1?_dy:_dy^3);
50322      );
50323
50324      cpow(x,p,s) = (
50325        ref(x,_x);
50326        (!s?sign(_x):s==1?-sign(_x):s==2?-1:1)*abs(_x)^p
50327      );
50328
50329      t = x/w*2*pi*"$periods";
50330      X = lerp(cpow(sin("$mxa"*t + "$noxa"),"$pxa","$sxa"),
50331               cpow(sin("$mxb"*t + "$noxb"),"$pxb","$sxb"),
50332               "$ratx");
50333      Y = lerp(cpow(sin("$mya"*t + "$noya"),"$pya","$sya"),
50334               cpow(sin("$myb"*t + "$noyb"),"$pyb","$syb"),
50335               "$raty");
50336      Z = lerp(cpow(sin("$mza"*t + "$noza"),"$pza","$sza"),
50337               cpow(sin("$mzb"*t + "$nozb"),"$pzb","$szb"),
50338               "$ratz");
50339
50340      # Set aspect ratio and rotate.
50341      X*=dx%;
50342      Y*=dy%;
50343      is_rot?(P = R*[ X,Y,Z ]; X = P[0]; Y = P[1]; Z = P[2]);
50344
50345      # 2D projection.
50346      X*="$nfocale";
50347      Y*="$nfocale";
50348      Z = max(1e-5,Z + 1 + "$nfocale");
50349      pX = X/Z;
50350      pY = -Y/Z;
50351
50352      # Normalize and get display coordinates.
50353      ang?(X = cosa*pX - sina*pY; pY = sina*pX + cosa*pY; pX = X);
50354
50355      const ax = "$zoom*$rW"; const bx = "$xc*$rW"%;
50356      const ay = "$zoom*$rH"; const by = "$yc*$rH"%;
50357      [ ax*pX + bx, ay*pY + by ]"
50358
50359    # Draw curve (as an alpha-channel).
50360    $rW,$rH
50361    eval.. "*
50362      const mwh = min(w#-1,h#-1)*5%;
50363      const r1 = max(0.01,mwh*"$radius1"%);
50364      const r2 = max(0.01,mwh*"$radius2"%);
50365      const Mr = max(r1,r2);
50366      const opacity = ("$opacity"%)^3;
50367
50368      X = R; Y = G;
50369
50370      Mr<0?(
50371        i(#-1,X,Y) = lerp(i(#-1,X,Y),1,opacity);
50372      ):(
50373        pX = i(x - 1,0,0,0);
50374        pY = i(x - 1,0,0,1);
50375        dX = X - pX;
50376        dY = Y - pY;
50377        ang = atan2(dY,dX)*180/pi;
50378        ellipse(#-1,X,Y,r1,r2,ang°,opacity,255);
50379      );
50380      I"
50381    rm..
50382    r. $W,$H,1,1,2 n 0,255
50383    i[0] 100%,100%,1,3 fc[0] $colR,$colG,$colB
50384    a[-2,-1] c
50385  endl
50386
50387  if 0$_is_preview
50388    if $!==1 i[0] $W,$H,1,3 fi
50389    if $preview_background [0],[0] f. {$preview_background==1?0:255} to_rgb. rv[0,-1] rm. fi
50390    blend[0,-1] alpha
50391    line. $xc%,$yc%,$xr%,$yr%,0.75,0xF0F0F0F0,238,0,85
50392    line. $xc%,$yc%,$xr%,$yr%,0.75,0x0F0F0F0F,255
50393    line. $xc%,$yc%,$xa%,$ya%,0.75,0xF0F0F0F0,238,85,0
50394    line. $xc%,$yc%,$xa%,$ya%,0.75,0x0F0F0F0F,0
50395  fi
50396  mv. 0
50397  if 0$_output_mode k[0] fi
50398
50399  # Update parameter values.
50400  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]}
50401  u "{"$preset"}{"$preset"}{"$resolution"}{"$periods"}{"$dp"}"\
50402    "{"$ratx"}_"$r"{"$raty"}_"$r"{"$ratz"}_"$r\
50403    "{"$mxa"}_"$m"{"$mya"}_"$m"{"$mza"}_"$m"{"$mxb"}_"$m"{"$myb"}_"$m"{"$mzb"}_"$m\
50404    "{"$oxa"}_"$o"{"$oya"}_"$o"{"$oza"}_"$o"{"$oxb"}_"$o"{"$oyb"}_"$o"{"$ozb"}_"$o\
50405    "{"$pxa"}_"$p"{"$pya"}_"$p"{"$pza"}_"$p"{"$pxb"}_"$p"{"$pyb"}_"$p"{"$pzb"}_"$p\
50406    "{"$sxa"}_"$s"{"$sya"}_"$s"{"$sza"}_"$s"{"$sxb"}_"$s"{"$syb"}_"$s"{"$szb"}_"$s\
50407    "{"$rotx"}_"$a"{"$roty"}_"$a"{"$rotz"}_"$a"{"$zoom"}_"$a"{"$focale"}_"$a\
50408    "{"$xc,$yc"}{"$xc"}{"$yc"}{"$xr,$yr"}{"$xa,$ya"}{"$xa"}{"$ya"}{"$radius1"}{"$radius2"}{"$opacity"}"\
50409    "{"$colR,$colG,$colB"}{"$antialiasing"}"\
50410    "{"$preview_background"}"
50411
50412fx_sine_curve_preview :
50413  _is_preview=1
50414  fx_sine_curve $*
50415  k[0]
50416
50417#@gui Superformula : fx_superformula, fx_superformula(1)
50418#@gui : Resolution = int(4096,2,8192)
50419#@gui : sep = separator()
50420#@gui : X-Size = float(0.9,0,2)
50421#@gui : Y-Size = float(0.9,0,2)
50422#@gui : sep = separator()
50423#@gui : M = int(8,1,32)
50424#@gui : N1 = float(1,-32,32)
50425#@gui : N2 = float(5,-32,32)
50426#@gui : N3 = float(8,-32,32)
50427#@gui : sep = separator()
50428#@gui : X-Angle = float(0,0,360)
50429#@gui : Y-Angle = float(0,0,360)
50430#@gui : Z-Angle = float(0,0,360)
50431#@gui : sep = separator()
50432#@gui : Thickness = float(3,0,50)
50433#@gui : Color = color(128,255,128,255)
50434#@gui : sep = separator()
50435#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/04</i>.</small>")
50436fx_superformula :
50437  repeat $! l[$>] to_rgba
50438    {w},{h}
50439    f3d {0.5*max(w,h)/tan($4*pi/360)}
50440    superformula3d $1,${4-7}
50441    r3d. 0,0,1,$10 r3d. 0,1,0,$9 r3d. 1,0,0,$8
50442    *3d. {0.5*$2*{-2,w}},{0.5*$3*{-2,h}}
50443    col3d. 1 j3d.. .,50%,50%,0,1,1,0,0 rm.
50444    distance. 1 >. $11% *.. . ==. 0
50445    r. 100%,100%,1,4
50446    sh. 0 *. $12 rm.
50447    sh. 1 *. $13 rm.
50448    sh. 2 *. $14 rm.
50449    sh. 3 *. $15 rm.
50450    +[-2,-1]
50451  endl done
50452
50453#@gui Symmetric 2D Shape : fx_symmetric_shape2d, fx_symmetric_shape2d_preview(1)
50454#@gui : Subdivisions = int(5,2,32)
50455#@gui : Center = point(50,50,0,1,255,255,255,128)
50456#@gui : Old Center = value(50,50)
50457#@gui : Angle / Size = point(50,30,0,1,255,255,255,128)
50458#@gui : Old Angle / Size = value(50,30)
50459#@gui : sep = separator()
50460#@gui : Control Point 1 = point(50,25,1,1,255,128,0,255,4)
50461#@gui : Control Point 2 = point(56,42,1,1,255,128,0,255,4)
50462#@gui : Control Point 3 = point(52,52,-1,1,255,128,0,255,4)
50463#@gui : Control Point 4 = point(52,52,-1,1,255,128,0,255,4)
50464#@gui : Control Point 5 = point(52,52,-1,1,255,128,0,255,4)
50465#@gui : Control Point 6 = point(52,52,-1,1,255,128,0,255,4)
50466#@gui : sep = separator()
50467#@gui : Drawing Mode = choice(1,"Outlined","Filled")
50468#@gui : Color = color(255,0,255)
50469#@gui : Opacity (%) = float(100,0,100)
50470#@gui : sep = separator()
50471#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/06/17</i>.</small>")
50472fx_symmetric_shape2d :
50473  if !narg($_is_preview) _is_preview=0 fi
50474  repeat $! l[$<]
50475    100%,100%,1,4 _fx_symmetric_shape2d. $* rv
50476    if !0$_is_preview" && "0$_output_mode rm. fi
50477  endl done
50478
50479_fx_symmetric_shape2d :
50480  (${10-21}) f. "isnan(i)?-1024:i" discard. -1024 r. 2,{h/2},1,1,-1 permute. cyzx
50481  f. "x = R - $2; y = G - $3; [ atan2(y,x), norm(x,y) ]"
50482  l. n={h} .x{$1-1} a y f "const pi2 = 2*pi; [ (R + int(y/"$n")*pi2/$1)%pi2, G ]" sort +,y endl
50483  f. "[ $2*w#-2,$3*h#-2 ]/100 + [ G*cos(R), G*sin(R) ]*(min(w#-2,h#-2)-1)%" permute. cyzx
50484  coords={^} rm.
50485  if $22 polygon {narg($coords)/2},$coords,1,${23-25},{$26*255%}
50486  else polygon {narg($coords)/2},$coords,1,0xFFFFFFFF,${23-25},{$26*255%}
50487  fi
50488
50489fx_symmetric_shape2d_preview :
50490  _fx_symmetric_shape2d_preview $*
50491
50492_fx_symmetric_shape2d_preview :
50493  _is_preview=1
50494  cx1,cy1,cx2,cy2,cx3,cy3,cx4,cy4,cx5,cy5,cx6,cy6=${10-21}
50495  angx,angy=$6,$7
50496  if [$2,$3]!=[$4,$5]" || "[$angx,$angy]!=[$8,$9] # Center or angle has been modified
50497    dx,dy={[$2-$4,$3-$5]}
50498    repeat 6 i={1+$>}
50499      cx$i,cy$i={"
50500        const cx = "${cx$i}";
50501        const cy = "${cy$i}";
50502        dang = atan2($7 - $3,$6 - $2) - atan2($9 - $3,$8 - $2);
50503        dsca = norm($6 - $2,$7 - $3)/norm($9 - $3,$8 - $2);
50504        [ $2,$3 ] + dsca*rot(dang)*[ cx - $4, cy - $5 ]"}
50505    done
50506    angx,angy={[$6+$2-$4,$7+$3-$5]}
50507  fi
50508
50509  repeat $! l[$>]
50510    r {s=min(w,h);[s,s]},1,100%,0,0,0.5,0.5
50511    fx_symmetric_shape2d ${1-9},$cx1,$cy1,$cx2,$cy2,$cx3,$cy3,$cx4,$cy4,$cx5,$cy5,$cx6,$cy6,${22-26}
50512    rv blend alpha
50513    eval "
50514      t0 = atan2($7 - $3,$6 - $2);
50515      repeat ($1,k,
50516        const pi2 = 2*pi;
50517        const xc = $2*(w-1)%;
50518        const yc = $3*(h-1)%;
50519        x = xc + (w+h)*cos(t0 + 2*pi*k/$1);
50520        y = yc + (w+h)*sin(t0 + 2*pi*k/$1);
50521        polygon(-2,xc,yc,x,y,0.35,0xF0F0F0F0,255);
50522        polygon(-2,xc,yc,x,y,0.35,0x0F0F0F0F,0);
50523      )"
50524  endl done
50525
50526  u "{$1}"\      # Subdivisions
50527    "{$2,$3}"\   # Center
50528    "{$2,$3}"\   # Old Center
50529    "{"$angx,$angy"}"\   # Angle
50530    "{"$angx,$angy"}"\   # Old Angle
50531    "{"$cx1,$cy1"}"\ # Control point 1
50532    "{"$cx2,$cy2"}"\ # Control point 2
50533    "{"$cx3,$cy3"}"\ # Control point 3
50534    "{"$cx4,$cy4"}"\ # Control point 4
50535    "{"$cx5,$cy5"}"\ # Control point 5
50536    "{"$cx6,$cy6"}"\ # Control point 6
50537    "{$22}"\     # Drawing mode
50538    "{$23,$24,$25}"\ # Color
50539    "{$26}"      # Opacity
50540
50541#@gui Tree : fx_tree, fx_tree_preview(1)
50542#@gui : note = note("<small><b><span color="#EE5500">Global parameters:</span></b></small>")
50543#@gui : Recursion Depth = int(11,1,18)
50544#@gui : Random Seed = int(10000,0,65535)
50545#@gui : X-ratio = float(0,-1,1)
50546#@gui : Y-ratio = float(0,-1,1)
50547#@gui : note = note("<small><b><span color="#FF0055">Note:</span></b> Set <i>Random Seed</i> to <b>0</b> to make it \
50548# random as well.</small>")
50549#@gui : sep = separator(), note = note("<small><b><span color="#EE5500">Trunk:</span></b></small>")
50550#@gui : Thickness (%) = float(15,0,100)
50551#@gui : Base Thickness (%) = float(150,0,300)
50552#@gui : Angle (deg.) = float(0,-90,90)
50553#@gui : sep = separator(), note = note("<small><b><span color="#EE5500">Recursion:</span></b></small>")
50554#@gui : Avg Branching = float(2.15,1,6)
50555#@gui : Std Branching = float(0.8,0,6)
50556#@gui : Avg Left Angle (deg.) = float(-40,-90,90)
50557#@gui : Avg Right Angle (deg.) = float(40,-90,90)
50558#@gui : Std Angle (deg.) = float(10,0,90)
50559#@gui : Avg Length Factor (%) = float(75,0,200)
50560#@gui : Std Length Factor (%) = float(0,0,200)
50561#@gui : Avg Thickness Factor (%) = float(70,0,200)
50562#@gui : Std Thickness Factor (%) = float(20,0,200)
50563#@gui : sep = separator(), note = note("<small><b><span color="#EE5500">Colors / Opacity:</span></b></small>")
50564#@gui : Trunk color = color(40,25,0,255)
50565#@gui : Trunk opacity (%) = float(100,0,100)
50566#@gui : Leaf color = color(70,140,60,255)
50567#@gui : Leaf opacity (%) = float(100,0,100)
50568#@gui : Color gamma = float(0.4,-2,2)
50569#@gui : Opacity gamma = float(0.4,-2,2)
50570#@gui : sep = separator()
50571#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/03/24</i>.</small>")
50572_fx_tree:
50573  recursion_depth,random_seed,xratio,yratio,\
50574  trunk_thickness,base_thickness,trunk_angle,\
50575  avg_branching,std_branching,avg_leftangle,avg_rightangle,std_angle,avg_length,std_length,avg_thickness,std_thickness,\
50576  Rt,Gt,Bt,At,Ot,Rl,Gl,Bl,Al,Ol,gammaRGBA,gammaO=${1-28}
50577
50578  W,H,S={[w,h,min(w,h)]} l[]
50579    if $2 srand $2 fi
50580
50581    # Init trunk.
50582    1,1,1,9,"
50583      const h_thickness = "$trunk_thickness"%/2;
50584      const hb_thickness = h_thickness*"$base_thickness"%;
50585      R = rot("$trunk_angle"°);
50586      C = [ 0.5,0 ];
50587      P0 = C + R*[ -hb_thickness,0 ];
50588      P1 = C + R*[ hb_thickness,0 ];
50589      P2 = C + R*[ h_thickness,0.5 ];
50590      P3 = C + R*[ -h_thickness,0.5 ];
50591      [ P0,P1,P2,P3,0 ]"
50592
50593    # Compute tree geometry.
50594    repeat $recursion_depth
50595      1,8,1,9
50596      eval.. ">
50597        const dangle = "$avg_rightangle" - "$avg_leftangle";
50598        ref(I,val);
50599        ref(val[0,2],P0);
50600        ref(val[2,2],P1);
50601        ref(val[4,2],P2);
50602        ref(val[6,2],P3);
50603        ndepth = val[8] + 1;
50604
50605        # Median axis.
50606        A = (P0 + P1)/2;
50607        B = (P2 + P3)/2;
50608        AB = B - A;
50609        thickness = norm(B - P2);
50610
50611        N = round(cut("$avg_branching" + u(-1,1)*"$std_branching",1,6));
50612        Nm1 = N<=1?1:N - 1;
50613
50614        repeat (N,n,
50615          ang = cut("$avg_leftangle" + dangle*n/Nm1 + u(-1,1)*"$std_angle",-90,90);
50616          len = cut("$avg_length" + u(-1,-1)*"$std_length",0,200);
50617          rot = rot(ang°);
50618          nB = B + len%*rot*AB;
50619          orth = (nB - B);
50620          orth/=norm(orth);
50621          orth = [ -orth[1],orth[0] ];
50622          nthickness = thickness*("$avg_thickness" + u(-1,1)*"$std_thickness")%;
50623          Q0 = nB + nthickness*orth;
50624          Q1 = nB - nthickness*orth;
50625          da_push([ P3,P2,Q1,Q0,ndepth ]);
50626        );
50627        end(resize(#-1,1,da_size(),1,s#-1,0)); val"
50628    done
50629    a y
50630
50631    # Normalize coordinates to fit image size and aspect ratio.
50632    1,100%,1,4,"[ i0#-1,i2#-1,i4#-1,i6#-1 ]"
50633    1,100%,1,4,"[ i1#-2,i3#-2,i5#-2,i7#-2 ]"
50634    sx,sy={10^[$xratio,$yratio]}
50635    -.. 0.5 *.. {-2,$S*$sx/(2.1*max(abs(iM),abs(im)))} +.. {$W/2} *. {$S*$sy/(1.05*max(iM))}
50636    f... "round([ i0#-2,i0#-1,i1#-2,i1#-1,i2#-2,i2#-1,i3#-2,i3#-1,i8 ])"
50637    rm[-2,-1]
50638
50639    # Make sure trunk starts at bottom when rotated.
50640    eval "ref(I,T); P0 = T[0,2]; P1 = T[2,2]; P2 = T[4,2]; P3 = T[6,2];
50641      I[0] = [ lerp(P0,P2,-2),lerp(P1,P2,-2),P2,P3,0 ]"
50642
50643    # Draw tree.
50644    $W,$H,1,4
50645    eval.. ">
50646      begin(
50647        RGBAt = [ "$Rt,$Gt,$Bt,$At" ];
50648        RGBAl = [ "$Rl,$Gl,$Bl,$Al" ];
50649        const gRGBA = 10^"$gammaRGBA";
50650        const gO = 10^"$gammaO";
50651      );
50652      ref(I,val);
50653      t = val[8]/"$recursion_depth"; # Between [0,1]
50654      RGBA = lerp(RGBAt,RGBAl,t^gRGBA);
50655      O = lerp("$Ot,$Ol",t^gO)%;
50656      polygon(#-1,4,val[0,8],O,RGBA);
50657      (i0==i2 && i1==i3) || (i4==i6 && i5==i7)?polygon(#-1,2,i0,i1,i4,i5,O,RGBA);
50658      val"
50659    mirror xy
50660    rm..
50661  endl
50662
50663fx_tree :
50664  _fx_tree. $* mv. 0
50665  if 0$_output_mode k[0] fi
50666
50667fx_tree_preview :
50668  repeat $! l[$>] _fx_tree $* blend alpha endl done
50669
50670#@gui Turbulence : fx_turbulence, fx_turbulence
50671#@gui : Radius = float(128,1,1024)
50672#@gui : Octaves = int(6,1,12)
50673#@gui : Damping per Octave = float(4,1,10)
50674#@gui : Difference Mixing = float(0,-10,10)
50675#@gui : Mode = choice("Turbulence","Turbulence 2","Fractal Noise","Fractured Clouds","Stardust","Pea Soup")
50676#@gui : sep = separator()
50677#@gui : note = note("<small>Author: <i>Preben Soeberg</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50678fx_turbulence :
50679  remove_opacity turbulence ${^0}
50680
50681
50682#@gui ____<b>Sequences</b>
50683#-------------------------
50684
50685# fx_animate_preview : _command,_parameters1,_parameters2,_compute_half={ 0 | 1 },_width>=0,_height>=0
50686# Generate a preview with start/end rendering of an animation.
50687fx_animate_preview : skip ${4=1},${5=0},${6=$5}
50688  repeat $!
50689    if $5 width=$5 else width={w} fi
50690    if $6 height=$6 else height={h} fi
50691    if $4 s. x,2 else . fi
50692    -$1.. $2 -$1. $3   # Assume this is a 1->1 filter.
50693    r[-2,-1] {max(w,{-2,w})},{max(h,{-2,h})},1,100%,3
50694    if !$4 columns.. 0,50% columns. 50%,100% fi
50695    a[-2,-1] x r. $width,$height,1,100%,2 drgba.
50696    line. 50%,0,50%,100%,1,0,0,0,255
50697    to. "Start",3,-1,13,2,1,255 to. "End",{w-24},{h-18},13,2,1,255
50698  mv. 0 done
50699
50700#@gui 3D Elevation [Animated] : fx_animate_elevation3d, fx_animate_elevation3d_preview(1)
50701#@gui : Frames = _int(10,2,100)
50702#@gui : Output as Frames = _bool(1)
50703#@gui : Output as Files = _bool(0)
50704#@gui : Output Folder = _folder()
50705#@gui : note = note{"\n<b>Global parameters :</b>"}
50706#@gui : Factor = float(100,-1000,1000)
50707#@gui : Smoothness = float(1,0,10)
50708#@gui : Width = _int(1024,8,4096)
50709#@gui : Height = _int(1024,8,4096)
50710#@gui : Rendering = choice(2,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
50711#@gui : note = note{"\n<b>Starting parameters :</b>"}
50712#@gui : Size = float(0.8,0,3)
50713#@gui : X-Angle = float(35,0,360)
50714#@gui : Y-Angle = float(0,0,360)
50715#@gui : Z-Angle = float(0,0,360)
50716#@gui : FOV = float(45,1,90)
50717#@gui : X-Light = float(0,-100,100)
50718#@gui : Y-Light = float(0,-100,100)
50719#@gui : Z-Light = float(-100,-100,0)
50720#@gui : Specular Lightness = float(0.5,0,1)
50721#@gui : Specular Shininess = float(0.7,0,3)
50722#@gui : note = note{"\n<b>Ending parameters :</b>"}
50723#@gui : Size = float(0.8,0,3)
50724#@gui : X-Angle = float(35,0,1440)
50725#@gui : Y-Angle = float(0,0,1440)
50726#@gui : Z-Angle = float(360,0,1440)
50727#@gui : FOV = float(45,1,90)
50728#@gui : X-Light = float(0,-100,100)
50729#@gui : Y-Light = float(0,-100,100)
50730#@gui : Z-Light = float(-100,-100,0)
50731#@gui : Specular Lightness = float(0.5,0,1)
50732#@gui : Specular Shininess = float(0.7,0,3)
50733#@gui : sep = separator()
50734#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50735fx_animate_elevation3d : skip "${4=}"
50736  if $3 filename="$4/gmic_elevation3d.png" else filename="" fi
50737  _fx_elevation3d ${5-6},0
50738  animate fx_render3d,"${7-8},${10-19},$9",\
50739                      "${7-8},${20-29},$9",$1,$2,{``$filename}
50740
50741fx_animate_elevation3d_preview : skip "${4=}"
50742  w={w} h={h}
50743  _fx_elevation3d ${5-6},0
50744  fx_animate_preview fx_render3d,$w","$h",${10-19},$9",\
50745                                      $w","$h",${20-29},$9",0,$w,$h
50746
50747#@gui 3D Extrusion [Animated] : fx_animate_extrude3d, fx_animate_extrude3d_preview(1)
50748#@gui : Frames = _int(10,2,100)
50749#@gui : Output as Frames = _bool(1)
50750#@gui : Output as Files = _bool(0)
50751#@gui : Output Folder = _folder()
50752#@gui : note = note{"\n<b>Global parameters :</b>"}
50753#@gui : Depth = float(10,1,256)
50754#@gui : Resolution = int(512,1,1024)
50755#@gui : Smoothness = float(0.6,0,3)
50756#@gui : Width = _int(1024,8,4096)
50757#@gui : Height = _int(1024,8,4096)
50758#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
50759#@gui : note = note{"\n<b>Starting parameters :</b>"}
50760#@gui : Size = float(0.8,0,3)
50761#@gui : X-Angle = float(35,0,360)
50762#@gui : Y-Angle = float(0,0,360)
50763#@gui : Z-Angle = float(0,0,360)
50764#@gui : FOV = float(45,1,90)
50765#@gui : X-Light = float(0,-100,100)
50766#@gui : Y-Light = float(0,-100,100)
50767#@gui : Z-Light = float(-100,-100,0)
50768#@gui : Specular Lightness = float(0.5,0,1)
50769#@gui : Specular Shininess = float(0.7,0,3)
50770#@gui : note = note{"\n<b>Ending parameters :</b>"}
50771#@gui : Size = float(0.8,0,3)
50772#@gui : X-Angle = float(35,0,1440)
50773#@gui : Y-Angle = float(360,0,1440)
50774#@gui : Z-Angle = float(0,0,1440)
50775#@gui : FOV = float(45,1,90)
50776#@gui : X-Light = float(0,-100,100)
50777#@gui : Y-Light = float(0,-100,100)
50778#@gui : Z-Light = float(-100,-100,0)
50779#@gui : Specular Lightness = float(0.5,0,1)
50780#@gui : Specular Shininess = float(0.7,0,3)
50781#@gui : sep = separator()
50782#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50783fx_animate_extrude3d : skip "${4=}"
50784  if $3 filename="$4/gmic_extrude3d.png" else filename="" fi
50785  _fx_extrude3d ${5-7},0
50786  animate fx_render3d,"${8-9},${11-20},$10",\
50787                      "${8-9},${21-30},$10",$1,$2,{``$filename}
50788
50789fx_animate_extrude3d_preview : skip "${4=}"
50790  w={w} h={h}
50791  _fx_extrude3d ${5-7},0
50792  fx_animate_preview fx_render3d,$w","$h",${11-20},$10",\
50793                                      $w","$h",${21-30},$10",0,$w,$h
50794
50795#@gui 3D Image Object [Animated] : fx_animate_imageobject3d, fx_animate_imageobject3d_preview(1)
50796#@gui : Frames = _int(10,2,100)
50797#@gui : Output as Frames = _bool(1)
50798#@gui : Output as Files = _bool(0)
50799#@gui : Output Folder = _folder()
50800#@gui : note = note{"\n<b>Global parameters :</b>"}
50801#@gui : Type = choice{1,"Plane","Cube","Pyramid","Sphere","Torus","Gyroid","Weird","Cup","Rubik"}
50802#@gui : Width = _int(1024,1,4096)
50803#@gui : Height = _int(1024,1,4096)
50804#@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong")
50805#@gui : note = note{"\n<b>Starting parameters :</b>"}
50806#@gui : Size = float(0.5,0,3)
50807#@gui : X-Angle = float(57,0,360)
50808#@gui : Y-Angle = float(41,0,360)
50809#@gui : Z-Angle = float(21,0,360)
50810#@gui : FOV = float(45,1,90)
50811#@gui : X-Light = float(0,-100,100)
50812#@gui : Y-Light = float(0,-100,100)
50813#@gui : Z-Light = float(-100,-100,0)
50814#@gui : Specular Lightness = float(0.5,0,1)
50815#@gui : Specular Shininess = float(0.7,0,3)
50816#@gui : note = note{"\n<b>Ending parameters :</b>"}
50817#@gui : Size = float(0.5,0,3)
50818#@gui : X-Angle = float(57,0,1440)
50819#@gui : Y-Angle = float(401,0,1440)
50820#@gui : Z-Angle = float(21,0,1440)
50821#@gui : FOV = float(45,1,90)
50822#@gui : X-Light = float(0,-100,100)
50823#@gui : Y-Light = float(0,-100,100)
50824#@gui : Z-Light = float(-100,-100,0)
50825#@gui : Specular Lightness = float(0.5,0,1)
50826#@gui : Specular Shininess = float(0.7,0,3)
50827#@gui : sep = separator()
50828#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50829fx_animate_imageobject3d : skip "${4=}"
50830  if $3 filename="$4/gmic_imageobject3d.png" else filename="" fi
50831  _fx_imageobject3d "_",$5
50832  animate fx_render3d,"${6-7},${9-18},$8",\
50833                      "${6-7},${19-28},$8",$1,$2,{``$filename}
50834
50835fx_animate_imageobject3d_preview : skip "${4=}"
50836  w={w} h={h}
50837  _fx_imageobject3d "_preview_",$5
50838  fx_animate_preview fx_render3d,$w","$h",${9-18},$8",\
50839                                      $w","$h",${19-28},$8",0,$w,$h
50840
50841#@gui 3D Text Pointcloud : fx_text_pointcloud3d, fx_text_pointcloud3d_preview
50842#@gui : Frames = _int(64,1,256)
50843#@gui : 1st Text = text("G'MIC")
50844#@gui : 2nd Text = text("Rocks!")
50845#@gui : Smoothness = float(1,0,5)
50846#@gui : Color = color(200,220,255)
50847#@gui : Background = color(255,255,255,255)
50848#@gui : X-Shadow= float(2,0,10)
50849#@gui : Y-Shadow= float(2,0,10)
50850#@gui : Shadow Smoothness = float(1,0,5)
50851#@gui : Stationary Frames = _int(19,1,32)
50852#@gui : sep = separator()
50853#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/01/09</i>.</small>")
50854fx_text_pointcloud3d :
50855  W={w} H={h} M={round(1.5*max(w,h))} rm
50856  text_pointcloud3d "$2","$3",$4
50857  col3d. ${5-7} *3d. {0.7*$M}
50858  f3d 4000 db3d 0 m3d
50859  repeat $1
50860    rprogress {60*$>/$1}
50861    angle={$>*360/$1}
50862    +r3d[0] 1,0,1,$angle
50863    $M,$M,1,3,-1 j3d. ..,50%,50%,0,1 rm..
50864  done
50865  rm[0] a z autocrop -1 to_rgba s z replace_color 0,0,-1,-1,-1,255,0,0,0,0
50866  if $11 N=$! repeat $! l[$>] rprogress {60+40*$>/$N}
50867    i[0] 100%,100%,1,4 fc[0] ${8-11} +channels. 3,3 +negate. b[-2,-1] $14% to_rgba.
50868    j[0] .,$12%,$13%,0,0,1,..,255 rm[-2,-1] blend alpha
50869  endl done fi
50870  if $W>$H r2dx $W else r2dy $H fi
50871  if $15>1
50872    i[{int($1/2)}] [{int($1/2)}]x{$15-1}
50873    i[0] [0]x{$15-1}
50874  fi
50875
50876fx_text_pointcloud3d_preview :
50877  fx_text_pointcloud3d 4,"$2","$3",$4,${5-7},${8-11},${12-14},1 drgba
50878  frame 1,1,0 append_tiles 2,2
50879
50880#@gui 3D Tiles : fx_transition3d, fx_transition3d_preview(0)
50881#@gui : Inter-Frames = _int(10,3,100)
50882#@gui : X-Tiles = int(8,1,64)
50883#@gui : Y-Tiles = int(8,1,64)
50884#@gui : X-Rotation = text("1")
50885#@gui : Y-Rotation = text("1")
50886#@gui : Z-Rotation = text("0")
50887#@gui : Focale = float(800,100,2000)
50888#@gui : Enable Antialiasing = bool(1)
50889#@gui : sep = separator()
50890#@gui : note = note{"<small><b>Note:</b>
50891#@gui : This filter needs two layers to work properly. Set the <i>Input layers</i> option to handle
50892#@gui : multiple input layers.
50893#@gui : </small>"}
50894#@gui : sep = separator()
50895#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2012/13/08</i>.</small>")
50896fx_transition3d :
50897  f3d $7
50898  transition3d $1,$2,$3,"$4","$5","$6",$8
50899
50900fx_transition3d_preview :
50901  if $!==1 gui_warning_preview "Missing input layer" return fi
50902  f3d $7
50903  k[0,1] transition3d 4,$2,$3,"$4","$5","$6",$8
50904  k[1,2]
50905  r[0] 50%,100%,1,100%,0
50906  r[1] 50%,100%,1,100%,0,0,1
50907  a x
50908  line 50%,0,50%,100%,1,0,0,0,255
50909
50910#@gui B&W Pencil [Animated] : fx_animate_pencilbw, fx_animate_pencilbw_preview(0)
50911#@gui : Frames = _int(10,2,100)
50912#@gui : Output Frames = _bool(1)
50913#@gui : Output Files = _bool(0)
50914#@gui : Output Folder = _folder()
50915#@gui : note = note{"\n<b>Starting Parameters :</b>"}
50916#@gui : Pencil Type = float(2.3,0,5)
50917#@gui : Amplitude = float(100,0,200)
50918#@gui : note = note{"\n<b>Ending Parameters :</b>"}
50919#@gui : Pencil Type = float(0.3,0,5)
50920#@gui : Amplitude = float(60,0,200)
50921#@gui : sep = separator()
50922#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50923fx_animate_pencilbw : skip "${4=}"
50924  if $3 filename="$4/gmic_pencilbw.png" else filename="" fi
50925  animate pencilbw,"${5-6}",\
50926                   "${7-8}",$1,$2,{``$filename}
50927
50928fx_animate_pencilbw_preview : skip "${4=}"
50929  fx_animate_preview pencilbw,"${5-6}",\
50930                                 "${7-8}"
50931
50932#@gui B&W Stencil [Animated] : fx_animate_stencilbw, fx_animate_stencilbw_preview(1)
50933#@gui : Frames = _int(10,2,100)
50934#@gui : Output Frames = _bool(1)
50935#@gui : Output Files = _bool(0)
50936#@gui : Output Folder = _folder()
50937#@gui : note = note{"\n<b>Starting Parameters :</b>"}
50938#@gui : Edge Threshold = float(10,0,30)
50939#@gui : Smoothness = float(10,0,30)
50940#@gui : note = note{"\n<b>Ending Parameters :</b>"}
50941#@gui : Edge Threshold = float(10,0,30)
50942#@gui : Smoothness = float(20,0,30)
50943#@gui : sep = separator()
50944#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50945fx_animate_stencilbw : skip "${4=}"
50946  if $3 filename="$4/gmic_stencilbw.png" else filename="" fi
50947  animate stencilbw,"${5-6}",\
50948                    "${7-8}",$1,$2,{``$filename}
50949
50950fx_animate_stencilbw_preview : skip "${4=}"
50951  fx_animate_preview stencilbw,"${5-6}",\
50952                                  "${7-8}"
50953
50954#@gui Cartoon [Animated] : fx_animate_cartoon, fx_animate_cartoon_preview(0)
50955#@gui : Frames = _int(10,2,100)
50956#@gui : Output Frames = _bool(1)
50957#@gui : Output Files = _bool(0)
50958#@gui : Output Folder = _folder()
50959#@gui : note = note{"\n<b>Global Parameters :</b>"}
50960#@gui : Color Quantization = int(4,2,256)
50961#@gui : note = note{"\n<b>Starting parameters :</b>"}
50962#@gui : Smoothness = float(0.5,0,2)
50963#@gui : Sharpening = float(200,0,400)
50964#@gui : Edge Threshold = float(10,1,30)
50965#@gui : Edge Thickness = float(0.1,0,1)
50966#@gui : Color Strength = float(1.5,0,3)
50967#@gui : note = note{"\n<b>Ending parameters :</b>"}
50968#@gui : Smoothness = float(3,0,2)
50969#@gui : Sharpening = float(200,0,400)
50970#@gui : Edge Threshold = float(10,1,30)
50971#@gui : Edge Thickness = float(0.1,0,1)
50972#@gui : Color Strength = float(1.5,0,3)
50973#@gui : sep = separator()
50974#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50975fx_animate_cartoon : skip "${4=}"
50976  if $3 filename="$4/gmic_cartoon.png" else filename="" fi
50977  animate cartoon,"${6-10},$5",\
50978                  "${11-15},$5",$1,$2,{``$filename}
50979
50980fx_animate_cartoon_preview : skip "${4=}"
50981  fx_animate_preview cartoon,"${6-10},$5",\
50982                                "${11-15},$5"
50983
50984#@gui Edges [Animated] : fx_animate_edges, fx_animate_edges_preview(0)
50985#@gui : Frames = _int(10,2,100)
50986#@gui : Output Frames = _bool(1)
50987#@gui : Output Files = _bool(0)
50988#@gui : Output Folder = _folder()
50989#@gui : note = note{"\n<b>Global Parameters :</b>"}
50990#@gui : Negative Colors = bool(0)
50991#@gui : note = note{"\n<b>Starting Parameters :</b>"}
50992#@gui : Smoothness = float(0,0,10)
50993#@gui : Edge Threshold = float(10,0,30)
50994#@gui : note = note{"\n<b>Ending Parameters :</b>"}
50995#@gui : Smoothness = float(0,0,10)
50996#@gui : Edge Threshold = float(30,0,30)
50997#@gui : sep = separator()
50998#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
50999fx_animate_edges : skip "${4=}"
51000  if $3 filename="$4/gmic_edges.png" else filename="" fi
51001  animate fx_edges,"${6-7},$5",\
51002                   "${8-9},$5",$1,$2,{``$filename}
51003
51004fx_animate_edges_preview : skip "${4=}"
51005  fx_animate_preview fx_edges,"${6-7},$5",\
51006                                   "${8-9},$5"
51007
51008#@gui Edges on Fire : fx_fire_edges, fx_fire_edges_preview(0)
51009#@gui : Edges = float(0.7,0,3)
51010#@gui : Attenuation = float(0.25,0,1)
51011#@gui : Smoothness = float(0.5,0,5)
51012#@gui : Threshold = float(25,0,100)
51013#@gui : sep = separator()
51014#@gui : Number of Frames = _int(20,1,999)
51015#@gui : Starting Frame = int(20,0,199)
51016#@gui : Frame Skip = _int(0,0,20)
51017#@gui : sep = separator()
51018#@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal",
51019#@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right",
51020#@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
51021#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
51022#@gui : sep = separator()
51023#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/06/07</i>.</small>")
51024fx_fire_edges :
51025  fire_edges ${1-7} rv
51026
51027fx_fire_edges_preview :
51028  gui_split_preview "fire_edges $1,$2,$3,$4,1,$6,0",${-3--1}
51029
51030#@gui Lava Lamp : fx_lavalampbw, fx_lavalampbw_preview(0)
51031#@gui : Number of Key-Frames = _int(3,2,50)
51032#@gui : Number of Inter-Frames = _int(30,2,100)
51033#@gui : Smooth Looping = _bool(1)
51034#@gui : sep = separator()
51035#@gui : Resolution = float(20,1,100)
51036#@gui : Size = float(2,0,30)
51037#@gui : Smoothness = _float(0.01,0,1)
51038#@gui : Transparent Background = bool(0)
51039#@gui : sep = separator()
51040#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/06/07</i>.</small>")
51041fx_lavalampbw :
51042  if !$! (255;100^64;16^128;0) r. 512,512,1,3,3 fi
51043  repeat $! l[$<] remove_opacity
51044    w={w} h={h}
51045    +r $4%,$4%,1,1,0 [-1]x{$1-1} rand[^0] 0,1 stencil[^0] $5,0
51046    if $3 [1] fi
51047    morph[^0] $2,$6,0
51048    stencil[^0] $5,0
51049    r[^0] $w,$h,1,1,3 b[^0] 10 >=[^0] 50% *[^0] 255
51050    r[^0] 100%,100%,1,4 j[^0] [0] rm[0]
51051    if $3 rm. fi
51052  endl done
51053  if !$7 repeat $! l[$>] split_opacity n. 0,1 *[^-1] . rm. endl done fi
51054
51055fx_lavalampbw_preview :
51056  fx_lavalampbw 2,2,1,$4,$5,$6,$7 k[0]
51057
51058#@gui Lissajous [Animated] : fx_animate_lissajous, fx_animate_lissajous_preview(1)
51059#@gui : Frames = _int(10,2,100)
51060#@gui : Output as Frames = _bool(1)
51061#@gui : Output as Files = _bool(0)
51062#@gui : Output Folder = _folder()
51063#@gui : sep = separator()
51064#@gui : note = note{"<b>Starting parameters :</b>"}
51065#@gui : Resolution = int(4096,2,8192)
51066#@gui : X-Size = float(0.9,0,2)
51067#@gui : Y-Size = float(0.9,0,2)
51068#@gui : Z-Size = float(3,1,10)
51069#@gui : X-Multiplier = float(8,0,32)
51070#@gui : Y-Multiplier = float(7,0,32)
51071#@gui : Z-Multiplier = float(0,0,32)
51072#@gui : X-Offset = float(0,0,1)
51073#@gui : Y-Offset = float(0,0,1)
51074#@gui : Z-Offset = float(0,0,1)
51075#@gui : X-Angle = float(0,0,360)
51076#@gui : Y-Angle = float(0,0,360)
51077#@gui : Z-Angle = float(0,0,360)
51078#@gui : Thickness = float(0,0,50)
51079#@gui : Color = color(255,255,255,255)
51080#@gui : sep = separator()
51081#@gui : note = note{"<b>Ending parameters :</b>"}
51082#@gui : Resolution = int(4096,2,8192)
51083#@gui : X-Size = float(0.9,0,2)
51084#@gui : Y-Size = float(0.9,0,2)
51085#@gui : Z-Size = float(3,1,10)
51086#@gui : X-Multiplier = float(8,0,32)
51087#@gui : Y-Multiplier = float(7,0,32)
51088#@gui : Z-Multiplier = float(0,0,32)
51089#@gui : X-Offset = float(0,0,1)
51090#@gui : Y-Offset = float(0,0,1)
51091#@gui : Z-Offset = float(0,0,1)
51092#@gui : X-Angle = float(0,0,360)
51093#@gui : Y-Angle = float(0,0,360)
51094#@gui : Z-Angle = float(0,0,360)
51095#@gui : Thickness = float(0,0,50)
51096#@gui : Color = color(255,255,255,255)
51097#@gui : sep = separator()
51098#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/18/04</i>.</small>")
51099fx_animate_lissajous : skip "${4=}"
51100  if $3 filename="$4/gmic_lissajous.png" else filename="" fi
51101  animate fx_lissajous,"${5-22}",\
51102                       "${23-40}",$1,$2,{``$filename}
51103
51104fx_animate_lissajous_preview : skip "${4=}"
51105  fx_animate_preview fx_lissajous,"${5-22}",\
51106                                  "${23-40}",0
51107
51108#@gui Moir&eacute; Animation : fx_moire, fx_moire_preview(1)+ : *
51109#@gui : Stripe orientation = choice(1,"Horizontal","Vertical")
51110#@gui : Input Transparency = choice("Replace With White","Reconstruct From Previous Frames")
51111#@gui : Output Format = choice{2,"Same as Input","A4 / 75 PPI","A4 / 100 PPI (Recommended)",
51112#@gui : "A4 / 150 PPI","A4 / 300 PPI"}
51113#@gui : Auto-Reduce Number of Frames = bool(1)
51114#@gui : Landscape = bool(1)
51115#@gui : Margin (%) = float(2,0,30)
51116#@gui : sep = separator()
51117#@gui : Print Frame Numbers = choice(1,"Disable","Top Left","Top Right","Bottom Left","Bottom Right")
51118#@gui : Size of Frame Numbers (%) = float(5,0,30)
51119#@gui : sep = separator()
51120#@gui : note = note{"<small><b><span color="#EE5500">Instructions:</span></b>\n\n
51121#@gui : This filter renders Moire Animations, as shown on
51122#@gui : <a href="https://www.youtube.com/watch?v=f5plDb_JRq4">this video</a>.\n
51123#@gui : To make the animation visible:\n\n
51124#@gui : &bull; Before running the filter, ensure that all frames are aligned and have the same size
51125#@gui : (and preferably without alpha)!\n
51126#@gui : &bull; Run the filter. It is recommended to keep the number of frames &lt;=6.\n
51127#@gui : &bull; Print the first layer (merged frames) on a A4 blank paper, at 300 PPI.\n
51128#@gui : &bull; Print the second layer (mask) on a A4 transparent sheet, at 300 PPI.\n
51129#@gui : &bull; Drag the transparent layer over the A4 paper to render the animation effect.
51130#@gui : "}
51131#@gui : sep = separator()
51132#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/03/02</i>.</small>")
51133fx_moire :
51134  if 0$_is_preview" && "!narg($_preview_width) _preview_width,_preview_height=512 fi
51135
51136  # Reconstruct frames from base image.
51137  if $2 repeat $! if $>" && "{$<,s==2||s==4} blend[$<] [{$<+1}],alpha,1,1 fi done fi
51138
51139  # Auto-reduce number of frames (4, 5 or 6).
51140  if $4
51141    skip={"
51142      l<=6?1:(
51143        const l5 = int(l/5);
51144        const l4 = int(l/4);
51145        const l6 = int(l/6);
51146        5*l5==l?l5:
51147        6*l6==l?l6:
51148        4*l4==l?l4:(
51149          const r6 = abs(6*l6 - l);
51150          const r5 = abs(5*l5 - l);
51151          const r4 = abs(4*l4 - l);
51152          r5<=r4 && r5<=r6?l5:
51153          r6<=r4 && r6<=r5?l6:
51154          l4
51155        )
51156      )"}
51157    k[^:$skip]
51158    if $!>6 rm[6--1] fi
51159  fi
51160  N=$!
51161
51162  # Blend with white background.
51163  repeat $! l[$>] if s==2||s==4 drgba 255 fi endl done
51164
51165  # Constrain size of frames.
51166  if 0$_is_preview
51167    W,H=$_preview_width,$_preview_height
51168    if $3
51169      if $W>$H W={$H/sqrt(2)} else H={$W*sqrt(2)} fi
51170      fW,fH=$W,$H
51171      W,H/={arg($3,4,3,2,1)}
51172    fi
51173  elif $3
51174    W,H,fW,fH={"$3==1?[620,877]:$3==2?[827,1169]:$3==3?[1240,1754]:[2480,3508]"},2480,3508
51175    W,H,fW,fH={round([$W,$H,$fW,$fH]*0.95)} # Take printer margins into account
51176  else
51177    W,H=${-max_wh}
51178  fi
51179
51180  if $3" && "$5 # Landscape mode
51181    W,H=$H,$W fW,fH=$fH,$fW
51182  fi
51183  if !narg($fW) fW,fH=$W,$H fi
51184  repeat $! l[$>] if [w,h]!=[$W,$H]
51185    - 255
51186    rr2d {(100-$6)%*[$W,$H]},2,2
51187    r $W,$H,1,100%,0,0,0.5,0.5
51188    + 255
51189  fi endl done
51190
51191  # Generate merged image and mask.
51192  if $1 # Vertical stripes
51193    100%,100%,1,100%,"i(#x%"$N",x,y,z,c)" rm[^-1]
51194    100%,100%,1,1,"x%"$N"?0:255"
51195  else # Horizontal stripes
51196    100%,100%,1,100%,"i(#y%"$N",x,y,z,c)" rm[^-1]
51197    100%,100%,1,1,"y%"$N"?0:255"
51198  fi
51199
51200  # Upscale with nearest-neighbor for printing.
51201  if $3
51202    ir={round(max($fW/$W,$fH/$H))*100}
51203    r $ir%,$ir%,1,100%,1
51204    r $fW,$fH,1,100%,0,1,0.5,0.5
51205  fi
51206
51207  # Insert frame number label.
51208  if $7" && "$8
51209    ax,ay={a=$7-1;[a%2?0.95:0.05,int(a/2)?0.95:0.05]}
51210    0 t. "#"$N,0,0,{0,h*$8%},1,255 autocrop. frame. 5%,15%,0 negate. to_rgb. j[^-1] .,$ax~,$ay~ rm.
51211  fi
51212  nm "name(Merged Frames),pos(0,0),opacity(100),mode(alpha)","name(Mask),pos(0,0),opacity(100),mode(alpha)"
51213  u $N
51214
51215fx_moire_preview :
51216  _is_preview=1
51217  fx_moire $*
51218
51219#@gui Rodilius [Animated] : fx_animate_rodilius, fx_animate_rodilius_preview(1)
51220#@gui : Frames = _int(10,2,100)
51221#@gui : Output as Frames = _bool(1)
51222#@gui : Output as Files = _bool(0)
51223#@gui : Output Folder = _folder()
51224#@gui : Color Mode = choice(1,"Darker","Lighter")
51225#@gui : note = note{"\n<b>Starting Parameters :</b>"}
51226#@gui : Amplitude = float(10,0,30)
51227#@gui : Thickness = float(10,0,100)
51228#@gui : Sharpness = float(300,0,1000)
51229#@gui : Orientations = int(5,2,20)
51230#@gui : Offset = float(0,0,180)
51231#@gui : note = note{"\n<b>Ending Parameters :</b>"}
51232#@gui : Amplitude = float(10,0,30)
51233#@gui : Thickness = float(10,0,100)
51234#@gui : Sharpness = float(300,0,1000)
51235#@gui : Orientations = int(5,2,20)
51236#@gui : Offset = float(180,0,180)
51237#@gui : sep = separator()
51238#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
51239fx_animate_rodilius : skip "${4=}"
51240  if $3 filename="$4/gmic_rodilius.png" else filename="" fi
51241  animate rodilius,"${6-10},$5",\
51242                   "${11-15},$5",$1,$2,{``$filename}
51243
51244fx_animate_rodilius_preview : skip "${4=}"
51245  fx_animate_preview rodilius,"${6-10},$5",\
51246                              "${11-15},$5"
51247
51248#@gui Soft Glow [Animated] : fx_animate_glow, fx_animate_glow_preview(1)
51249#@gui : Frames = _int(10,2,100)
51250#@gui : Output as Frames = _bool(1)
51251#@gui : Output as Files = _bool(0)
51252#@gui : Output Folder = _folder()
51253#@gui : note = note{"\n<b>Starting Parameters :</b>"}
51254#@gui : Amplitude = float(0,0,8)
51255#@gui : note = note{"\n<b>Ending Parameters :</b>"}
51256#@gui : Amplitude = float(3,0,8)
51257#@gui : sep = separator()
51258#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
51259fx_animate_glow : skip "${4=}"
51260  if $3 filename="$4/gmic_glow.png" else filename="" fi
51261  animate glow,"$5",\
51262               "$6",$1,$2,{``$filename}
51263
51264fx_animate_glow_preview : skip "${4=}"
51265  fx_animate_preview glow,"$5",\
51266                          "$6"
51267
51268#@gui Spatial Transition : fx_spatial_transition, fx_spatial_transition_preview(1)
51269#@gui : Number of Added Frames = _int(10,1,256)
51270#@gui : Shading (%) = float(0,0,100)
51271#@gui : Transition Shape = choice(7,"Bottom Layer","Top Layer","Custom Formula","Horizontal","Vertical",
51272#@gui : "Angular","Radial","Plasma")
51273#@gui : Custom Formula = text{"cos(x*y/(16+32*A))"}_1
51274#@gui : A-Value = float(0,0,1)
51275#@gui : sep = separator()
51276#@gui : Preview Type = choice(1,"Transition Map","Timed Image","Sequence x4","Sequence x6","Sequence x8")
51277#@gui : Preview Time = float(0.5,0,1)
51278#@gui : Preview = value(0)
51279#@gui : sep = separator()
51280#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/10/04</i>.</small>")
51281fx_spatial_transition :
51282  to_rgba r ${-max_wh},1,100%,0,0,0.5,0.5
51283  shape=-1 formula=
51284  if $3==0 # Do nothing.
51285  elif $3==1 shape=0
51286  elif $3==2 formula="$4"
51287  elif $3==3 formula="sin(x*0.5*pi/w*(1+100*A))"
51288  elif $3==4 formula="sin(y*0.5*pi/h*(1+100*A))"
51289  elif $3==5 formula="atan2(y-h/2,x-w/2)%((1-A)*2*pi+0.001)"
51290  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))"
51291  elif $3==7 100%,100% plasma. 1,1,{8/(1+$5)} equalize. 1024
51292  fi
51293  if narg($formula)
51294    {w},{h},1,1,"A=$5;"$formula fi
51295  if $-1 # Preview mode.
51296    if $6==0 k[$shape] norm n 0,255
51297    elif $6==1" && "$7==0 rm[$shape] rm.
51298    elif $6==1" && "$7==1 rm[$shape] rm[0]
51299    elif $6==1
51300      transition[^$shape] [$shape],$1,$2,$7*($1-1) rm[$shape] rm[0--1:2]
51301    else
51302      transition[^$shape] [$shape],{$6*2},$2 rm[$shape] to_rgba
51303    fi
51304    if $!>1 to_rgba frame 2%,2%,0,0,0,0 append_tiles , fi
51305  else # Apply mode.
51306    transition[^$shape] [$shape],$1,$2
51307    rm.
51308  fi
51309  nm name(transition),pos(0,0)
51310  if narg($formula) u "{$1}{$2}{$3}{"$formula"}_"{$3==2?2:1}"{$5}{$6}{$7}{0}" fi
51311
51312fx_spatial_transition_preview :
51313  if ($3<=1" && "$!<3)" || "($3>1" && "$!<2)
51314    gui_print_preview "Warning:",,"This filter requires more input layers to work properly."
51315    return
51316  fi
51317  fx_spatial_transition ${1-3},"$4",${5-7},1
51318
51319
51320#@gui ____<b>Silhouettes</b>
51321#---------------------------
51322
51323#@gui <i>Misc</i>
51324
51325#@gui Cupid : fx_cupid, fx_cupid_preview
51326#@gui : Size (%) = float(75,0,100)
51327#@gui : Smoothness = float(0,0,10)
51328#@gui : Color = color(255,255,255,255)
51329#@gui : Antialiasing = bool(1)
51330#@gui : sep = separator()
51331#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/08</i>.</small>")
51332fx_cupid :
51333  max_wh={$!>0?[${-max_wh}]:[512,512]}
51334  w,h={S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)]}
51335  l[]
51336    shape_cupid {($7?2:1)*min($w,$h)}
51337    if $7 r2dx 50% fi
51338    frame {2.5*$2}%,{2.5*$2}%,0 b $2% * $6 round c 0,255 autocrop
51339    100%,100%,1,3 fc. ${3-5} rv[-2,-1] a c
51340    gui_set_layer_name "Heart"
51341    gui_set_layer_pos {0.5*([$max_wh]-[w,h])}
51342  endl
51343  mv. 0
51344
51345fx_cupid_preview :
51346  fx_cupid $* blend[^0] [0],alpha rm[0]
51347
51348#@gui Gear : fx_gear, fx_gear_preview
51349#@gui : Size (%) = float(75,0,100)
51350#@gui : Number of Teeth = int(12,1,96)
51351#@gui : Elevation (%) = float(15,0,100)
51352#@gui : Angle (%) = float(0,0,100)
51353#@gui : Inner Radius (%) = float(40,0,100)
51354#@gui : Smoothness = float(0,0,10)
51355#@gui : Color = color(255,255,255,255)
51356#@gui : Antialiasing = bool(1)
51357#@gui : sep = separator()
51358#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/08</i>.</small>")
51359fx_gear :
51360  max_wh={$!>0?[${-max_wh}]:[512,512]}
51361  w,h={S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)]}
51362  l[]
51363    shape_gear {($11?2:1)*min($w,$h)},${2-5}
51364    if $11 r2dx 50% fi
51365    frame {2.5*$6}%,{2.5*$6}%,0 b $6% * $10 round c 0,255 autocrop
51366    100%,100%,1,3 fc. ${7-9} rv[-2,-1] a c
51367    gui_set_layer_name "Gear"
51368    gui_set_layer_pos {0.5*([$max_wh]-[w,h])}
51369  endl
51370  mv. 0
51371
51372fx_gear_preview :
51373  fx_gear $* blend[^0] [0],alpha rm[0]
51374
51375#@gui Heart : fx_heart, fx_heart_preview
51376#@gui : Size (%) = float(75,0,100)
51377#@gui : Smoothness = float(0,0,10)
51378#@gui : Color = color(255,255,255,255)
51379#@gui : Antialiasing = bool(1)
51380#@gui : sep = separator()
51381#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2018/01/08</i>.</small>")
51382fx_heart :
51383  max_wh={$!>0?[${-max_wh}]:[512,512]}
51384  w,h={S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)]}
51385  l[]
51386    shape_heart {($7?2:1)*min($w,$h)}
51387    if $7 r2dx 50% fi
51388    frame {2.5*$2}%,{2.5*$2}%,0 b $2% * $6 round c 0,255 autocrop
51389    100%,100%,1,3 fc. ${3-5} rv[-2,-1] a c
51390    gui_set_layer_name "Heart"
51391    gui_set_layer_pos {0.5*([$max_wh]-[w,h])}
51392  endl
51393  mv. 0
51394
51395fx_heart_preview :
51396  fx_heart $* blend[^0] [0],alpha rm[0]
51397
51398#@gui Sierpinski Triangle : fx_sierpinski, fx_sierpinski(1)
51399#@gui : Recursions = int(6,0,10)
51400#@gui : 1st X-Coord = float(50,0,100)
51401#@gui : 1st Y-Coord = float(0,0,100)
51402#@gui : 2nd X-Coord = float(0,0,100)
51403#@gui : 2nd Y-Coord = float(100,0,100)
51404#@gui : 3rd X-Coord = float(100,0,100)
51405#@gui : 3rd Y-Coord = float(100,0,100)
51406#@gui : Color = color(255,255,255)
51407#@gui : Opacity = float(1,0,1)
51408#@gui : sep = separator()
51409#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
51410fx_sierpinski :
51411  repeat $! l[$>] split_opacity l[0]
51412    100%,100% sierpinski. ${1-7}
51413    +fc.. $8,$9,$10 j[0] .,0,0,0,0,$11,..,255 rm[-2,-1]
51414  endl a c endl done
51415
51416#@gui _<i>Nature</i>
51417
51418#@gui Barnsley Fern : fx_barnsley_fern, fx_barnsley_fern_preview(1)
51419#@gui : Type = choice("Asplenium Adiantum-Nigrum","Thelypteridaceae")
51420#@gui : Density (%) = float(100,0,300)
51421#@gui : Angle = float(30,-180,180)
51422#@gui : Opacity (%) = float(40,0,100)
51423#@gui : Color = color(10,178,0,255)
51424#@gui : Add as a New Layer = _bool(1)
51425#@gui : sep = separator()
51426#@gui : note = note("This filter renders the Barnsley fern fractal, described here:")
51427#@gui : url = link("https://en.wikipedia.org/wiki/Barnsley_fern")
51428#@gui : sep = separator()
51429#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/18/10</i>.</small>")
51430fx_barnsley_fern :
51431  repeat $! l[$<]
51432    shape_fern {min(w,h)},$2%,$3,{$4%},$1 *. 255
51433    100%,100%,1,3,[${5-7}]
51434    rv[-2,-1] a[-2,-1] c
51435    if !$9 blend alpha,{$8/255}
51436    else nm. "name(Barnsley Fern),opacity("{round($8*100/255)})")" rv[-2,-1]
51437    fi
51438  endl done
51439
51440fx_barnsley_fern_preview :
51441  fx_barnsley_fern ${1-8},0
51442
51443#@gui Snowflake : fx_snowflake, fx_snowflake(1)
51444#@gui : Recursions = int(5,0,6)
51445#@gui : Opacity = float(1,0,1)
51446#@gui : Color = color(255,255,255)
51447#@gui : sep = separator()
51448#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
51449fx_snowflake :
51450  repeat $! l[$>] to_color split_opacity l[0]
51451    shape_snowflake {min(w,h)},$1 100%,100%,1,3,[${3-5}]
51452    j[0] .,{([w#0,h#0]-[w#1,h#1])/2},0,0,$2,.. k[0]
51453  endl a c endl done
51454
51455#@gui _<i>Others</i>
51456
51457#@gui Dragon Curve : fx_dragoncurve, fx_dragoncurve(1)
51458#@gui : Recursions = int(20,0,30)
51459#@gui : Angle = float(0,-180,180)
51460#@gui : Opacity = float(1,0,1)
51461#@gui : Color = color(255,255,255)
51462#@gui : sep = separator()
51463#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2019/01/29</i>.</small>")
51464fx_dragoncurve :
51465  repeat $! l[$>] to_color split_opacity l[0]
51466    shape_dragon {min(w,h)},$1,$2 100%,100%,1,3,[${4-6}]
51467    j[0] .,{([w#0,h#0]-[w#1,h#1])/2},0,0,$3,.. k[0]
51468  endl a c endl done
51469
51470#@gui ____<b>Various</b>
51471#------------------------
51472
51473#@gui Custom Code [Global] : fx_custom_code, fx_custom_code_preview(1)
51474#@gui : Code = text(1,"repeat $! l[$>]\n\n  to_rgb\n  +deform 20\n  blend_edges 3\n\nendl done\n\n\n")
51475#@gui : sep = separator()
51476#@gui : Channel(s) = choice{"None (Allows Multi-layers)","All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]",
51477#@gui : "RGB [Blue]","RGBA [Alpha]","Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]",
51478#@gui : "YCbCr [Luminance]","YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
51479#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
51480#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
51481#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
51482#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]"}
51483#@gui : Value Action = choice("None","Cut","Normalize")
51484#@gui : Display Debug Info on Preview = bool(0)
51485#@gui : Debug Font Size = choice(2,"Tiny","Small","Normal","Large")
51486#@gui : sep = separator()
51487#@gui : Preview Type = choice{"Full (Allows Multi-Layers)","Forward Horizontal","Forward Vertical",
51488#@gui : "Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom",
51489#@gui : "Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"}
51490#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
51491#@gui : sep = separator()
51492#@gui : note = note{"<small><b>Note: </b>
51493#@gui : This filter can execute any set of instructions understood by the <b>G'MIC</b> language interpreter.
51494#@gui : Here, you can then test some commands before creating your own G'MIC custom commands and plug-in
51495#@gui : menu entries.\n\n
51496#@gui : Please look at the documentation reference web page :</small>"}
51497#@gui : url = link("https://gmic.eu/reference/")
51498#@gui : note = note{"<small>
51499#@gui : to learn more about available <b>G'MIC</b> commands.
51500#@gui : </small>"}
51501#@gui : sep = separator()
51502#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/03/10</i>.</small>")
51503
51504#@gui Custom Code [Local] : fx_custom_code, fx_custom_code_preview(0)
51505#@gui : Code = text(1,"repeat $! l[$>]\n\n  to_rgb\n  +deform 20\n  blend_edges 3\n\nendl done\n\n\n")
51506#@gui : sep = separator()
51507#@gui : Channel(s) = choice{"None (Allows Multi-layers)","All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]",
51508#@gui : "RGB [Blue]","RGBA [Alpha]","Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]",
51509#@gui : "YCbCr [Luminance]","YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
51510#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
51511#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
51512#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
51513#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]"}
51514#@gui : Value Action = choice("None","Cut","Normalize")
51515#@gui : Display Debug Info on Preview = bool(0)
51516#@gui : Debug Font Size = choice(2,"Tiny","Small","Normal","Large")
51517#@gui : sep = separator()
51518#@gui : Preview Type = choice{"Full (Allows Multi-Layers)","Forward Horizontal","Forward Vertical",
51519#@gui : "Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom",
51520#@gui : "Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"}
51521#@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0
51522#@gui : sep = separator()
51523#@gui : note = note{"<small><b>Note: </b>
51524#@gui : This filter can execute any set of instructions understood by the <b>G'MIC</b> language interpreter.
51525#@gui : Here, you can then test some commands before creating your own G'MIC custom commands and
51526#@gui : plug-in menu entries.\n\n
51527#@gui : Please look at the documentation reference web page :</small>"}
51528#@gui : url = link("https://gmic.eu/reference/")
51529#@gui : note = note{"<small>
51530#@gui : to learn more about available <b>G'MIC</b> commands.
51531#@gui : </small>"}
51532#@gui : sep = separator()
51533#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/03/10</i>.</small>")
51534fx_custom_code : skip "${1=-skip ,}"
51535  ({'{/"$1"}'}) discard. 92,10 _gcp_arg={t} rm.
51536  m "_fx_custom_code_start : "$_gcp_arg
51537  if $4
51538    _nb_in=$!
51539    _dim_in="" sep=""
51540    repeat $! l[$>]
51541      _dim_in=$_dim_in$sep"["$>"] = "{w}x{h}x{d}x{s}", in ["{_round([im,iM],0.1)}"]" sep="\n"
51542    endl done
51543  fi
51544  if $2
51545    ac "_fx_custom_code_start _status_out=${}",{$2-1},$3
51546  else
51547    _fx_custom_code_start _status_out=${}
51548    if $3==1 c 0,255 elif $3==2 n 0,255 fi
51549  fi
51550  if $4
51551    _nb_out=$!
51552    _dim_out="" sep=""
51553    repeat $! l[$>]
51554      _dim_out=$_dim_out$sep"["$>"] = "{w}x{h}x{d}x{s}", in ["{_round([im,iM],0.1)}"]" sep="\n"
51555    endl done
51556  fi
51557  um _fx_custom_code_start
51558
51559fx_custom_code_preview : skip "${1=-skip ,}"
51560  w={w} h={h}
51561  l
51562    ({'{/"$1"}'}) discard. 92,10 _gcp_arg={t} rm.
51563    if $6 gui_split_preview "fx_custom_code $_gcp_arg,${2--2}",${-3--1}
51564    else fx_custom_code $_gcp_arg,${2--2}
51565    fi
51566  onfail
51567    error_msg=${}
51568    rr2d $w,$h,2,1
51569    gui_print_preview "Syntax error:",,{``$error_msg},20,40
51570  endl
51571  if $4 # Display debug infos on preview
51572    if !$3 % 256 fi
51573    if $!>1 gui_preview fi
51574    rr2d $_preview_width,$_preview_height,0,1
51575    siz0=13 siz1=17 siz2=19 siz3=22
51576    if ['$_status_out']==0 _status_out=(empty) fi
51577    info="Input images: "#$_nb_in"\n"\
51578         $_dim_in"\n\n"\
51579         "Output images: "#$_nb_out"\n"\
51580         $_dim_out"\n\n"\
51581         "Output status: "$_status_out
51582    0 t. {``$info},0,0,${siz$5},1,255 expand_xy. 5,0 +dilate. 3 a[-2,-1] c
51583    rr2d[^-1] ${-max_wh},2,2
51584    r. ..,..,1,100%,0 drgba[^-1]
51585    /[^-1] 2 blend[^-1] .,alpha rm.
51586  fi
51587
51588#@gui Export RGB-565 File : fx_output_565,_none_
51589#@gui : Filename = _fileout("out565.rgb")
51590#@gui : Reverse endianness = _bool(0)
51591#@gui : sep = separator()
51592#@gui : note = note{"<b>Note:</b> This filter saves your selected layer as a raw RGB-565 file. Keep in mind that
51593#@gui : you have to remember the image dimension if you want to reload the image file afterwards!"}
51594#@gui : sep = separator()
51595#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/05/03</i>.</small>")
51596fx_output_565 :
51597  output_565 "$1",$2
51598
51599#@gui Games & Demos : fx_gmic_demos, fx_gmic_demos_preview
51600#@gui : Selection = choice("2048","Blobs Editor","Bouncing Balls","Connect-Four","Fire Effect","Fireworks",
51601#@gui : "Fish-Eye Effect","Fourier Filtering","Hanoi Tower",
51602#@gui : "Histogram","Hough Transform","Jawbreaker","Virtual Landscape","The Game of Life","Light Effect",
51603#@gui : "Mandelbrot Explorer","3D Metaballs","Minesweeper",
51604#@gui : "Minimal Path","Pacman","Paint","Plasma Effect","RGB Quantization","3D Reflection","3D Rubber Object",
51605#@gui : "Shadebobs","Spline Editor",
51606#@gui : "3D Starfield","Tetris","Tic-Tac-Toe","3D Waves","Fractal Whirl")
51607#@gui : sep = separator()
51608#@gui : note = note("<small><b>Note:</b> This filter proposes a showcase of some interactive demos, all written
51609#@gui : as G'MIC scripts.</small>")
51610#@gui : note = note{"<small>On most demos, you can use the keyboard shortcut <b>CTRL+D</b> to double the window
51611#@gui : size (and <b>CTRL+C</b> to go back to the original size).
51612#@gui : Also, feel free to use the mouse buttons, as they are often used to perform an action.
51613#@gui : </small>"}
51614#@gui : sep = separator()
51615#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2014/10/09</i>.</small>")
51616fx_gmic_demos :
51617  coms=2048,blobs,bouncing,connect4,fire,fireworks,fisheye,fourier,hanoi,histogram,hough,jawbreaker,landscape,life,\
51618       light,mandelbrot,metaballs3d,minesweeper,minimal_path,pacman,paint,plasma,quantize_rgb,reflection3d,\
51619       rubber3d,shadebobs,spline,starfield3d,tetris,tictactoe,waves,whirl
51620  com=${arg\ {1+$1},$coms}
51621  if $!>0 sel=0 else sel= fi
51622  +l[$sel] m "foo : x_"$com foo rm um foo endl
51623
51624fx_gmic_demos_preview :
51625  rm input_cached img/gmic_demos.cimgz k[$1,-1] rows. $1 map[0] [1] k[0]
51626
51627#@gui Histogram Analysis : _none_, fx_display_histogram(1)
51628#@gui : Number of Clusters = int(256,2,1024)
51629#@gui : sep = separator()
51630#@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]",
51631#@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]",
51632#@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]",
51633#@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]",
51634#@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]",
51635#@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]",
51636#@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]")
51637#@gui : sep = separator()
51638#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2016/20/06</i>.</small>")
51639fx_display_histogram :
51640  mode=${arg\ 1+$2,all,rgba,rgb,rgb_r,rgb_g,rgb_b,rgba_a,\
51641             lrgb,lrgb_r,lrgb_g,lrgb_b,\
51642             ycbcr_y,ycbcr_cbcr,ycbcr_cb,ycbcr_cr,ycbcr_cg,\
51643             lab_l,lab_ab,lab_a,lab_b,\
51644             lch_ch,lch_c,lch_h,\
51645             hsv_h,hsv_s,hsv_v,hsi_i,hsl_l,\
51646             cmyk_c,cmyk_m,cmyk_y,cmyk_k,\
51647             yiq_y,yiq_iq}
51648  _ac_$mode m "_ac_precond : "$_p m "_ac_forward : "$_f m "_ac_backward : "$_b
51649  repeat $! l[$>]
51650    _ac_precond _ac_forward[0] channels $_s
51651    display_histogram {w},{h},$1,0,255
51652    if s==2" || "s==4 channels 0,2 fi
51653  endl done
51654
51655#@gui Import Data : fx_import_image, gui_no_preview
51656#@gui : Filename = filein()
51657#@gui : Normalize = bool(1)
51658#@gui : note = note{"\n<small><b>Note: </b>
51659#@gui : This filter can import any image data read by the <b>G'MIC</b> language interpreter.
51660#@gui : It includes exotic formats as : <i>Pandore, CImg, Inrimage, AVI/MPEG (requires FFMPEG installed), ...</i>
51661#@gui : </small>"}
51662#@gui : sep = separator()
51663#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2010/29/12</i>.</small>")
51664fx_import_image : skip "${1=}"
51665  rm i "$1" s z if $2 n 0,255 else c 0,255 fi
51666
51667#@gui Import RGB-565 File : fx_input_565
51668#@gui : Filename = filein()
51669#@gui : Width = text("800")
51670#@gui : Height = text("600")
51671#@gui : Reverse endianness = bool(0)
51672#@gui : sep = separator()
51673#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2020/05/03</i>.</small>")
51674fx_input_565 : skip "${1=}"
51675  l[] check "isint($2) && $2>0 && isint($3) && $3>0"
51676  onfail error "Invalid Specified Dimensions"
51677  endl
51678  if ['"$1"']==0 gui_warning_preview "Choose a filename"
51679  elif !isfile(['"$1"']) gui_warning_preview "Filename not found!"
51680  else input_565 "$1",${2-4} mv. 0
51681  fi
51682
51683#@gui Intarsia : fx_intarsia, fx_intarsia_preview
51684#@gui : note = note{"<small><b>Note:</b>
51685#@gui : Intarsia is a method of Crochet/Knitting with a number of colours, in which a separate ball of yarn
51686#@gui : is used for each area of colour.
51687#@gui : This filter creates a HTML version of a graph chart which is solely used for this purpose
51688#@gui : </small>"}
51689#@gui : sep = separator()
51690#@gui : Output Directory = _folder("")
51691#@gui : Output HTML File = _text("intarsia.html")
51692#@gui : sep = separator()
51693#@gui : Maximum Image Size = int(512,2,1024)
51694#@gui : Maximum Number of Image Colors = _int(12,2,64)
51695#@gui : Starting Point = choice(1,"Top Left","Top Right","Bottom Left","Bottom Right")
51696#@gui : Loop Method = choice("Row by Row","Column by Column")
51697#@gui : sep = separator()
51698#@gui : Add Comment Area in HTML Page = _bool(1)
51699#@gui : Preview Progress (%) = float(100,0,100)
51700#@gui : sep = separator()
51701#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2015/09/07</i>.</small>")
51702fx_intarsia :
51703  to_rgb repeat $! nm=${gui_layer_name[$>]} +l[$>]
51704
51705    # Constrain image for size and number of colors, and index it with colormap.
51706    if max(w,h)>$3 rr2d $3,$3,0 fi
51707    +colormap 0
51708    if w>$4 rm. +colormap $4,1 fi
51709    round[1] index[0] [1]
51710
51711    # Output header and title.
51712    0 nm. $nm ('{b}') f. 'if(x,i,if(i>=97&&i<=122,i-32,i))' image_basename={t} rm[-2,-1]
51713    ('"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\
51714       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\
51715       <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\
51716       <head></head><body bgcolor=\"#FFFDFF\"><center><font size=\"-1\">\n\
51717       <h2>"$image_basename" ("{0,w}x{0,h}")</h2>\n\
51718       <table cellpadding=\"8\"><tr><td>\n\
51719       <table cellpadding=\"4\">\n"')
51720
51721    # Render image of colors.
51722    0 nm. "$2" image_name={b} rm.
51723    nb_cols={1,w}
51724    repeat $nb_cols
51725      color={1,I($>)}
51726      R={arg(1,$color)} G={arg(2,$color)} B={arg(3,$color)}
51727      ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'}
51728      f. if(i>=_'a'" && "i<=_'z',i+_'A'-_'a',i)
51729      hcolor={t} rm.
51730      48,32,1,4 fc. $color,255 frame. 1,1,0,0,0,255 o. "$1/"${image_name}_$>.png rm.
51731      ('"<tr><td><b>Colour "$>"</b></td><td><img src=\""${image_name}_$>.png"\" /></td><td>#"$hcolor"</td></tr>\n"')
51732    done
51733    ('"</table>\n</td><td>"')
51734
51735    # Render result and overview images.
51736    starting=${"arg {1+$5},\"Top left\",\"Top right\",\"Bottom left\",\"Bottom right\""}
51737    label=${"arg {1+$6},Row,Column"}
51738    if $6 dir0="T &#8594; B" dir1="B &#8594; T" else dir0="L &#8594; R" dir1="R &#8594; L" fi
51739    dir={arg(1+2*$5+$6,0,0,1,0,0,1,1,1)}
51740
51741    +map[0] [1]
51742    +fx_intarsia_preview. ${1-7},63 drgba.
51743    rr2d.. 200,200,1,1
51744    to_rgba[-2,-1] frame[-2,-1] 1,1,0,0,0,255 frame[-2,-1] 0,20,0,0,0,0
51745    t.. "Result",0,0,16,1,0,0,0,255
51746    t. "Ordering overview",0,0,16,1,0,0,0,255
51747    frame[-2,-1] 20,20,0,0,0,0
51748
51749    o.. "$1/"${image_name}_A.png
51750    o. "$1/"${image_name}_B.png
51751    rm[-2,-1]
51752
51753    ('"<table><tr><td><img src=\""${image_name}_A.png"\" /></td></tr><tr><td>"\
51754      "<img src=\""${image_name}_B.png"\" /></td></tr></table></td></tr></table>\n"')
51755    if $7 ('"<p><b>Additional comments:</b><br/><textarea cols=\"80\" rows=\"10\"
51756             placeholder=\"Enter comments here...\"></textarea></p>\n"') fi
51757    ('"<p><b>Starting point:</b> "$starting"\
51758                 <b>Orientation:</b> "$label" by "$label"</p>\n"')
51759    rm[1]
51760
51761    # Output geometry.
51762    ('"<table cellspacing=\"0\" cellpadding=\"4\" border=\"1\">\n"')
51763
51764    _fx_intarsia[0] $5,$6,0
51765    +l[0]
51766      s y
51767      repeat $! l[$>]
51768        if $>%2 mirror. x fi
51769        im={im} compress_rle 0,0 rows 6,100%
51770        ('"<tr><td valign=\"top\"><b>"$label" "{1+$>}"</b></td><td valign=\"top\">"${dir$dir}"</td><td>\n"')
51771        i=0 n=0 do
51772          val={0,i[$i]} i+=1
51773          if $val>=0 occ=1
51774          else
51775            occ={-$val}
51776            val={0,i[$i]}
51777            if $val<0 val=0 else i+=1 fi
51778          fi
51779          val+=$im
51780          ('"colour:<b>"$val"</b> "$occ')
51781          if {0,$i<h} ('", "') fi
51782          n+=1
51783          if !($n%8) ('"<br/>\n"') fi
51784        while $i<h#0
51785        ('"</td></tr>\n"')
51786        rm[0] a x
51787        dir={!$dir}
51788      endl done
51789      a x
51790    endl
51791    rm[0]
51792
51793    ('"</table>\n</font></center>\n</body>"')
51794    a x ot "$1/$2"
51795    rm
51796  endl done
51797
51798fx_intarsia_preview :
51799  to_rgb repeat $! l[$>]
51800    if max(w,h)>$3 rr2d $3,$3,0 fi
51801    to_rgba
51802    _fx_intarsia $5,$6,0
51803    100%,100%,1,1,'if(y%2,y*w+w-1-x,y*w+x)<$8*wh/100' *
51804    if min(w,h)<140 rr2d 140,140,1,1 fi
51805    expand_xy 16,0
51806
51807    100%,100% circle. 16,16,1%,1,1
51808    arrow3d. 0,0,0,{w/4},0,0,2%,15%,10% col3d. 1 j3d.. .,16,16,0,1,2,0,0 rm.
51809    +dilate. 3 r.. 100%,100%,1,3,0,0,0,0,0,0.5 a[-2,-1] c *. 255
51810    blend alpha
51811
51812    _fx_intarsia $5,$6,1
51813  endl done
51814
51815_fx_intarsia :
51816  if $3" && "$2 transpose fi
51817  if $1==0 # Start from top left.
51818  elif $1==1 # Start from top right.
51819    mirror x
51820  elif $1==2 # Start from bottom left.
51821    mirror y
51822  elif $1==3 # Start from bottom right.
51823    mirror xy
51824  fi
51825  if !$3" && "$2 transpose fi
51826
51827#@gui Sample Image : fx_image_sample, fx_image_sample_preview
51828#@gui : Input = choice{"Random","Apples","Balloons","Barbara","Boats","Bottles","Butterfly","Cameraman","Car","Cat",
51829#@gui : "Chick","Cliff","Colorful","David","Dog","Duck","Eagle","Elephant","Earth","Flower","Fruits",
51830#@gui : "Gmicky (Deevad)","Gmicky (Mahvin)","Gmicky & Wilber","Greece","Gummy","House","Inside","Landscape","Leaf",
51831#@gui : "Lena","Leno","Lion","Mandrill","Mona Lisa","Monkey","Parrots","Pencils","Peppers","Portrait0","Portrait1",
51832#@gui : "Portrait2","Portrait3","Portrait4","Portrait5","Portrait6","Portrait7","Portrait8","Portrait9",
51833#@gui : "Roddy","Rooster","Rose","Square","Swan","Teddy","Tiger","Tulips","Wall","Waterfall","Zelda"}
51834#@gui : note = note("<small>Choosing <b>0</b> for parameters <i>Width</i> or <i>Height</i> means <i>Automatic</i>.
51835#@gui : </small>")
51836#@gui : Width = _int(0,0,1024)
51837#@gui : Height = _int(0,0,1024)
51838#@gui : sep = separator()
51839#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2017/16/01</i>.</small>")
51840fx_image_sample :
51841  if $1 sp {$1-1},$2,$3 else sp ?,$2,$3 fi
51842  mv. 0
51843
51844fx_image_sample_preview :
51845  w={w} h={h} rm
51846  fx_image_sample $1,{$w>$h?$w:0},{$h>$w?$h:0}
51847
51848#@gui Solve Maze : fx_solve_maze, fx_solve_maze_preview(1)
51849#@gui : Starting Point (%) = point(5,5)
51850#@gui : Ending Point (%) = point(95,95)
51851#@gui : Smoothness = float(0.1,0,1)
51852#@gui : Thickness = int(3,1,10)
51853#@gui : Color = color(255,0,0)
51854#@gui : Maze Type = choice("Dark Walls","White Walls")
51855#@gui : sep = separator()
51856#@gui : note = note("<small>Author: <i>David Tschumperlé</i>.      Latest Update: <i>2011/01/09</i>.</small>")
51857fx_solve_maze :
51858  repeat $!
51859    +norm. >=. 50%
51860    if !$10 negate. fi
51861    *. 255 +b. $5% *.. 1e10 +[-2,-1]
51862    minimal_path. $1%,$2%,0,$3%,$4%,0 transpose.
51863    pointcloud. 0 dilate. $6 r. ..,..,1,1,0
51864    to_rgba.
51865    replace_color. 0,0,1,1,1,255,${7-9},255
51866    replace_color. 0,0,0,0,0,255,0,0,0,0
51867    ellipse. $1%,$2%,5,5,0,1,${7-9},255
51868    ellipse. $3%,$4%,5,5,0,1,${7-9},255
51869    rv[-2,-1]
51870  mv[-2,-1] 0 done
51871
51872fx_solve_maze_preview :
51873  drgba
51874  line $1%,$2%,$3%,$4%,1,0xCCCCCCCC,${7-9}
51875  ellipse $1%,$2%,3,3,0,1,${7-9}
51876  ellipse $1%,$2%,3,3,0,1,0x1,0
51877  ellipse $3%,$4%,3,3,0,1,${7-9}
51878  ellipse $3%,$4%,3,3,0,1,0x1,0
51879
51880#@gui _
51881
51882#---------------------------------
51883#
51884#@cli :: Additional Gallery Images
51885#
51886#---------------------------------
51887#@cli _gallery_arrays
51888#@cli : This entry defines some examples of array filters for the G'MIC gallery page.
51889#@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]"
51890#@cli : $ sample tiger,leno,monkey,duck,eagle frame 3,3,0 frame 3,3,255 montage A _label="Montage"
51891#@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"
51892
51893#@cli _gallery_artistic
51894#@cli : This entry defines some examples of artistic filters for the G'MIC gallery page.
51895#@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"
51896#@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"
51897#@cli : $ image.jpg fx_8bits 25,800,16,0 _label="Oldschool~8bits"
51898#@cli : $ image.jpg fire_edges 0.7,0.25,0.5,25,20 _fps=6 _label="Edges~on~fire"
51899#@cli : $ image.jpg fx_diffusiontensors 10,5,3,1,0.15,1,0,3,0 _label="Diffusion~tensors"
51900#@cli : $ image.jpg fx_dreamsmooth 3,1,1,0.8,0,0.8,1,24,0 _label="Dream~smoothing"
51901#@cli : $ image.jpg fx_feltpen 300,50,1,0.1,20,5,0 _label="Felt~pen"
51902#@cli : $ image.jpg gtutor_fpaint 0.5,0.5,0,0,45,0.5,0.5,0.5,0 _label="Finger~paint"
51903#@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 \
51904# _label="Novel~FX"
51905#@cli : $ image.jpg fx_illustration_look 100,0,0,0,0 _label="Illustration~look"
51906#@cli : $ image.jpg fx_lylejk_painting 10,2,4,10,0 _label="Lylejk~painting"
51907#@cli : $ image.jpg fx_painting 5,2.5,1.5,50,1,0 _label="Painting"
51908#@cli : $ image.jpg fx_posterize 150,30,1,6,0,0,1,0 _label="Posterize"
51909#@cli : $ image.jpg fx_quadtree 2,1024,1.05,0,2.33,0.68,0.39,1,0 _label="Quadtree~variations"
51910#@cli : $ image.jpg fx_vector_painting 9.37,0 _label="Vector~painting"
51911
51912#@cli _gallery_blackandwhite
51913#@cli : This entry defines some examples of black-and-white filters for the G'MIC gallery page.
51914#@cli : $ image.jpg fx_freaky_bw 90,20,0,0,0,0 _label="Freaky~B&amp;W"
51915#@cli : $ image.jpg fx_engrave 0.5,50,0,8,40,0,0,0,10,1,0,0,0,1,0 _label="Engrave"
51916#@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"
51917#@cli : $ image.jpg fx_pencil_portraitbw 30,120,1,0.5,144,79,21,0 _label="Pencil~portrait"
51918#@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"
51919
51920#@cli _gallery_colors
51921#@cli : This entry defines some examples of color filters for the G'MIC gallery page.
51922#@cli : $ image.jpg fx_color_abstraction 1,10,0.2,0 _label="Color~abstraction"
51923#@cli : $ image.jpg fx_boost_chroma 90,0,0 _label="Boost~chromaticity"
51924#@cli : $ image.jpg fx_retrofade 20,6,40,0 _label="Retro~fade"
51925#@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,\
51926# 0,0,100,0,0.3,25,0,0 _label="Vintage~style"
51927
51928#@cli _gallery_deformations
51929#@cli : This entry defines some examples of deformation filters for the G'MIC gallery page.
51930#@cli : $ image.jpg animate "flower","30,10,0,0","30,10,0,360",10 rm. _fps=6 _label="flower"
51931#@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" \
51932# _label="Conformal~maps"
51933#@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 \
51934# _label="Continuous~droste"
51935#@cli : $ image.jpg fx_crease 30,10,3 _label="Crease"
51936#@cli : $ image.jpg fx_distort_lens 0.29,0,0.23,50,50,0,0 _label="Distort~lens"
51937#@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 \
51938# _label="Drop~water"
51939#@cli : $ image.jpg fx_reflect 50,1,110,160,190,64,0,1.5,0,-3.3,7,1.5 _label="Reflection"
51940#@cli : $ image.jpg fx_square_circle 0,1,0,0,0,0,0,0 _label="Square~to~circle"
51941#@cli : $ image.jpg fx_textured_glass 40,40,1,1,0,2,0,0 _label="Textured~glass"
51942#@cli : $ sample lena,leno,320 morph 40 _fps=5 _label="morph"
51943
51944#@cli _gallery_filtering
51945#@cli : This entry defines some examples of filters for the G'MIC gallery page.
51946#@cli : $ image.jpg fx_gcd_crt 1.8,1.8,0,0 equalize 256 _label="CRT~sub-pixels"
51947#@cli : $ image.jpg fx_dirty 30,1,0,0,0 _label="Dirty"
51948#@cli : $ image.jpg fx_freaky_details 2,10,1,11,0,32,0 _label="Freaky~details"
51949#@cli : $ image.jpg jeje_normalize_local_variance 50,5,5,1,0,0 _label="Local~variance~normalization"
51950#@cli : $ image.jpg fx_mighty_details 25,1,25,1,11,0 _label="Mighty~details"
51951
51952#@cli _gallery_patterns
51953#@cli : This entry defines some examples of pattern filters for the G'MIC gallery page.
51954#@cli : $ image.jpg fx_rain 65,10,50,0.1,1,1,0 gui_merge_layers _label="Rain~&amp;~snow"
51955#@cli : $ 400,400,1,3 fx_camouflage 9,12,100,30,46,33,75,90,65,179,189,117,255,246,158 _label="Camouflage"
51956#@cli : $ image.jpg jeje_clouds 50,0.5 _label="Clouds"
51957#@cli : $ image.jpg fx_crystal 50,0.2,20,0 _label="Crystal"
51958#@cli : $ 400,400,1,3 fx_crystal_background 10,25,0,100,1 _label="Crystal~background"
51959#@cli : $ image.jpg fx_marble 0.5,1,0,0,0.4,0.6,0.6,1.1,0,100 _label="Marble"
51960#@cli : $ image.jpg fx_mineral_mosaic 1,2,1,100,0 _label="Mineral~mosaic"
51961#@cli : $ image.jpg fx_shapes 1,16,10,2,5,106.8,2,0,0,1,0 _label="Op~art"
51962#@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"
51963#@cli : $ 400,400,1,3 fx_seamless_turbulence 15,20,0,1,3,1 _label="Seamless~turbulence"
51964#@cli : $ image.jpg fx_shockwaves 10,10,20,0,0 _label="Shock~waves"
51965#@cli : $ 400,400,1,3 fx_equation_parametric "sin(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)",\
51966# "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]"
51967#@cli : $ 400,400,1,3 KittyRings 30,8,0,1,113,0,113,0,255,0 _label="Kitaoka~spin~illusion"
51968#@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"
51969#@cli : $ image.jpg fx_lava 8,5,3,0,0 _label="Lava"
51970#@cli : $ sample monkey,lion,monkey 100%,100% plasma. equalize. 256 transition[0,1,2] [3],10 rm. _fps=10 \
51971# _label="transition"
51972#@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"
51973
51974#@cli _gallery_3dmeshes
51975#@cli : This entry defines some examples of 3D rendering filters for the G'MIC gallery page.
51976#@cli : $ sample leno,lion,leno resize 400,400 transition3d 20,5,5 rm. _fps=10 _label="transition3d"
51977#@cli : $ 256,192 fx_text_pointcloud3d 64,"G'MIC","Rocks!",1,200,220,255,255,255,255,255,2,2,1,19 _fps=10 \
51978# _label="3D~text~pointcloud"
51979
51980#@cli _gallery_stylization
51981#@cli : This entry defines some examples of image stylization for the G'MIC gallery page.
51982#@cli : $ sample car _fx_stylize starrynight _output_mode=1 \
51983# +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,5,2,1.85,0 _label="from~Van~Gogh:~Starry~Night"
51984#@cli : $ sample car _fx_stylize graytree _output_mode=1 \
51985# +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 _label="from~Mondrian:~Gray~Tree"
51986#@cli : $ sample car _fx_stylize yellowredblue _output_mode=1 \
51987# +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 \
51988# _label="from~Kandinsky:~Yellow-Red-Blue"
51989#@cli : $ sample car _fx_stylize littlebayatlaciotat _output_mode=1 \
51990# +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 \
51991# _label="from~Braque:~Little~Bay~at~La~Ciotat"
51992#@cli : $ sample car _fx_stylize leviaducalestaque _output_mode=1 \
51993# +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 \
51994# _label="from~Braque:~Le~Viaduc~a~l'Estaque"
51995#@cli : $ sample car _fx_stylize greatwave _output_mode=1 \
51996# +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"
51997#@cli : $ sample elephant input ../img/hatching.png _output_mode=1 \
51998# +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,1,2,1.85,0 _label="from~Hatch~Drawing"
51999#@cli : $ sample cat input ../img/hatching.png _output_mode=1 \
52000# +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,1,2,1.85,0 _label="from~Hatch~Drawing"
52001#@cli : $ sample bottles _fx_stylize starrynight _output_mode=1 \
52002# +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"
52003#@cli : $ sample cat _fx_stylize summertime9a _output_mode=1 \
52004# +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,10,2,1.85,0 _label="from~Pollock:~Summertime~No~9A"
52005#@cli : $ sample cat _fx_stylize greatwave _output_mode=1 \
52006# +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"
52007#@cli : $ sample dog _fx_stylize convergence _output_mode=1 \
52008# +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,5,2,1.85,0 _label="from~Pollock:~Convergence"
52009#@cli : $ sample dog _fx_stylize irises _output_mode=1 \
52010# +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,20,2,1.85,0 _label="from~Van~Gogh:~Irises"
52011#@cli : $ sample mandrill _fx_stylize themandola _output_mode=1 \
52012# +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,1,2,1.85,0 _label="from~Braque:~The~Mandola"
52013#@cli : $ sample square _fx_stylize orientalpleasuregardenanagoria _output_mode=1 \
52014# +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,4,2,1.85,0 \
52015# _label="from~Klee:~Oriental~Pleasure~Garden~Anagoria"
52016#@cli : $ sample monalisa _fx_stylize squareswithconcentriccircles _output_mode=1 \
52017# +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,1,2,1.85,0 \
52018# _label="from~Kandisnky:~Squares~with~Concentric~Circles"
52019#@cli : $ sample monalisa _fx_stylize inthestyleofkairouan _output_mode=1 \
52020# +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,5,2,1.85,0 \
52021# _label="from~Klee:~In~the~Style~of~Kairouan"
52022#@cli : $ sample square _fx_stylize polyphony2 _output_mode=1 \
52023# +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,1,2,1.85,0 _label="from~Klee:~Polyphony~2"
52024#@cli : $ sample square _fx_stylize wheatstacksendofsummer _output_mode=1 \
52025# +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,1,2,1.85,0 \
52026# _label="from~Monet:~Wheatstacks~-~End~of~Summer"
52027#@cli : $ sample square _fx_stylize portraitdemetzinger _output_mode=1 \
52028# +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,10,2,1.85,0 \
52029# _label="from~Delaunay:~Portrait~de~Metzinger"
52030#@cli : $ sample monalisa input ../img/mandelbrot.jpg _output_mode=1 \
52031# +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,5,2,1.85,0 _label="from~Mandelbrot~Fractal~Set"
52032#@cli : $ sample bottles _fx_stylize redtree _output_mode=1 \
52033# +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,1,2,1.85,0 \
52034# _label="from~Mondrian:~Evening;~Red~Tree"
52035#@cli : $ sample bottles _fx_stylize redwaistcoat _output_mode=1 \
52036# +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,5,1.05,1.85,0 \
52037# _label="from~Klee:~Red~Waistcoat"
52038#@cli : $ sample bottles _fx_stylize reservoirhortadeebro _output_mode=1 \
52039# +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~Picasso:~The~Reservoir"
52040#@cli : $ sample bottles _fx_stylize almondblossom _output_mode=1 \
52041# +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,1,2,1.85,0 _label="from~Van~Gogh:~Almond~Blossom"
52042#@cli : $ sample bottles _fx_stylize landscapenearantwerp _output_mode=1 \
52043# +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,1,2,1.85,0 \
52044# _label="from~Braque:~Landscape~near~Antwerp"
52045#@cli : $ sample bottles _fx_stylize wheatfieldwithcrows _output_mode=1 \
52046# +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,1,5,1.85,0 \
52047# _label="from~Van~Gogh:~Wheat~Field~with~Crows"
52048
52049#@cli _gallery_codesamples
52050#@cli : This entry defines some examples of coding fun filters for the G'MIC gallery page.
52051#@cli : $ https://gmic.eu/samples/lissajous.gmic go _fps=10 _label="Lissajous"
52052#@cli : $ https://gmic.eu/samples/torus3d.gmic go _fps=10 _label="3D~torus"
52053#@cli : $ https://gmic.eu/samples/pacman.gmic go _fps=25 _label="Pacman"
52054#@cli : $ https://gmic.eu/samples/scrolling.gmic go _fps=25 _label="Scrolling"
52055#@cli : $ https://gmic.eu/samples/landscape.gmic go _fps=12 _label="Landscape"
52056#@cli : $ https://gmic.eu/samples/mandelbrot.gmic go _fps=8 _label="Mandelbrot"
52057#@cli : $ https://gmic.eu/samples/heart.gmic go _fps=15 _label="Heart"
52058#@cli : $ https://gmic.eu/samples/distortion.gmic go _fps=20 _label="Distortion"
52059#@cli : $ https://gmic.eu/samples/rotozoom.gmic go _fps=15 _label="Rotozoom"
52060#@cli : $ https://gmic.eu/samples/french_flag.gmic go _fps=20 _label="French~Flag"
52061
52062# Local Variables:
52063# mode: sh
52064# End:
52065#
52066# (End of G'MIC custom commands)
52067