Subversion Repositories filter_foundry

Rev

Rev 472 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
221 daniel-mar 1
Implementation detail differences
2
=================================
141 dmarschall 3
 
304 daniel-mar 4
Filter Foundry tries to be as compatible with Filter Factory as possible.
5
However, there are some differences that are explained in this documentation.
141 dmarschall 6
 
294 daniel-mar 7
Various implementations
8
-----------------------
141 dmarschall 9
 
294 daniel-mar 10
In the source-code file funcs.c, some functions are implemented twice:
11
One instance is the default implementation (older Filter Foundry versions used),
12
and one instance is a 100% replica of the Filter Factory code, obtained
13
from the "OPER" resource.
14
(More information at https://misc.daniel-marschall.de/projects/filter_factory/res_oper.html )
297 daniel-mar 15
If required, the compiler-definitions `use_filterfactory_implementation_*`
294 daniel-mar 16
can be set or unset to select the implementation.
17
 
304 daniel-mar 18
In Filter Foundry 1.7.0.8, the following functions have been updated to the Filter Factory replica:
297 daniel-mar 19
- `rnd(x)`
20
- `cos(x)`
21
- `sin(x)`
22
- `tan(x)`
23
- `r2x(d,m)`
24
- `r2y(d,m)`
25
- `rad(d,m,z)`
26
- `c2d(x,y)`
27
- `c2m(x,y)`
28
- `sqr(x)`
29
- `d`
30
- `m`
31
- `M`
294 daniel-mar 32
 
33
 
34
sqr(x)
35
------
36
 
37
Filter Factory:
38
 
39
	sqr(x)=x for x < 0
295 daniel-mar 40
 
304 daniel-mar 41
	Can be tested with the following expression:
295 daniel-mar 42
	sqr(-20)+21 == 1
294 daniel-mar 43
 
44
Filter Foundry (prior to 1.7.0.8):
45
 
46
	sqr(x)=0 for x < 0
47
 
304 daniel-mar 48
Beginning with Filter Foundry 1.7.0.8, the behavior of Filter Factory was implemented.
297 daniel-mar 49
 
304 daniel-mar 50
 
158 dmarschall 51
i, u, v (Testcase iuv.afs)
141 dmarschall 52
-------
53
 
54
Filter Foundry <1.7 uses the same formulas as in Filter Factory:
55
 
221 daniel-mar 56
    i=((76*r)+(150*g)+(29*b))/256            // Output range is 0..254
57
    u=((-19*r)+(-37*g)+(56*b))/256           // Output range is -55..55
58
    v=((78*r)+(-65*g)+(-13*b))/256           // Output range is -77..77
141 dmarschall 59
 
60
Filter Foundry 1.7 uses more accurate formulas:
61
 
221 daniel-mar 62
    i=(299*r+587*g+114*b)/1000               // Output range is 0..255
63
    u=(-147407*r-289391*g+436798*b)/2000000  // Output range is -55..55
64
    v=614777*r-514799*g-99978*b)/2000000     // Output range is -78..78
141 dmarschall 65
 
192 daniel-mar 66
Both formulas follow the same YUV standard but have different accuracy.
141 dmarschall 67
 
68
 
304 daniel-mar 69
I, U, V, imin, umin, vmin (Testcase iuv_minmax.afs)
70
-------------------------
71
 
399 daniel-mar 72
In Filter Foundry 1.7.0.8, the previously undocumented variables `I`, `U`, `V` as well as `imin`, `umin`, and `vmin`
73
have been changed to represent the actual results of the `i`, `u`, and `v` variables:
304 daniel-mar 74
 
399 daniel-mar 75
    imax = 255 (was 255 in Filter Factory for Windows and 256 in Filter Factory for Mac OS)
76
    umax = 55 (was 255 in Filter Factory for Windows and 256 in Filter Factory for Mac OS)
77
    vmax = 78 (was 255 in Filter Factory for Windows and 256 in Filter Factory for Mac OS)
304 daniel-mar 78
 
79
    imin = 0 (stayed the same)
80
    umin = -55 (was 0 in Filter Factory)
81
    vmin = -78 (was 0 in Filter Factory)
82
 
335 daniel-mar 83
It is questionable if `I` was meant to be a synonym of `imax`, or if `I` was meant to be `I := imax - imin`.
84
We have chosen the latter in Filter Foundry 1.7.0.9. Same thing with `U` and `V`.
304 daniel-mar 85
 
335 daniel-mar 86
Therefore:
87
 
399 daniel-mar 88
    I := imax-imin = 255 (was 255 in Filter Factory for Windows and 256 in Filter Factory for Mac OS)
89
    U := umax-umin = 110 (was 255 in Filter Factory for Windows and 256 in Filter Factory for Mac OS)
90
    V := vmax-vmin = 156 (was 255 in Filter Factory for Windows and 256 in Filter Factory for Mac OS)
335 daniel-mar 91
 
92
 
93
dmin, D (Testcase d_minmax.afs)
94
-------
95
 
96
**The Filter Factory manual writes:**
97
 
399 daniel-mar 98
- 0 corresponds to the 3 o'clock position,
99
- 256 to the 6 o'clock position,
100
- 512 to the 9 o'clock position,
101
- 768 to the 12 o'clock position,
102
- and 1024 to the full rotation back to the 3 o'clock position.
335 daniel-mar 103
 
399 daniel-mar 104
But this does neither match the Windows implementation of Filter Factory nor its Mac OS implementation!
335 daniel-mar 105
 
399 daniel-mar 106
**In the original Windows & Mac OS Filter Factory implementations we can observe:**
335 daniel-mar 107
 
399 daniel-mar 108
- d=-512 is at 9 o'clock position
109
- d=-256 is at 12 o'clock position
110
- d=0 is at 3 o'clock position
111
- d=256 is at 6 o'clock position
490 daniel-mar 112
- d=512 is the full rotation back to 9 o'clock position
335 daniel-mar 113
 
399 daniel-mar 114
Therefore, `dmin` has been changed from 0 to -512, and `dmax` have been changed from 1024 to 512.
115
It is questionable if `D` was meant to be a synonym of `dmax`, or if `D` was meant to be `D := dmax - dmin`.
116
We have chosen the latter, so it stayed 1024.
335 daniel-mar 117
 
118
 
141 dmarschall 119
get(i) (Testcase getput.afs)
120
------
121
 
122
Filter Foundry:
123
 
221 daniel-mar 124
    get(x)=0 if x>255 or x<0
125
 
141 dmarschall 126
Filter Factory:
127
 
221 daniel-mar 128
    get(x)=x if x>255 or x<0
141 dmarschall 129
 
221 daniel-mar 130
Note: The result "x" was most likely not intended but a result of an undefined behavior
131
 
132
 
472 daniel-mar 133
r, g, b of fully transparent pixels (Testcase emptycanvas.afs)
134
-----------------------------------
141 dmarschall 135
 
472 daniel-mar 136
In Filter Factory (Windows and Mac implementation), a fully transparent pixel is initialized (`r`,`g`,`b` variables) with the current background color.
141 dmarschall 137
 
221 daniel-mar 138
Filter Foundry initializes it as `r=g=b=255`
141 dmarschall 139
 
140
 
294 daniel-mar 141
rnd(a,b) (Testcases rnd*)
289 daniel-mar 142
--------
143
 
144
Filter Factory uses Donald E. Knuth's subtractive random number generator algorithm,
145
which has been published in "The Art of Computer Programming, volume 2: Seminumerical Algorithms".
146
Addison-Wesley, Reading, MA, second edition, 1981.
147
 
148
Beginning with Filter Foundry 1.7.0.8, the same PRNG was implemented,
149
so that the output of rnd(a,b) is exactly the same now.
150
 
151
 
294 daniel-mar 152
rst(i) (Testcases rnd*.afs and rst_*.afs)
153
------
141 dmarschall 154
 
289 daniel-mar 155
Filter Factory contains an undocumented function that sets the seed for the random number generator.
141 dmarschall 156
 
304 daniel-mar 157
Filter Factory and Filter Foundry beginning with 1.7.0.8 accept a seed between 0 and 32767, inclusively.
158
If the argument is not within this range, the operation lowest 15 bits are taken.
141 dmarschall 159
 
304 daniel-mar 160
There are many differences in the implementation between Filter Factory and Filter Foundry in regards rst(i):
141 dmarschall 161
 
289 daniel-mar 162
**Filter Factory:**
143 dmarschall 163
 
289 daniel-mar 164
If rst(i) is called in Filter Factory, an internal Seed-Variable is set.
165
It does NOT influence any calls of rnd(a,b), because a lookup-table needs to be built first.
166
The building of the lookup-table is probably done before the processing of the first pixel (x,y,z=0).
167
It is suspected that the call of rst(i) will take effect on the next calculation.
168
Due to a bug (or feature?), the random state is not reset to its initial state (0) before the
169
filter is applied. The preview image processing will modify the random state, and once the filter
170
is actually applied (pressing "OK"), the random state that was set in the preview picture, will be used.
171
This could be considered as a bug, but it is probably required, otherwise the call of rst(i)
172
(inside the preview calculation) won't affect the rnd(a,b) in the real run.
173
However, in a standalone filter without dialog/preview, there is no preview that could set
174
the internal seed, so the rnd(a,b) functions will always work using the default seed 0,
175
and only the subsequent calls will use the rst(i) of the previous call.
221 daniel-mar 176
 
289 daniel-mar 177
**Filter Foundry:**
143 dmarschall 178
 
289 daniel-mar 179
In Filter Foundry, the function rnd(a,b) retrieves a random number in "realtime"; therefore, if the
180
seed is changed via rst(i), there is an immediate effect on the next call of the rnd(a,b) function.
181
 
304 daniel-mar 182
For example, the following filter would generate an one-colored picture without any randomness:
221 daniel-mar 183
        R: rst(123), rnd(0,255)
184
        G: rnd(0,255)
185
        B: rnd(0,255)
144 dmarschall 186
 
289 daniel-mar 187
If you want to generate a random pixel image with a non-default seed, you need to make sure
188
that rst(i) is called only once at the beginning (channel 0, coordinate 0|0):
294 daniel-mar 189
        R: (x== 0 && y ==0) ? rst(123) : 0, rnd(0,255)
221 daniel-mar 190
        G: rnd(0,255)
191
        B: rnd(0,255)
144 dmarschall 192
 
289 daniel-mar 193
In Filter Foundry, rst(i) can be called by branches and variables/sliders can
194
be used as arguments of rst(i).
144 dmarschall 195
 
196
 
143 dmarschall 197
Evaluation of conditional branches
198
----------------------------------
199
 
221 daniel-mar 200
**Filter Foundry:**
144 dmarschall 201
 
221 daniel-mar 202
Only the branches which will be chosen due to the conditional expression will be evaluated.
203
 
304 daniel-mar 204
This means that the following filter would generate a black canvas: (Testcase conditional_eval_1.afs)
221 daniel-mar 205
 
206
        R: 1==0 ? put(255,0) : 0
207
        G: get(0)
208
        B: 0
209
 
210
In boolean expressions, the evaluation will be aborted if the result is already determined.
211
 
212
So, this will also generate a black canvas: (Testcase conditional_eval_2.afs)
213
 
214
        R: 1==0 && put(255,0) ? 0: 0
215
        G: get(0)
216
        B: 0
217
 
218
This will also generate a black canvas: (Testcase conditional_eval_3.afs)
219
 
220
        R: 1==1 || put(255,0) ? 0 : 0
221
        G: get(0)
222
        B: 0
223
 
224
**Filter Factory:**
225
 
304 daniel-mar 226
Each branch inside an if-then-else expression will be evaluated.
227
This means that the following filter would generate a green canvas: (Testcase conditional_eval_1.afs)
221 daniel-mar 228
 
229
        R: 1==0 ? put(255,0) : 0
230
        G: get(0)
231
        B: 0
232
 
304 daniel-mar 233
Also, all arguments of a boolean expression will be fully evaluated.
221 daniel-mar 234
So, this will also generate a green canvas: (Testcase conditional_eval_2.afs)
235
 
236
        R: 1==0 && put(255,0) ? 0: 0
237
        G: get(0)
238
        B: 0
239
 
240
This will also generate a green canvas: (Testcase conditional_eval_3.afs)
241
 
242
        R: 1==1 || put(255,0) ? 0 : 0
243
        G: get(0)
244
        B: 0