Subversion Repositories filter_foundry

Rev

Rev 304 | Rev 399 | Go to most recent revision | 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
 
72
In Filter Foundry 1.7.0.8, the previously undocumented variables I, U, V as well as imin, umin, vmin
73
have been changed to represent the actual results of the i,u,v variables:
74
 
335 daniel-mar 75
    imax = 255 (stayed the same)
76
    umax = 55 (was 255 in Filter Factory)
77
    vmax = 78 (was 255 in Filter Factory)
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
 
88
    I := imax-imin = 255
89
    U := umax-umin = 110
90
    V := vmax-vmin = 156
91
 
92
 
93
dmin, D (Testcase d_minmax.afs)
94
-------
95
 
96
**The Filter Factory manual writes:**
97
 
98
 
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
103
 
104
But this does not match the Windows implementation of Filter Factory
105
(maybe it is true to the Mac implementation?)
106
 
107
**In the original Windows implementation we can observe:**
108
 
109
d=-512 is at 9 o'clock position
110
d=-256 is at 12 o'clock position
111
d=0 is at 3 o'clock position
112
d=256 is at 6 o'clock position
113
d=512 is the full rotation back to 3 o'clock position
114
 
115
Therefore, `dmin` has been changed from 0 to -512,
116
and `D`, `dmax` has been changed from 1024 to 512.
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
 
141 dmarschall 133
r, g, b at empty canvas (Testcase emptycanvas.afs)
134
-----------------------
135
 
221 daniel-mar 136
In Filter Factory, an empty (transparent) canvas of a new file is initialized as `r=g=b=0`
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