1
+ /**
2
+ * Copyright (c) 2023 tuorqai
3
+ *
4
+ * This software is provided 'as-is', without any express or implied
5
+ * warranty. In no event will the authors be held liable for any damages
6
+ * arising from the use of this software.
7
+ *
8
+ * Permission is granted to anyone to use this software for any purpose,
9
+ * including commercial applications, and to alter it and redistribute it
10
+ * freely, subject to the following restrictions:
11
+ *
12
+ * 1. The origin of this software must not be misrepresented; you must not
13
+ * claim that you wrote the original software. If you use this software
14
+ * in a product, an acknowledgment in the product documentation would be
15
+ * appreciated but is not required.
16
+ * 2. Altered source versions must be plainly marked as such, and must not be
17
+ * misrepresented as being the original software.
18
+ * 3. This notice may not be removed or altered from any source distribution.
19
+ */
20
+
21
+ /**
22
+ * secamiz0r.c: SECAM fire frei0r plugin.
23
+ */
1
24
2
25
#include <math.h>
3
26
#include <stdlib.h>
4
27
#include "frei0r.h"
5
28
29
+ /**
30
+ * Limit integer value to the range.
31
+ */
32
+ static int clamp_int (int value , int a , int b )
33
+ {
34
+ return (value < a ) ? a : ((value > b ) ? b : value );
35
+ }
36
+
37
+ /**
38
+ * Limit value to the 8-bit range.
39
+ */
40
+ static uint8_t clamp_byte (int value )
41
+ {
42
+ return (uint8_t ) clamp_int (value , 0 , 255 );
43
+ }
44
+
45
+ /**
46
+ * Convert 24-bit RGB value to array of floating-point values.
47
+ */
48
+ static void unpack_rgb (float * rgb , uint8_t const * src )
49
+ {
50
+ rgb [0 ] = ((float ) src [0 ]) / 255.f ;
51
+ rgb [1 ] = ((float ) src [1 ]) / 255.f ;
52
+ rgb [2 ] = ((float ) src [2 ]) / 255.f ;
53
+ }
54
+
55
+ /**
56
+ * Calculate 8-bit luminance value from floating-point RGB value.
57
+ */
58
+ static uint8_t y_from_rgb (float const * rgb )
59
+ {
60
+ return (uint8_t ) (16.0 + (65.7380 * rgb [0 ]) + (129.057 * rgb [1 ]) + (25.0640 * rgb [2 ]));
61
+ }
62
+
63
+ /**
64
+ * Calculate 8-bit B-Y value from floating-point RGB value.
65
+ */
66
+ static uint8_t u_from_rgb (float const * rgb )
67
+ {
68
+ return (uint8_t ) (128.0 - (37.9450 * rgb [0 ]) - (74.4940 * rgb [1 ]) + (112.439 * rgb [2 ]));
69
+ }
70
+
71
+ /**
72
+ * Calculate 8-bit R-Y value from floating-point RGB value.
73
+ */
74
+ static uint8_t v_from_rgb (float const * rgb )
75
+ {
76
+ return (uint8_t ) (128.0 + (112.439 * rgb [0 ]) - (94.1540 * rgb [1 ]) - (18.2850 * rgb [2 ]));
77
+ }
78
+
79
+ /**
80
+ * Convert floating-point YUV value to 24-bit RGB.
81
+ */
82
+ static void rgb_from_yuv (uint8_t * dst , float y , float u , float v )
83
+ {
84
+ dst [0 ] = clamp_byte ((int ) ((298.082 * y ) + (408.583 * v ) - 222.921 ));
85
+ dst [1 ] = clamp_byte ((int ) ((298.082 * y ) - (100.291 * u ) - (208.120 * v ) + 135.576 ));
86
+ dst [2 ] = clamp_byte ((int ) ((298.082 * y ) + (516.412 * u ) - 276.836 ));
87
+ }
88
+
89
+ /**
90
+ * Unsigned modulo.
91
+ */
92
+ static unsigned int umod (int a , int b )
93
+ {
94
+ return ((a % b ) + b ) % b ;
95
+ }
96
+
97
+ /**
98
+ * Do I have any idea what this does?
99
+ * Make random integer out of random integer.
100
+ */
101
+ static int juice (int j )
102
+ {
103
+ j ^= j << 13 ;
104
+ j ^= j >> 17 ;
105
+ j ^= j << 5 ;
106
+
107
+ return j ;
108
+ }
109
+
110
+ /**
111
+ * secamiz0r instance struct.
112
+ */
6
113
struct secamiz0r
7
114
{
8
115
unsigned int width ;
@@ -19,28 +126,64 @@ struct secamiz0r
19
126
int echo_offset ;
20
127
};
21
128
129
+ /**
130
+ * Some values are dependent on "fire intensity" parameter.
131
+ */
132
+ static void set_fire_intensity (struct secamiz0r * self , double fire_intensity )
133
+ {
134
+ double const x = self -> fire_intensity = fire_intensity ;
135
+
136
+ self -> fire_threshold = 1024 - (int ) (x * x * 256.0 );
137
+ self -> fire_seed = (int ) (x * 1024.0 );
138
+ }
139
+
140
+ /**
141
+ * Some values are dependent on "noise intensity" parameter.
142
+ */
143
+ static void set_noise_intensity (struct secamiz0r * self , double noise_intensity )
144
+ {
145
+ double const x = self -> noise_intensity = noise_intensity ;
146
+
147
+ self -> luma_noise = clamp_int ((int ) (x * x * 256.0 ), 16 , 224 );
148
+ self -> chroma_noise = clamp_int ((int ) (x * 256.0 ), 32 , 256 );
149
+ self -> echo_offset = clamp_int ((int ) (x * 8.0 ), 2 , 16 );
150
+ }
151
+
152
+ /**
153
+ * frei0r plugin entry point: seems to be deprecated.
154
+ */
22
155
int f0r_init ()
23
156
{
24
- return 1 ;
157
+ return 1 ;
25
158
}
26
159
160
+ /**
161
+ * Same as f0r_init(), no signs of usage.
162
+ */
27
163
void f0r_deinit ()
28
164
{
29
165
}
30
166
167
+ /**
168
+ * Expose some metadata.
169
+ */
31
170
void f0r_get_plugin_info (f0r_plugin_info_t * info )
32
171
{
33
172
info -> name = "secamiz0r" ;
34
173
info -> author = "tuorqai" ;
35
174
info -> plugin_type = F0R_PLUGIN_TYPE_FILTER ;
36
175
info -> color_model = F0R_COLOR_MODEL_RGBA8888 ;
37
176
info -> frei0r_version = FREI0R_MAJOR_VERSION ;
38
- info -> major_version = 1 ;
177
+ info -> major_version = 2 ;
39
178
info -> minor_version = 0 ;
40
179
info -> num_params = 2 ;
41
180
info -> explanation = "SECAM Fire effect" ;
42
181
}
43
182
183
+ /**
184
+ * Expose parameter metadata.
185
+ * Note: kdenlive uses .name field to refer to these.
186
+ */
44
187
void f0r_get_param_info (f0r_param_info_t * info , int index )
45
188
{
46
189
switch (index ) {
@@ -59,28 +202,9 @@ void f0r_get_param_info(f0r_param_info_t *info, int index)
59
202
}
60
203
}
61
204
62
- static int clamp_int (int value , int a , int b )
63
- {
64
- return (value < a ) ? a : ((value > b ) ? b : value );
65
- }
66
-
67
- static void set_fire_intensity (struct secamiz0r * self , double fire_intensity )
68
- {
69
- double const x = self -> fire_intensity = fire_intensity ;
70
-
71
- self -> fire_threshold = 1024 - (int ) (x * x * 256.0 );
72
- self -> fire_seed = (int ) (x * 1024.0 );
73
- }
74
-
75
- static void set_noise_intensity (struct secamiz0r * self , double noise_intensity )
76
- {
77
- double const x = self -> noise_intensity = noise_intensity ;
78
-
79
- self -> luma_noise = clamp_int ((int ) (x * x * 256.0 ), 16 , 224 );
80
- self -> chroma_noise = clamp_int ((int ) (x * 256.0 ), 32 , 256 );
81
- self -> echo_offset = clamp_int ((int ) (x * 8.0 ), 2 , 16 );
82
- }
83
-
205
+ /**
206
+ * The actual entry point of a frei0r plugin.
207
+ */
84
208
f0r_instance_t f0r_construct (unsigned int width , unsigned int height )
85
209
{
86
210
struct secamiz0r * self = malloc (sizeof (* self ));
@@ -99,11 +223,17 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
99
223
return self ;
100
224
}
101
225
226
+ /**
227
+ * Don't forget to turn off your TV.
228
+ */
102
229
void f0r_destruct (f0r_instance_t instance )
103
230
{
104
231
free (instance );
105
232
}
106
233
234
+ /**
235
+ * Parameter value setter.
236
+ */
107
237
void f0r_set_param_value (f0r_instance_t instance , f0r_param_t param , int index )
108
238
{
109
239
struct secamiz0r * self = instance ;
@@ -120,6 +250,9 @@ void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int index)
120
250
}
121
251
}
122
252
253
+ /**
254
+ * Parameter value getter.
255
+ */
123
256
void f0r_get_param_value (f0r_instance_t instance , f0r_param_t param , int index )
124
257
{
125
258
struct secamiz0r * self = instance ;
@@ -136,40 +269,13 @@ void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int index)
136
269
}
137
270
}
138
271
139
- static void unpack_rgb (float * rgb , uint8_t const * src )
140
- {
141
- rgb [0 ] = ((float ) src [0 ]) / 255.f ;
142
- rgb [1 ] = ((float ) src [1 ]) / 255.f ;
143
- rgb [2 ] = ((float ) src [2 ]) / 255.f ;
144
- }
145
-
146
- static uint8_t y_from_rgb (float const * rgb )
147
- {
148
- return (uint8_t ) (16.0 + (65.7380 * rgb [0 ]) + (129.057 * rgb [1 ]) + (25.0640 * rgb [2 ]));
149
- }
150
-
151
- static uint8_t u_from_rgb (float const * rgb )
152
- {
153
- return (uint8_t ) (128.0 - (37.9450 * rgb [0 ]) - (74.4940 * rgb [1 ]) + (112.439 * rgb [2 ]));
154
- }
155
-
156
- static uint8_t v_from_rgb (float const * rgb )
157
- {
158
- return (uint8_t ) (128.0 + (112.439 * rgb [0 ]) - (94.1540 * rgb [1 ]) - (18.2850 * rgb [2 ]));
159
- }
160
-
161
- static uint8_t clamp_byte (int value )
162
- {
163
- return (uint8_t ) clamp_int (value , 0 , 255 );
164
- }
165
-
166
- static void rgb_from_yuv (uint8_t * dst , float y , float u , float v )
167
- {
168
- dst [0 ] = clamp_byte ((int ) ((298.082 * y ) + (408.583 * v ) - 222.921 ));
169
- dst [1 ] = clamp_byte ((int ) ((298.082 * y ) - (100.291 * u ) - (208.120 * v ) + 135.576 ));
170
- dst [2 ] = clamp_byte ((int ) ((298.082 * y ) + (516.412 * u ) - 276.836 ));
171
- }
172
-
272
+ /**
273
+ * Filtering Stage 1. Copy two consecutive pixel rows from source buffer to the destination
274
+ * buffer, converting it to YUV on the fly.
275
+ * We need to do this, because this plugin aims not to allocate memory at all (with the
276
+ * exception of secamiz0r struct in f0r_construct()). So the only available storage for us
277
+ * is the destination buffer provided by frei0r itself.
278
+ */
173
279
static void copy_pair_as_yuv (struct secamiz0r * self , uint8_t * dst_even , uint8_t * dst_odd , uint8_t const * src_even , uint8_t const * src_odd )
174
280
{
175
281
for (size_t i = 0 ; i < self -> width ; i += 2 ) {
@@ -224,20 +330,14 @@ static void copy_pair_as_yuv(struct secamiz0r *self, uint8_t *dst_even, uint8_t
224
330
}
225
331
}
226
332
227
- static int juice (int j )
228
- {
229
- j ^= j << 13 ;
230
- j ^= j >> 17 ;
231
- j ^= j << 5 ;
232
-
233
- return j ;
234
- }
235
-
236
- static unsigned int umod (int a , int b )
237
- {
238
- return ((a % b ) + b ) % b ;
239
- }
240
-
333
+ /**
334
+ * Filtering Stage 2.1. The loop inside this function works on two consecutive
335
+ * lines copied in Stage 1. It aims to detect areas where luminance level is
336
+ * changed rapidly, and then marks those areas. To achieve this, we keep summing
337
+ * up difference between two pixels, halving the sum every step (it kinda fades
338
+ * away). To make this more chaotic, we also add a random value. Then, if at
339
+ * some point the sum is larger than a threshold value, we mark this pixel.
340
+ */
241
341
static void prefilter_pair (struct secamiz0r * self , uint8_t * even , uint8_t * odd )
242
342
{
243
343
int r_even = rand ();
@@ -266,6 +366,10 @@ static void prefilter_pair(struct secamiz0r *self, uint8_t *even, uint8_t *odd)
266
366
}
267
367
}
268
368
369
+ /**
370
+ * Filtering Stage 2.2. This actually modifies the image, adding random noise
371
+ * and fires at marked areas.
372
+ */
269
373
static void filter_pair (struct secamiz0r * self , uint8_t * even , uint8_t * odd )
270
374
{
271
375
int r_even = rand ();
@@ -339,6 +443,11 @@ static void filter_pair(struct secamiz0r *self, uint8_t *even, uint8_t *odd)
339
443
}
340
444
}
341
445
446
+ /**
447
+ * Filtering Stage 3. Two consecutive YUV pixel rows, filtered in previous stages,
448
+ * now converted to RGB in place. But conversion isn't straightforward: to make
449
+ * the image look more analog, a sophisticated method is used.
450
+ */
342
451
static void convert_pair_to_rgb (struct secamiz0r * self , uint8_t * even , uint8_t * odd )
343
452
{
344
453
int const luma_loss = 4 ;
@@ -372,6 +481,10 @@ static void convert_pair_to_rgb(struct secamiz0r *self, uint8_t *even, uint8_t *
372
481
}
373
482
}
374
483
484
+ /**
485
+ * The whole process of filtering is done here.
486
+ * This function is called every frame.
487
+ */
375
488
void f0r_update (f0r_instance_t instance , double time , uint32_t const * src , uint32_t * dst )
376
489
{
377
490
struct secamiz0r * self = instance ;
0 commit comments