# GRunT

Document v1.0.1 (Gavino, 27th September 2008)

## Contents

1. Introduction
2. Summary of Features
3. Run-time filters
4. Run-time functions
5. ConditionalFilter
6. Bug fix
7. Configuration
8. Filters and Functions Reference
9. Plugin Revision History

## Introduction

Since v2.52, Avisynth has included a very powerful run-time environment (also known as the 'conditional' environment). This was a great step forward, supporting complex video processing that would be difficult or impossible to perform in a normal script. However, it is not easy to use - the behaviour of run-time scripts (especially in combination) can be hard to understand and there are usability problems concerning scope and lifetime of variables.

"Cond. filters are quite complex - and sometimes I am even surprised of the outcome" - sh0dan

Some of the difficulty is inherent in the concept itself, requiring a clear understanding of the difference between the compiling and frame-serving phases of Avisynth. However, many of the problems stem from specific limitations of the interface presented by the run-time filters/functions and the details of their implementation which, with the benefit of hindsight, we can see could have been done differently.

GRunT (Gavino's Run-Time) is a plugin which addresses these and other problems, making the run-time system much easier to use.

## Summary of Features

GRunT provides the following features:

1. A simple, natural and robust way to pass variables into a run-time script from 'outside'
2. A run-time script can optionally be evaluated in its own independent scope
3. Run-time functions can be called from a user function
4. Run-time functions can be applied to any frame (relative to the current one)
5. Additional variant of ConditionalFilter with single boolean expression
6. Fixes a fairly serious bug in the run-time system
7. Lightweight plugin extending the standard run-time environment, minimal time and memory overhead
8. 100% backwards compatible with existing scripts

Backwards compatibility is an important design objective of GRunT. It means that you can put the plugin in your plugins folder and all correctly functioning scripts will continue to operate exactly as before.

GRunT should be useful to anyone who uses the run-time filters, from those who make occasional use of ScriptClip to those who write complex functions based on run-time features (such as Restore24 or MRestore).

## Run-time filters

The plugin provides extended versions of the following run-time filters:

- ScriptClip
- FrameEvaluate
- ConditionalFilter
- WriteFile
- WriteFileIf

Some users may prefer not to use the same names as the original filters, so the alternative names GScriptClip, GFrameEvaluate, GConditionalFilter, GWriteFile and GWriteFileIf may also be used. However, if running on version 2.57 or earlier of Avisynth, then only the alternative names may be used. (This restriction is necessary for technical reasons - sorry about that.)

Each filter is 100% backwards compatible with its standard equivalent, but has two additional optional arguments:

- string args - the variables whose values are to be imported into the run-time script, written as a list of names separated by commas.
  The given variable names are evaluated in the current (compile-time) context, so can include function parameters or local variables.
  Each value becomes the (initial) value of the corresponding variable at each invocation of the run-time script (ie for each frame produced by the filter).
- bool local - if true, the filter will evaluate its run-time script in a new variable scope, avoiding unintended sharing of variables between run-time scripts.
  Default is true if args is also specified, otherwise false (to preserve backwards compatibility).

A short example (based on the original) shows how this greatly simplifies passing function parameters into a run-time script.

    function bracket_luma(clip c, float th1, float th2) {
      Assert(0 <= th1 && th1 < th2 && th2 <= 255, "Invalid thresholds!")
      ScriptClip(c, """
        avl = AverageLuma()
        avl <= th1 ? last.BlankClip() : avl >= th2 ? last.BlankClip(color=color_white) : last
      """, args="th1,th2", local=true)
    }

This is much easier than the standard approach of dynamically building the runtime script using string concatenation, or passing the values via global variables.

"I really do not like this global variable business with ScriptClip ..." - stickboy

And because the run-time script is evaluated in its own scope, there is now no problem in calling bracket_luma more than once in the same script (previously the variables th1 and th2 of different instances could interfere with each other).

Elements of the args string can also take the form 'name=expression' - the expression is evaluated in the current context and, as in the simple case, is used to set the initial value of the named variable in the run-time script. For example,

    args="x, y=n+1, c=c.Trim(2, 0)"

will provide values for the variables x, y and c which are used in the run-time script.
Here y need not even exist in the current environment (although x, n and c must).

Evidently, the simple form (without the expression) is just a shorthand for the more general form, with the variable name itself being used as the expression.
So, for example,

    args="x, y, z"

is equivalent to

    args="x=x, y=y, z=z"

passing the values of the compile-time variables x, y and z (from outside the run-time script) into the variables of the same name inside the script.

Spaces may be freely used (or not) inside the args string, just as in Avisynth function calls.

## Run-time functions

The plugin also provides the following extensions to the run-time functions (such as AverageLuma):

1. These functions can now be called inside a user function, when (and only when) the user function is called from a run-time script.
2. Each function has a new optional int argument, which can be used to get the value from another frame, relative to the current one.
   For example, `AverageLuma(-1)` returns the value for the previous frame.

Note that to support the first feature, current_frame is now a global variable (see discussion). However, the second feature means that it is no longer necessary to change current_frame to access other frames. In fact, for most purposes, you can forget that current_frame exists as an explicit variable.

Here is an example of a weighted second order luma interpolation function:

      function InterpLuma2(clip c) {
        lm_k = AverageLuma(c)
        lm_km1 = AverageLuma(c, -1)
        lm_kp1 = AverageLuma(c, 1)
        dvg = (lm_km1 - 2 * lm_k + lm_kp1) / 2
        return lm_k + Sign(dvg) * Sqrt(Abs(dvg))
      }

This function can be called from any run-time script. Previously, something like this this had to be done by assigning to current_frame (or using additional filters, eg Trim(1,0)), and the code had to be written directly in the run-time script rather than in a function.

In effect, we are now able to write run-time functions of our own, derived from the standard ones. Here is another example:

      function AverageRed(clip c) {
        return RGBDifference(ShowRed(c), BlankClip(c))
      }

Note too that, while useful in its own right, the ability to call run-time functions in a user function gives you more than just that. For the first time, it allows an entire run-time script to be put inside a function body, called for example like this:

    ScriptClip("MyFunc()")

As the function body will be evaluated in a separate scope, this is another way of eliminating the problem of unintended sharing of variables between run-time scripts. However, you may prefer to write the run-time script 'in-line' and invoke the run-time filter with local=true.

## ConditionalFilter

For added convenience, there is a new variant of ConditionalFilter which takes a single boolean expression instead of three separate parameters as at present. This is useful when the condition to be tested is a compound one or is already available in boolean form (like `IsCombed()`). For example,

    ConditionalFilter(c, c1, c2, "AverageLuma(c1) > AverageLuma() && AverageLuma(c1) > AverageLuma(c2)")

where previously you would have to say

    ConditionalFilter(c, c1, c2, "AverageLuma(c1) > AverageLuma() && AverageLuma(c1) > AverageLuma(c2)", "=", "true")

Note that this form of ConditionalFilter also makes it easier use the relational operators `<=`, `>=` and `!=`, which are not supported as the operator in the standard ConditionalFilter.

## Bug fix

The plugin fixes a fairly serious bug I discovered in the run-time system. This fix is needed if you are running a version of Avisynth prior to build 080620 of 2.58. (GRunT actually originated as a means to demonstrate this bug-fix, but I have now extended it to do a lot more.)

Since things like ScriptClip are often hidden deep inside huge functions like Restore24, you may be affected by this bug without even realising it.

## Configuration

For the extended run-time filters, the default for local is false unless args is also specified (for compatibility reasons). However, I recommend always using local=true unless you really need to communicate values of variables from one run-time script to another (note that you can always use global variables for this).

To this end, the plugin provides a function GRTConfig which allows the default behaviour to be changed.
So, `GRTConfig(local=true)` sets the default to local=true for all subsequent filters.

## Filters and Functions Reference

For reference, the complete interface to the extended run-time filters is as follows (see above for a description of the args and local parameters):

`ScriptClip` (clip, string function, bool "show", bool "after_frame", string "args", bool "local")

`FrameEvaluate` (clip, string function, bool "show", bool "after_frame", string "args", bool "local")

`ConditionalFilter` (clip testclip, clip source1, clip source2, string filter, string operator, string value, bool "show", string "args", bool "local")
`ConditionalFilter` (clip testclip, clip source1, clip source2, string condition, bool "show", string "args", bool "local")

`WriteFile` (clip, string filename, string expression1, ... , string expression16, bool "append", bool "flush", string "args", bool "local")
`WriteFileIf` (clip, string filename, string expression1, ... , string expression16, bool "append", bool "flush", string "args", bool "local")

The alternative names `GScriptClip`, `GFrameEvaluate`, `GConditionalFilter`, `GWriteFile` and `GWriteFileIf` may also be used (and with Avisynth version 2.57 or earlier, must be used).

Here is the complete list of run-time functions, with their extended interface.

`AverageLuma` (clip, int offset)
`AverageChromaU` (clip, int offset)
`AverageChromaV` (clip, int offset)
`RGBDifference` (clip1, clip2, int offset)
`LumaDifference` (clip1, clip2, int offset)
`ChromaUDifference` (clip1, clip2, int offset)
`ChromaVDifference` (clip1, clip2, int offset)
`RGBDifferenceFromPrevious` (clip, int offset)
`YDifferenceFromPrevious` (clip, int offset)
`UDifferenceFromPrevious` (clip, int offset)
`VDifferenceFromPrevious` (clip, int offset)
`RGBDifferenceToNext` (clip, int offset)
`YDifferenceToNext` (clip, int offset)
`UDifferenceToNext` (clip, int offset)
`VDifferenceToNext` (clip, int offset)
`YPlaneMax` (clip, float threshold, int offset)
`UPlaneMax` (clip, float threshold, int offset)
`VPlaneMax` (clip, float threshold, int offset)
`YPlaneMin` (clip, float threshold, int offset)
`UPlaneMin` (clip, float threshold, int offset)
`VPlaneMin` (clip, float threshold, int offset)
`YPlaneMedian` (clip, int offset)
`UPlaneMedian` (clip, int offset)
`VPlaneMedian` (clip, int offset)
`YPlaneMinMaxDifference` (clip, float threshold, int offset)
`UPlaneMinMaxDifference` (clip, float threshold, int offset)
`VPlaneMinMaxDifference` (clip, float threshold, int offset)

The optional offset parameter specifies the offset (which may be negative) from the current frame; default is zero.

## Plugin Revision History

v1.0.1 (Gavino, 27th September 2008): fix for Avisynth 2.5.7 and earlier (have to use alternative names for filters, eg GScriptClip)

v1.0.0 (Gavino, 9th July 2008): first full version

v0.1 (Gavino, 18th June 2008): initial version to fix run-time system bug