Skip to content

Commit de76de7

Browse files
committed
Fri, 19 Jul, 2024 11:39:59
1 parent 2f0f53a commit de76de7

File tree

2 files changed

+204
-72
lines changed

2 files changed

+204
-72
lines changed

LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2023 tuorqai
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

secamiz0r.c

+185-72
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,115 @@
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+
*/
124

225
#include <math.h>
326
#include <stdlib.h>
427
#include "frei0r.h"
528

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+
*/
6113
struct secamiz0r
7114
{
8115
unsigned int width;
@@ -19,28 +126,64 @@ struct secamiz0r
19126
int echo_offset;
20127
};
21128

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+
*/
22155
int f0r_init()
23156
{
24-
return 1;
157+
return 1;
25158
}
26159

160+
/**
161+
* Same as f0r_init(), no signs of usage.
162+
*/
27163
void f0r_deinit()
28164
{
29165
}
30166

167+
/**
168+
* Expose some metadata.
169+
*/
31170
void f0r_get_plugin_info(f0r_plugin_info_t *info)
32171
{
33172
info->name = "secamiz0r";
34173
info->author = "tuorqai";
35174
info->plugin_type = F0R_PLUGIN_TYPE_FILTER;
36175
info->color_model = F0R_COLOR_MODEL_RGBA8888;
37176
info->frei0r_version = FREI0R_MAJOR_VERSION;
38-
info->major_version = 1;
177+
info->major_version = 2;
39178
info->minor_version = 0;
40179
info->num_params = 2;
41180
info->explanation = "SECAM Fire effect";
42181
}
43182

183+
/**
184+
* Expose parameter metadata.
185+
* Note: kdenlive uses .name field to refer to these.
186+
*/
44187
void f0r_get_param_info(f0r_param_info_t *info, int index)
45188
{
46189
switch (index) {
@@ -59,28 +202,9 @@ void f0r_get_param_info(f0r_param_info_t *info, int index)
59202
}
60203
}
61204

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+
*/
84208
f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
85209
{
86210
struct secamiz0r *self = malloc(sizeof(*self));
@@ -99,11 +223,17 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
99223
return self;
100224
}
101225

226+
/**
227+
* Don't forget to turn off your TV.
228+
*/
102229
void f0r_destruct(f0r_instance_t instance)
103230
{
104231
free(instance);
105232
}
106233

234+
/**
235+
* Parameter value setter.
236+
*/
107237
void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int index)
108238
{
109239
struct secamiz0r *self = instance;
@@ -120,6 +250,9 @@ void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int index)
120250
}
121251
}
122252

253+
/**
254+
* Parameter value getter.
255+
*/
123256
void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int index)
124257
{
125258
struct secamiz0r *self = instance;
@@ -136,40 +269,13 @@ void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int index)
136269
}
137270
}
138271

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+
*/
173279
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)
174280
{
175281
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
224330
}
225331
}
226332

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+
*/
241341
static void prefilter_pair(struct secamiz0r *self, uint8_t *even, uint8_t *odd)
242342
{
243343
int r_even = rand();
@@ -266,6 +366,10 @@ static void prefilter_pair(struct secamiz0r *self, uint8_t *even, uint8_t *odd)
266366
}
267367
}
268368

369+
/**
370+
* Filtering Stage 2.2. This actually modifies the image, adding random noise
371+
* and fires at marked areas.
372+
*/
269373
static void filter_pair(struct secamiz0r *self, uint8_t *even, uint8_t *odd)
270374
{
271375
int r_even = rand();
@@ -339,6 +443,11 @@ static void filter_pair(struct secamiz0r *self, uint8_t *even, uint8_t *odd)
339443
}
340444
}
341445

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+
*/
342451
static void convert_pair_to_rgb(struct secamiz0r *self, uint8_t *even, uint8_t *odd)
343452
{
344453
int const luma_loss = 4;
@@ -372,6 +481,10 @@ static void convert_pair_to_rgb(struct secamiz0r *self, uint8_t *even, uint8_t *
372481
}
373482
}
374483

484+
/**
485+
* The whole process of filtering is done here.
486+
* This function is called every frame.
487+
*/
375488
void f0r_update(f0r_instance_t instance, double time, uint32_t const* src, uint32_t *dst)
376489
{
377490
struct secamiz0r *self = instance;

0 commit comments

Comments
 (0)