added effectv module with BurningTV filter provided by Stephane Fillod
[melted] / src / modules / effectv / filter_burn.c
1 /*
2 * filter_burn.c -- burning filter
3 * Copyright (C) 2007 Stephane Fillod
4 *
5 * Filter taken from EffecTV - Realtime Digital Video Effector
6 * Copyright (C) 2001-2006 FUKUCHI Kentaro
7 *
8 * BurningTV - burns incoming objects.
9 * Copyright (C) 2001-2002 FUKUCHI Kentaro
10 *
11 * Fire routine is taken from Frank Jan Sorensen's demo program.
12 *
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 #include "filter_burn.h"
30
31 #include <framework/mlt_frame.h>
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <math.h>
37 #include "utils.h"
38
39
40 #define MaxColor 120
41 #define Decay 15
42 #define MAGIC_THRESHOLD "50"
43
44 static RGB32 palette[256];
45
46 /* FIXME: endianess? */
47 static void makePalette(void)
48 {
49 int i, r, g, b;
50
51 for(i=0; i<MaxColor; i++) {
52 HSItoRGB(4.6-1.5*i/MaxColor, (double)i/MaxColor, (double)i/MaxColor, &r, &g, &b);
53 palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff;
54 }
55 for(i=MaxColor; i<256; i++) {
56 if(r<255)r++;if(r<255)r++;if(r<255)r++;
57 if(g<255)g++;
58 if(g<255)g++;
59 if(b<255)b++;
60 if(b<255)b++;
61 palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff;
62 }
63 }
64
65 static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
66 {
67 RGB32 *background;
68 unsigned char *diff;
69 unsigned char *buffer;
70
71 // Get the filter
72 mlt_filter filter = mlt_frame_pop_service( this );
73
74 // Get the image
75 int error = mlt_frame_get_image( this, image, format, width, height, 1 );
76
77 // Only process if we have no error and a valid colour space
78 if ( error == 0 && *format == mlt_image_yuv422 )
79 {
80 // Get the "Burn the foreground" value
81 int burn_foreground = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "foreground" );
82 int y_threshold = image_set_threshold_y(
83 mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "threshold" ));
84
85 // We'll process pixel by pixel
86 int x = 0;
87 int y = 0;
88 int i;
89
90 int video_width = *width;
91 int video_height = *height;
92 int video_area = video_width * video_height;
93 // We need to create a new frame as this effect modifies the input
94 RGB32 *dest = mlt_pool_alloc( video_area * sizeof(RGB32) );
95 RGB32 *src = (RGB32*)dest;
96
97 unsigned char v, w;
98 RGB32 a, b;
99
100 mlt_convert_yuv422_to_rgb24a(*image, (uint8_t *)dest, video_area);
101
102
103 diff = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ),
104 "_diff", NULL );
105 if (diff == NULL)
106 {
107 diff = mlt_pool_alloc(video_area*sizeof(unsigned char));
108 mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_diff",
109 diff, video_area*sizeof(unsigned char), mlt_pool_release, NULL );
110 }
111
112 buffer = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ),
113 "_buffer", NULL );
114 if (buffer == NULL)
115 {
116 buffer = mlt_pool_alloc(video_area*sizeof(unsigned char));
117 memset(buffer, 0, video_area*sizeof(unsigned char));
118 mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_buffer",
119 buffer, video_area*sizeof(unsigned char), mlt_pool_release, NULL );
120 }
121
122
123 if (burn_foreground == 1) {
124 /* to burn the foreground, we need a background */
125 background = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ),
126 "_background", NULL );
127 if (background == NULL)
128 {
129 background = mlt_pool_alloc(video_area*sizeof(RGB32));
130 image_bgset_y(background, src, video_area, y_threshold);
131 mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_background",
132 background, video_area*sizeof(RGB32), mlt_pool_release, NULL );
133 }
134 }
135
136 if (burn_foreground == 1) {
137 image_bgsubtract_y(diff, background, src, video_area, y_threshold);
138 } else {
139 /* default */
140 image_y_over(diff, src, video_area, y_threshold);
141 }
142
143 for(x=1; x<video_width-1; x++) {
144 v = 0;
145 for(y=0; y<video_height-1; y++) {
146 w = diff[y*video_width+x];
147 buffer[y*video_width+x] |= v ^ w;
148 v = w;
149 }
150 }
151 for(x=1; x<video_width-1; x++) {
152 i = video_width + x;
153 for(y=1; y<video_height; y++) {
154 v = buffer[i];
155 if(v<Decay)
156 buffer[i-video_width] = 0;
157 else
158 buffer[i-video_width+fastrand()%3-1] = v - (fastrand()&Decay);
159 i += video_width;
160 }
161 }
162
163 i = 1;
164 for(y=0; y<video_height; y++) {
165 for(x=1; x<video_width-1; x++) {
166 /* FIXME: endianess? */
167 a = (src[i] & 0xfefeff) + palette[buffer[i]];
168 b = a & 0x1010100;
169 dest[i] = a | (b - (b >> 8));
170 i++;
171 }
172 i += 2;
173 }
174
175 mlt_convert_rgb24a_to_yuv422((uint8_t *)dest, *width, *height, *width * sizeof(RGB32),
176 *image, NULL );
177
178 mlt_pool_release(dest);
179 }
180
181 return error;
182 }
183
184 /** Filter processing.
185 */
186
187 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
188 {
189 // Push the frame filter
190 mlt_frame_push_service( frame, this );
191 mlt_frame_push_get_image( frame, filter_get_image );
192
193 return frame;
194 }
195
196 /** Constructor for the filter.
197 */
198
199 mlt_filter filter_burn_init( char *arg )
200 {
201 mlt_filter this = mlt_filter_new( );
202 if ( this != NULL )
203 {
204 this->process = filter_process;
205 mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "foreground", "0" );
206 mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "threshold", MAGIC_THRESHOLD );
207 }
208 if (!palette[128])
209 {
210 makePalette();
211 }
212 return this;
213 }
214