Filter Forge uses a sample-based approach to image generation. Filter Forge components don't produce images by painting their pixels directly. Instead, Filter Forge 'asks' a component "What color does your image have at the coordinates x and y?", and the component 'answers' with four RGBA values. In other words, the resulting image is implicitly defined by the component, as opposed to being explicitly painted by it.
To 'ask' about color at a given point of the image, Filter Forge calls the get_sample() function of the component with the sample point coordinates as arguments, and the function then calculates and returns the final 'answer' as four RGBA values.
During the execution of its own get_sample() function, a component may in turn call a get_sample() function of the components connected to its inputs, and use the returned RGBA values to calculate its own resulting color. With the exception of control components, the filter tree you see in the Filter Editor is a visual representation of the get_sample() call graph.
To render a filter into a bitmap, Filter Forge renderer cycles through all its pixels and calls the get_sample() function of the root component of the filter tree at least once per pixel (anti-aliased pixels may require multiple calls, read below for details.) For example, generating a non-antialiased bitmap with the dimensions of 30x30 pixels would require 900 calls to component's get_sample() function.
Calling the get_sample() function of a component is called sampling, the coordinates of the point whose color is being requested are called sample coordinates, and the returned color is often referred to as simply sample.
Map components (including map scripts) do their work by calling get_sample() functions of components connected to their inputs.
A component that received a get_sample() call from an 'upstream' component it is connected to or from the Filter Forge renderer directly, may in turn call get_sample() of other components that are connected to its inputs. This way, components in the filter tree can obtain results generated by their 'downstream' components and use them to calculate their own results.
Filter Forge components are free to call get_sample() of their map inputs any number of times, with any sample coordinates they want, and they can combine the RGBA values returned by these calls in any manner. For example, distortion components may shoot downstream samples at different coordinates than specified in the upstream call they received. Another example is adjustment components that usually shoot downstream samples at the same coordinates as those given in an upstream call – they just perform some form of color correction on the returned sample color which they return as result.
Bitmap-based components such as Blur, Sharpen or Median, are something of an exception to the sample-based approach. Their algorithms, by design, require multiple reads of the source image, so it makes sense to cache it in a bitmap. All this is encapsulated using the usual get_sample() function which, in this case, just fetches pixels from the internal bitmap cache and performs bicubic filtering on them.
Curve components also use sampling. In addition to the sample coordinates, arguments of their get_sample() function include a parameter t, which is the argument to be passed to the function represented by the curve (the position on the abscissa axis.)
Unlike the get_sample() function of map components which returns RGBA values, the get_sample() function of curve components returns a single numeric value in the range of 0…1, which is the value of the curve at the specified point (the position on the ordinate axis.)
You may wonder why a curve component needs the coordinates of the sample points. This is because Filter Forge's curve components can have map inputs, and therefore the curve they generate may have different shape at different sample points. For more information on how parameter mapping works for curves, see Map Inputs.
In case of slave components, there's an additional argument of the get_sample() function involved in the sampling process. When a master component samples its slave-accepting input, it includes an additional package of data along with the sampling point coordinates. When slave components belonging to this master component encounter the data package when executing their get_sample() function, they change their output according to the data sent by their master.
Technically, the data package sent by a master to its slaves is an additional argument of the get_sample() function. It contains the ID of the master component that sent it, and the information for slave components, based on which they will change their output. When slave components encounter this data package during the execution of their get_sample() function, they check if the master component that sent it is their own master, and if yes, they change their output according to the information contained in the package. Normal components that are not slaves do not react to these data packages, and just pass them unchanged when calling get_sample() for their own inputs.
For example, when a Loop component samples its Accumulator input, it includes the number of the current loop iteration into the data package. This package will be used as an additional argument, along with the point coordinates, during the call of the get_sample() function of the component connected to Accumulator. The component connected to Accumulator is usually a normal (non-slave) component, so it just passes the data package to its own inputs when sampling them. When the data package finally reaches a slave component of this particular Loop, for example the Iteration slave, the slave component reads the package, retrieves the iteration number,modifies it if necessary, and sends it to its output. If the data package comes from a Loop component that is not the master of this particular slave, or from a completely different master component (e.g. Bomber Plus), the slave doesn’t change its output based on the data package.
When a slave component executes a get_sample() call without a data package 'addressed' to it (i.e. a data package from someone else's master, or from a master component of different kind), it doesn't use the data from the package to form its own output. Instead, it uses its preview settings to produce output.
In any case, when executing their get_sample(), slaves forward any data packages to their inputs, regardless of whether they react to the data package or not. This means that you can connect slaves to the inputs of other slaves, and they will work correctly.
Bitmap-based components (such as Blur or Motion Blur) cannot store the data packages mentioned above in their input or output bitmap caches. This makes them incompatible with slave-to-master connections (with the exception of the Result component: see the 'Using Slave Components' section of the help article for the Result component.)
A sample point with the coordinates of 0, 0 corresponds to the top-left corner of the image, and a point with the coordinates of 1, 1 corresponds to the bottom-right corner of the square Size x Size pixels wide, where Size is the current value of the global Size slider. When Size is set to its maximum value (which is determined as the minimum between the width and height of the source image) and the image is square, the point of 1, 1 corresponds to the bottom-right corner of the image.
The dependency of sample coordinates on the value of the Size slider may seem counterintuitive and complicated, but actually it frees script writers from having to deal with Size, image dimensions and aspect ratios manually. Unless you're writing an advanced script which needs to be Size-independent, you don't have to worry about Size or image dimensions – you can just keep the important elements of script's output image in the region between 0, 0 and 1, 1.
Both sample coordinates can extend beyond the 0…1 range both in positive and negative direction. Essentially, a sample-based component implements an infinite textured plane, so it must be able to correctly handle sample requests at any coordinates.
Components must not assume that sampling calls they receive will conform to any particular order or pattern. A component must be able to report its color for any sample location at any time.
The sampling pattern and order may change when a component is sampled by an upstream distortion or transform component, or when Filter Forge applies smart anti-aliasing where some pixels receive more samples than others (especially when sample jittering is turned on), or even due to a GUI scenario where the renderer places higher priority on rendering blocks that are currently visible in the preview area.