Rev 158 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 158 | Rev 192 | ||
---|---|---|---|
1 | 1 | ||
2 | Implementation detail differences Daniel Marschall |
2 | Implementation detail differences Daniel Marschall |
3 | ================================= 08 January 2018 |
3 | ================================= 08 January 2018 |
4 | 4 | ||
5 | FilterFoundry tries to be as compatible with Filter Factory as possible. |
5 | FilterFoundry tries to be as compatible with Filter Factory as possible. |
6 | However, results are usually not 100% equal, because functions like |
6 | However, results are usually not 100% equal because functions like |
7 | cos, sin, sqr, etc. have different accuracy due to the underlying |
7 | cos, sin, sqr, etc., have different accuracy due to the underlying |
8 | implementation. |
8 | implementation. |
9 | 9 | ||
10 | Furthermore, there are following known differences between Filter Foundry |
10 | Furthermore, there are the following known differences between |
11 | and Filter Factory: |
11 | Filter Foundry and Filter Factory: |
12 | 12 | ||
13 | i, u, v (Testcase iuv.afs) |
13 | i, u, v (Testcase iuv.afs) |
14 | ------- |
14 | ------- |
15 | 15 | ||
16 | Filter Foundry <1.7 uses the same formulas as in Filter Factory: |
16 | Filter Foundry <1.7 uses the same formulas as in Filter Factory: |
17 | 17 | ||
18 | i=((76*r)+(150*g)+(29*b))/256 Output range is 0..254 |
18 | i=((76*r)+(150*g)+(29*b))/256 Output range is 0..254 |
19 | u=((-19*r)+(-37*g)+(56*b))/256 Output range is -55..55 |
19 | u=((-19*r)+(-37*g)+(56*b))/256 Output range is -55..55 |
20 | v=((78*r)+(-65*g)+(-13*b))/256 Output range is -77..77 |
20 | v=((78*r)+(-65*g)+(-13*b))/256 Output range is -77..77 |
21 | 21 | ||
22 | Filter Foundry 1.7 uses more accurate formulas: |
22 | Filter Foundry 1.7 uses more accurate formulas: |
23 | 23 | ||
24 | i=(299*r+587*g+114*b)/1000 Output range is 0..255 |
24 | i=(299*r+587*g+114*b)/1000 Output range is 0..255 |
25 | u=(-147407*r-289391*g+436798*b)/2000000 Output range is -55..55 |
25 | u=(-147407*r-289391*g+436798*b)/2000000 Output range is -55..55 |
26 | v=614777*r-514799*g-99978*b)/2000000 Output range is -78..78 |
26 | v=614777*r-514799*g-99978*b)/2000000 Output range is -78..78 |
27 | 27 | ||
28 | Both formulas follow the same YUV standard, but have different accuracy. |
28 | Both formulas follow the same YUV standard but have different accuracy. |
29 | 29 | ||
30 | 30 | ||
31 | get(i) (Testcase getput.afs) |
31 | get(i) (Testcase getput.afs) |
32 | ------ |
32 | ------ |
33 | 33 | ||
34 | Filter Foundry: |
34 | Filter Foundry: |
35 | get(x)=0 if x>255 or x<0 |
35 | get(x)=0 if x>255 or x<0 |
36 | 36 | ||
37 | Filter Factory: |
37 | Filter Factory: |
38 | get(x)=x if x>255 or x<0 |
38 | get(x)=x if x>255 or x<0 |
39 | [Note: The result "x" was most likely not intended but a result of an undefined behavior] |
39 | [Note: The result "x" was most likely not intended but a result of an undefined behavior] |
40 | 40 | ||
41 | 41 | ||
42 | r, g, b at empty canvas (Testcase emptycanvas.afs) |
42 | r, g, b at empty canvas (Testcase emptycanvas.afs) |
43 | ----------------------- |
43 | ----------------------- |
44 | 44 | ||
45 | In Filter Factory, an empty (transparent) canvas of a new file is initialized as r=g=b=0 |
45 | In Filter Factory, an empty (transparent) canvas of a new file is initialized as r=g=b=0 |
46 | 46 | ||
47 | Filter Foundry initializes it as r=g=b=255 |
47 | Filter Foundry initializes it as r=g=b=255 |
48 | 48 | ||
49 | 49 | ||
50 | rnd(a,b) and rst(i) (Testcases rst_*.afs) |
50 | rnd(a,b) and rst(i) (Testcases rst_*.afs) |
51 | ------------------- |
51 | ------------------- |
52 | 52 | ||
53 | Filter Foundry's implementation of rst(i) (undocumented function that sets the seed for the PRG) |
53 | Filter Foundry's implementation of rst(i) (an undocumented function that sets the seed for the PRG) |
54 | and rnd(a,b) (generate random number between a and b, inclusively) |
54 | and rnd(a,b) (generate a random number between a and b, inclusively) |
55 | differs from the implementation of Filter Factory in many ways. |
55 | differs from the implementation of Filter Factory in many ways. |
56 | 56 | ||
57 | 1. In Filter Foundry, the random seed is automatically initialized with seed 691204. |
57 | 1. In Filter Foundry, the random seed is automatically initialized with seed 691204. |
58 | In Filter Factory, the random seed is automatically initialized with seed 0. |
58 | In Filter Factory, the random seed is automatically initialized with seed 0. |
59 | 59 | ||
60 | 2. In Filter Foundry, the argument i of the function rst(i) is limited to the |
60 | 2. In Filter Foundry, the argument i of the function rst(i) is limited to the |
61 | type "unsigned int" (argument of the function srand() in the C StdLib), |
61 | type "unsigned int" (argument of the function srand() in the C StdLib), |
62 | so the allowed range is 0..4294967295. |
62 | so the allowed range is 0..4294967295. |
63 | 63 | ||
64 | In Filter Factory, the argument i must be between 0 and 32767, inclusively. |
64 | In Filter Factory, the argument i must be between 0 and 32767, inclusively. |
65 | If the argument is not within this range, the operation "and 0x7FFF" will be applied to it |
65 | If the argument is not within this range, the operation "and 0x7FFF" will be applied to it |
66 | to extract the low 15 bits. |
66 | to extract the low 15 bits. |
67 | 67 | ||
68 | 3. In Filter Foundry, the function rnd(a,b) retrieves a random number at realtime; therefore, if the |
68 | 3. In Filter Foundry, the function rnd(a,b) retrieves a random number in realtime; therefore, if the |
69 | seed is changed via rst(i), there is an immediate effect on the next call of the rnd(a,b) function. |
69 | seed is changed via rst(i), there is an immediate effect on the next call of the rnd(a,b) function. |
70 | For example, following filter would generate an one-colored picture without any randomness: |
70 | For example, following filter would generate an one-colored picture without any randomness: |
71 | R: rst(123), rnd(0,255) |
71 | R: rst(123), rnd(0,255) |
72 | G: rnd(0,255) |
72 | G: rnd(0,255) |
73 | B: rnd(0,255) |
73 | B: rnd(0,255) |
74 | If you want to generate a random pixel image with a non-default seed, you need to make sure |
74 | If you want to generate a random pixel image with a non-default seed, you need to make sure |
75 | that rst(i) is called only once at the beginning (channel 0, coordinate 0|0): |
75 | that rst(i) is called only once at the beginning (channel 0, coordinate 0|0): |
76 | R: (x==0 && y==0) ? rst(123) : 0, rnd(0,255) |
76 | R: (x==0 && y==0) ? rst(123) : 0, rnd(0,255) |
77 | G: rnd(0,255) |
77 | G: rnd(0,255) |
78 | B: rnd(0,255) |
78 | B: rnd(0,255) |
79 | 79 | ||
80 | In Filter Factory, the rnd(a,b) function is more complex. |
80 | In Filter Factory, the rnd(a,b) function is more complex. |
81 | As soon as the function rnd(a,b) is used once, rst(i) will not have any effect. |
81 | As soon as the function rnd(a,b) is used once, rst(i) will not have any effect. |
82 | So, if you want to use rst(i), you must make sure to call it before using rnd(a,b). |
82 | So, if you want to use rst(i), you must make sure to call it before using rnd(a,b). |
83 | Following filter will generate a random pixel picture: |
83 | Following filter will generate a random pixel picture: |
84 | R: rst(123), rnd(0,255) |
84 | R: rst(123), rnd(0,255) |
85 | G: rnd(0,255) |
85 | G: rnd(0,255) |
86 | B: rnd(0,255) |
86 | B: rnd(0,255) |
87 | Following filter would generate a different random pixel picture: |
87 | Following filter would generate a different random pixel picture: |
88 | R: rst(456), rnd(0,255) |
88 | R: rst(456), rnd(0,255) |
89 | G: rnd(0,255) |
89 | G: rnd(0,255) |
90 | B: rnd(0,255) |
90 | B: rnd(0,255) |
91 | Following filter would generate the same random pixel picture: |
91 | Following filter would generate the same random pixel picture: |
92 | R: rst(456), rnd(0,255)+rst(123) <-- note that rst() always returns 0, so the '+' operator is OK |
92 | R: rst(456), rnd(0,255)+rst(123) <-- note that rst() always returns 0, so the '+' operator is OK |
93 | G: rnd(0,255) |
93 | G: rnd(0,255) |
94 | B: rnd(0,255)+rst(456) <-- the last rst(456) call is to mitigate a bug; see below. |
94 | B: rnd(0,255)+rst(456) <-- the last rst(456) call is to mitigate a bug; see below. |
95 | 95 | ||
96 | 4. In Filter Factory, due to a bug, the random state is not reset to its initial state (0) before the filter is applied: |
96 | 4. In Filter Factory, due to a bug, the random state is not reset to its initial state (0) before the filter is applied: |
97 | The preview image processing will modify the random state, and once the filter is actually applied (pressing "OK"), |
97 | The preview image processing will modify the random state, and once the filter is actually applied (pressing "OK"), |
98 | the random state that was set in the preview picture, will be used. |
98 | the random state that was set in the preview picture, will be used. |
99 | Example: |
99 | Example: |
100 | R: rnd(0,255), rst(123) <-- note that the rst(123) is ignored because rnd() was already called. |
100 | R: rnd(0,255), rst(123) <-- note that the rst(123) is ignored because rnd() was already called. |
101 | G: rnd(0,255) |
101 | G: rnd(0,255) |
102 | B: rnd(0,255) |
102 | B: rnd(0,255) |
103 | This filter will produce a random pixel picture with the initial default random seed 0, |
103 | This filter will produce a random pixel picture with the initial default random seed 0, |
104 | but only for the first calculation (i.e. in the preview picture processing, or in a standalone filter without dialog). |
104 | but only for the first calculation (i.e. in the preview picture processing, or in a standalone filter without dialog). |
105 | Any further calculation will result in a random pixel picture with random seed 123, |
105 | Any further calculation will result in a random pixel picture with random seed 123, |
106 | since the random seed 123 will be taken from the previous run. |
106 | since the random seed 123 will be taken from the previous run. |
107 | 107 | ||
108 | Furthermore, the random state can't be changed again, not even at the beginning of the red channel before any rnd() call. |
108 | Furthermore, the random state can't be changed again, not even at the beginning of the red channel before any rnd() call. |
109 | Example: |
109 | Example: |
110 | R: rst(333), rnd(0,255) |
110 | R: rst(333), rnd(0,255) |
111 | G: rnd(0,255) |
111 | G: rnd(0,255) |
112 | B: rnd(0,255)+rst(555) |
112 | B: rnd(0,255)+rst(555) |
113 | This filter will produce a picture with random seed 333 on the first calculation, |
113 | This filter will produce a picture with random seed 333 on the first calculation, |
114 | and at every further calculation, a random picture with seed 555. |
114 | and at every further calculation, a random picture with seed 555. |
115 | Another example: |
115 | Another example: |
116 | R: rst(ctl(0)), rnd(0,255) |
116 | R: rst(ctl(0)), rnd(0,255) |
117 | G: rnd(0,255) |
117 | G: rnd(0,255) |
118 | B: rnd(0,255)+rst(555) |
118 | B: rnd(0,255)+rst(555) |
119 | In this filter, the slider value is ignored and the resulting picture will always be the same. |
119 | In this filter, the slider value is ignored and the resulting picture will always be the same. |
120 | 120 | ||
121 | 121 | ||
122 | Evaluation of conditional branches |
122 | Evaluation of conditional branches |
123 | ---------------------------------- |
123 | ---------------------------------- |
124 | 124 | ||
125 | Filter Foundry: |
125 | Filter Foundry: |
126 | Only the branches which will be chosen due to the conditional expression will be evaluated. |
126 | Only the branches which will be chosen due to the conditional expression will be evaluated. |
127 | This means that following filter would generate a black canvas: (Testcase conditional_eval_1.afs) |
127 | This means that following filter would generate a black canvas: (Testcase conditional_eval_1.afs) |
128 | R: 1==0 ? put(255,0) : 0 |
128 | R: 1==0 ? put(255,0) : 0 |
129 | G: get(0) |
129 | G: get(0) |
130 | B: 0 |
130 | B: 0 |
131 | In boolean expressions, the evaluation will be aborted if the result is already determined. |
131 | In boolean expressions, the evaluation will be aborted if the result is already determined. |
132 | So, this will also generate a black canvas: (Testcase conditional_eval_2.afs) |
132 | So, this will also generate a black canvas: (Testcase conditional_eval_2.afs) |
133 | R: 1==0 && put(255,0) ? 0: 0 |
133 | R: 1==0 && put(255,0) ? 0: 0 |
134 | G: get(0) |
134 | G: get(0) |
135 | B: 0 |
135 | B: 0 |
136 | This will also generate a black canvas: (Testcase conditional_eval_3.afs) |
136 | This will also generate a black canvas: (Testcase conditional_eval_3.afs) |
137 | R: 1==1 || put(255,0) ? 0 : 0 |
137 | R: 1==1 || put(255,0) ? 0 : 0 |
138 | G: get(0) |
138 | G: get(0) |
139 | B: 0 |
139 | B: 0 |
140 | 140 | ||
141 | Filter Factory: |
141 | Filter Factory: |
142 | Each branch inside a if-then-else expression will be evaluated. |
142 | Each branch inside a if-then-else expression will be evaluated. |
143 | This means that following filter would generate a green canvas: (Testcase conditional_eval_1.afs) |
143 | This means that following filter would generate a green canvas: (Testcase conditional_eval_1.afs) |
144 | R: 1==0 ? put(255,0) : 0 |
144 | R: 1==0 ? put(255,0) : 0 |
145 | G: get(0) |
145 | G: get(0) |
146 | B: 0 |
146 | B: 0 |
147 | Also, all arguments of an boolean expression will be fully evaluated. |
147 | Also, all arguments of an boolean expression will be fully evaluated. |
148 | So, this will also generate a green canvas: (Testcase conditional_eval_2.afs) |
148 | So, this will also generate a green canvas: (Testcase conditional_eval_2.afs) |
149 | R: 1==0 && put(255,0) ? 0: 0 |
149 | R: 1==0 && put(255,0) ? 0: 0 |
150 | G: get(0) |
150 | G: get(0) |
151 | B: 0 |
151 | B: 0 |
152 | This will also generate a green canvas: (Testcase conditional_eval_3.afs) |
152 | This will also generate a green canvas: (Testcase conditional_eval_3.afs) |
153 | R: 1==1 || put(255,0) ? 0 : 0 |
153 | R: 1==1 || put(255,0) ? 0 : 0 |
154 | G: get(0) |
154 | G: get(0) |
155 | B: 0 |
155 | B: 0 |