]> git.sesse.net Git - mlt/blob - src/modules/effectv/filter_burn.c
Massive refactoring of image conversion.
[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 <framework/mlt_filter.h>
30 #include <framework/mlt_frame.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <math.h>
36 #include "utils.h"
37
38
39 #define MaxColor 120
40 #define Decay 15
41 #define MAGIC_THRESHOLD "50"
42
43 static RGB32 palette[256];
44
45 /* FIXME: endianess? */
46 static void makePalette(void)
47 {
48         int i, r, g, b;
49
50         for(i=0; i<MaxColor; i++) {
51                 HSItoRGB(4.6-1.5*i/MaxColor, (double)i/MaxColor, (double)i/MaxColor, &r, &g, &b);
52                 palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff;
53         }
54         for(i=MaxColor; i<256; i++) {
55                 if(r<255)r++;if(r<255)r++;if(r<255)r++;
56                 if(g<255)g++;
57                 if(g<255)g++;
58                 if(b<255)b++;
59                 if(b<255)b++;
60                 palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff;
61         }
62 }
63
64 static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
65 {
66         RGB32 *background;
67         unsigned char *diff;
68         unsigned char *buffer;
69
70         // Get the filter
71         mlt_filter filter = mlt_frame_pop_service( this );
72
73         // Get the image
74         *format = mlt_image_rgb24a;
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 )
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 = (RGB32*)*image;
95                 RGB32 *src = (RGB32*)*image;
96
97                 unsigned char v, w;
98                 RGB32 a, b;
99
100                 diff = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), 
101                                                 "_diff", NULL );
102                 if (diff == NULL)
103                 {
104                         diff = mlt_pool_alloc(video_area*sizeof(unsigned char));
105                         mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_diff", 
106                                         diff, video_area*sizeof(unsigned char), mlt_pool_release, NULL );
107                 }
108
109                 buffer = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), 
110                                                 "_buffer", NULL );
111                 if (buffer == NULL)
112                 {
113                         buffer = mlt_pool_alloc(video_area*sizeof(unsigned char));
114                         memset(buffer, 0, video_area*sizeof(unsigned char));
115                         mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_buffer", 
116                                         buffer, video_area*sizeof(unsigned char), mlt_pool_release, NULL );
117                 }
118
119
120                 if (burn_foreground == 1) {
121                         /* to burn the foreground, we need a background */
122                         background = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), 
123                                                 "_background", NULL );
124                         if (background == NULL)
125                         {
126                                 background = mlt_pool_alloc(video_area*sizeof(RGB32));
127                                 image_bgset_y(background, src, video_area, y_threshold);
128                                 mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_background", 
129                                         background, video_area*sizeof(RGB32), mlt_pool_release, NULL );
130                         }
131                 }
132
133                 if (burn_foreground == 1) {
134                         image_bgsubtract_y(diff, background, src, video_area, y_threshold);
135                 } else {
136                         /* default */
137                         image_y_over(diff, src, video_area, y_threshold);
138                 }
139         
140                 for(x=1; x<video_width-1; x++) {
141                         v = 0;
142                         for(y=0; y<video_height-1; y++) {
143                                 w = diff[y*video_width+x];
144                                 buffer[y*video_width+x] |= v ^ w;
145                                 v = w;
146                         }
147                 }
148                 for(x=1; x<video_width-1; x++) {
149                         i = video_width + x;
150                         for(y=1; y<video_height; y++) {
151                                 v = buffer[i];
152                                 if(v<Decay)
153                                         buffer[i-video_width] = 0;
154                                 else
155                                         buffer[i-video_width+fastrand()%3-1] = v - (fastrand()&Decay);
156                                 i += video_width;
157                         }
158                 }
159         
160                 i = 1;
161                 for(y=0; y<video_height; y++) {
162                         for(x=1; x<video_width-1; x++) {
163                                 /* FIXME: endianess? */
164                                 a = (src[i] & 0xfefeff) + palette[buffer[i]];
165                                 b = a & 0x1010100;
166                                 dest[i] = a | (b - (b >> 8));
167                                 i++;
168                         }
169                         i += 2;
170                 }
171         }
172
173         return error;
174 }
175
176 /** Filter processing.
177 */
178
179 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
180 {
181         // Push the frame filter
182         mlt_frame_push_service( frame, this );
183         mlt_frame_push_get_image( frame, filter_get_image );
184
185         return frame;
186 }
187
188 /** Constructor for the filter.
189 */
190
191 mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
192 {
193         mlt_filter this = mlt_filter_new( );
194         if ( this != NULL )
195         {
196                 this->process = filter_process;
197                 mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "foreground", "0" );
198                 mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "threshold", MAGIC_THRESHOLD );
199         }
200         if (!palette[128])
201         {
202                 makePalette();
203         }
204         return this;
205 }
206