]> git.sesse.net Git - mlt/blob - src/modules/effectv/filter_burn.c
d5a579b56c1bd863967e4399d83154773941f95c
[mlt] / 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