Subversion Repositories filter_foundry

Rev

Rev 472 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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