Rev 286 | Rev 294 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
141 | dmarschall | 1 | |
221 | daniel-mar | 2 | Implementation detail differences |
3 | ================================= |
||
141 | dmarschall | 4 | |
144 | dmarschall | 5 | FilterFoundry tries to be as compatible with Filter Factory as possible. |
192 | daniel-mar | 6 | However, results are usually not 100% equal because functions like |
7 | cos, sin, sqr, etc., have different accuracy due to the underlying |
||
141 | dmarschall | 8 | implementation. |
9 | |||
192 | daniel-mar | 10 | Furthermore, there are the following known differences between |
11 | Filter Foundry and Filter Factory: |
||
141 | dmarschall | 12 | |
158 | dmarschall | 13 | i, u, v (Testcase iuv.afs) |
141 | dmarschall | 14 | ------- |
15 | |||
16 | Filter Foundry <1.7 uses the same formulas as in Filter Factory: |
||
17 | |||
221 | daniel-mar | 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 |
||
20 | v=((78*r)+(-65*g)+(-13*b))/256 // Output range is -77..77 |
||
141 | dmarschall | 21 | |
22 | Filter Foundry 1.7 uses more accurate formulas: |
||
23 | |||
221 | daniel-mar | 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 |
||
26 | v=614777*r-514799*g-99978*b)/2000000 // Output range is -78..78 |
||
141 | dmarschall | 27 | |
192 | daniel-mar | 28 | Both formulas follow the same YUV standard but have different accuracy. |
141 | dmarschall | 29 | |
30 | |||
31 | get(i) (Testcase getput.afs) |
||
32 | ------ |
||
33 | |||
34 | Filter Foundry: |
||
35 | |||
221 | daniel-mar | 36 | get(x)=0 if x>255 or x<0 |
37 | |||
141 | dmarschall | 38 | Filter Factory: |
39 | |||
221 | daniel-mar | 40 | get(x)=x if x>255 or x<0 |
141 | dmarschall | 41 | |
221 | daniel-mar | 42 | Note: The result "x" was most likely not intended but a result of an undefined behavior |
43 | |||
44 | |||
141 | dmarschall | 45 | r, g, b at empty canvas (Testcase emptycanvas.afs) |
46 | ----------------------- |
||
47 | |||
221 | daniel-mar | 48 | In Filter Factory, an empty (transparent) canvas of a new file is initialized as `r=g=b=0` |
141 | dmarschall | 49 | |
221 | daniel-mar | 50 | Filter Foundry initializes it as `r=g=b=255` |
141 | dmarschall | 51 | |
52 | |||
289 | daniel-mar | 53 | rnd(a,b) (Testcases rnd_*) |
54 | -------- |
||
55 | |||
56 | Filter Factory uses Donald E. Knuth's subtractive random number generator algorithm, |
||
57 | which has been published in "The Art of Computer Programming, volume 2: Seminumerical Algorithms". |
||
58 | Addison-Wesley, Reading, MA, second edition, 1981. |
||
59 | |||
60 | Beginning with Filter Foundry 1.7.0.8, the same PRNG was implemented, |
||
61 | so that the output of rnd(a,b) is exactly the same now. |
||
62 | |||
63 | |||
64 | rnd(a,b) and rst(i) (Testcases rnd*.afs and rst_*.afs) |
||
141 | dmarschall | 65 | ------------------- |
66 | |||
289 | daniel-mar | 67 | Filter Factory contains an undocumented function that sets the seed for the random number generator. |
141 | dmarschall | 68 | |
289 | daniel-mar | 69 | Filter Factory and FilterFoundry beginning with 1.7.0.8 accept a seed between 0 and 32767, inclusively. |
70 | If the argument is not within this range, the operation "and 0x7FFF" will be applied to it |
||
71 | to extract the low 15 bits. |
||
141 | dmarschall | 72 | |
289 | daniel-mar | 73 | There are many differences in the implementation between FilterFactory and FilterFoundry in regards rst(i): |
141 | dmarschall | 74 | |
289 | daniel-mar | 75 | **Filter Factory:** |
143 | dmarschall | 76 | |
289 | daniel-mar | 77 | If rst(i) is called in Filter Factory, an internal Seed-Variable is set. |
78 | It does NOT influence any calls of rnd(a,b), because a lookup-table needs to be built first. |
||
79 | The building of the lookup-table is probably done before the processing of the first pixel (x,y,z=0). |
||
80 | It is suspected that the call of rst(i) will take effect on the next calculation. |
||
81 | Due to a bug (or feature?), the random state is not reset to its initial state (0) before the |
||
82 | filter is applied. The preview image processing will modify the random state, and once the filter |
||
83 | is actually applied (pressing "OK"), the random state that was set in the preview picture, will be used. |
||
84 | This could be considered as a bug, but it is probably required, otherwise the call of rst(i) |
||
85 | (inside the preview calculation) won't affect the rnd(a,b) in the real run. |
||
86 | However, in a standalone filter without dialog/preview, there is no preview that could set |
||
87 | the internal seed, so the rnd(a,b) functions will always work using the default seed 0, |
||
88 | and only the subsequent calls will use the rst(i) of the previous call. |
||
221 | daniel-mar | 89 | |
289 | daniel-mar | 90 | **Filter Foundry:** |
143 | dmarschall | 91 | |
289 | daniel-mar | 92 | In Filter Foundry, the function rnd(a,b) retrieves a random number in "realtime"; therefore, if the |
93 | seed is changed via rst(i), there is an immediate effect on the next call of the rnd(a,b) function. |
||
94 | |||
95 | For example, following filter would generate an one-colored picture without any randomness: |
||
221 | daniel-mar | 96 | R: rst(123), rnd(0,255) |
97 | G: rnd(0,255) |
||
98 | B: rnd(0,255) |
||
144 | dmarschall | 99 | |
289 | daniel-mar | 100 | If you want to generate a random pixel image with a non-default seed, you need to make sure |
101 | that rst(i) is called only once at the beginning (channel 0, coordinate 0|0): |
||
102 | R: (x==0 && y==0) ? rst(123) : 0, rnd(0,255) |
||
221 | daniel-mar | 103 | G: rnd(0,255) |
104 | B: rnd(0,255) |
||
144 | dmarschall | 105 | |
289 | daniel-mar | 106 | In Filter Foundry, rst(i) can be called by branches and variables/sliders can |
107 | be used as arguments of rst(i). |
||
144 | dmarschall | 108 | |
109 | |||
143 | dmarschall | 110 | Evaluation of conditional branches |
111 | ---------------------------------- |
||
112 | |||
221 | daniel-mar | 113 | **Filter Foundry:** |
144 | dmarschall | 114 | |
221 | daniel-mar | 115 | Only the branches which will be chosen due to the conditional expression will be evaluated. |
116 | |||
117 | This means that following filter would generate a black canvas: (Testcase conditional_eval_1.afs) |
||
118 | |||
119 | R: 1==0 ? put(255,0) : 0 |
||
120 | G: get(0) |
||
121 | B: 0 |
||
122 | |||
123 | In boolean expressions, the evaluation will be aborted if the result is already determined. |
||
124 | |||
125 | So, this will also generate a black canvas: (Testcase conditional_eval_2.afs) |
||
126 | |||
127 | R: 1==0 && put(255,0) ? 0: 0 |
||
128 | G: get(0) |
||
129 | B: 0 |
||
130 | |||
131 | This will also generate a black canvas: (Testcase conditional_eval_3.afs) |
||
132 | |||
133 | R: 1==1 || put(255,0) ? 0 : 0 |
||
134 | G: get(0) |
||
135 | B: 0 |
||
136 | |||
137 | **Filter Factory:** |
||
138 | |||
139 | Each branch inside a if-then-else expression will be evaluated. |
||
140 | This means that following filter would generate a green canvas: (Testcase conditional_eval_1.afs) |
||
141 | |||
142 | R: 1==0 ? put(255,0) : 0 |
||
143 | G: get(0) |
||
144 | B: 0 |
||
145 | |||
146 | Also, all arguments of an boolean expression will be fully evaluated. |
||
147 | So, this will also generate a green canvas: (Testcase conditional_eval_2.afs) |
||
148 | |||
149 | R: 1==0 && put(255,0) ? 0: 0 |
||
150 | G: get(0) |
||
151 | B: 0 |
||
152 | |||
153 | This will also generate a green canvas: (Testcase conditional_eval_3.afs) |
||
154 | |||
155 | R: 1==1 || put(255,0) ? 0 : 0 |
||
156 | G: get(0) |
||
157 | B: 0 |