]> git.sesse.net Git - mlt/blob - src/modules/plus/filter_lift_gamma_gain.c
5c1f74756cd5124f02c2b7cf0087a205184cd0ba
[mlt] / src / modules / plus / filter_lift_gamma_gain.c
1 /*
2  * filter_lift_gamma_gain.cpp
3  * Copyright (C) 2014 Brian Matherly <pez4brian@yahoo.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <framework/mlt.h>
21 #include <stdlib.h>
22 #include <math.h>
23
24 typedef struct
25 {
26         uint8_t rlut[256];
27         uint8_t glut[256];
28         uint8_t blut[256];
29         double rlift, glift, blift;
30         double rgamma, ggamma, bgamma;
31         double rgain, ggain, bgain;
32 } private_data;
33
34 static void refresh_lut( mlt_filter filter, mlt_frame frame )
35 {
36         private_data* private = (private_data*)filter->child;
37         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
38         mlt_position position = mlt_filter_get_position( filter, frame );
39         mlt_position length = mlt_filter_get_length2( filter, frame );
40         double rlift = mlt_properties_anim_get_double( properties, "lift_r", position, length );
41         double glift = mlt_properties_anim_get_double( properties, "lift_g", position, length );
42         double blift = mlt_properties_anim_get_double( properties, "lift_b", position, length );
43         double rgamma = mlt_properties_anim_get_double( properties, "gamma_r", position, length );
44         double ggamma = mlt_properties_anim_get_double( properties, "gamma_g", position, length );
45         double bgamma = mlt_properties_anim_get_double( properties, "gamma_b", position, length );
46         double rgain = mlt_properties_anim_get_double( properties, "gain_r", position, length );
47         double ggain = mlt_properties_anim_get_double( properties, "gain_g", position, length );
48         double bgain = mlt_properties_anim_get_double( properties, "gain_b", position, length );
49
50         // Only regenerate the LUT if something changed.
51         if( private->rlift != rlift || private->glift != glift || private->blift != blift ||
52                 private->rgamma != rgamma || private->ggamma != ggamma || private->bgamma != bgamma ||
53                 private->rgain != rgain || private->ggain != ggain || private->bgain != bgain )
54         {
55                 int i = 0;
56                 for( i = 0; i < 256; i++ )
57                 {
58                         // Convert to gamma 2.2
59                         double gamma22 = pow( (double)i / 255.0, 1.0f / 2.2f );
60                         double r = gamma22;
61                         double g = gamma22;
62                         double b = gamma22;
63
64                         // Apply lift
65                         r += rlift * ( 1 - r );
66                         g += glift * ( 1 - g );
67                         b += blift * ( 1 - b );
68
69                         // Apply gamma
70                         r = pow( r, 2.2f / rgamma );
71                         g = pow( g, 2.2f / ggamma );
72                         b = pow( b, 2.2f / bgamma );
73
74                         // Apply gain
75                         r *= pow( rgain, 1.0f / rgamma );
76                         g *= pow( ggain, 1.0f / ggamma );
77                         b *= pow( bgain, 1.0f / bgamma );
78
79                         // Update LUT
80                         private->rlut[ i ] = (int)(r * 255.0);
81                         private->glut[ i ] = (int)(g * 255.0);
82                         private->blut[ i ] = (int)(b * 255.0);
83                 }
84
85                 // Store the values that created the LUT so that
86                 // changes can be detected.
87                 private->rlift = rlift;
88                 private->glift = glift;
89                 private->blift = blift;
90                 private->rgamma = rgamma;
91                 private->ggamma = ggamma;
92                 private->bgamma = bgamma;
93                 private->rgain = rgain;
94                 private->ggain = ggain;
95                 private->bgain = bgain;
96         }
97 }
98
99 static void apply_lut( mlt_filter filter, uint8_t* image, mlt_image_format format, int width, int height )
100 {
101         private_data* private = (private_data*)filter->child;
102         int total = width * height + 1;
103         uint8_t* sample = image;
104
105         switch( format )
106         {
107         case mlt_image_rgb24:
108                 while( --total )
109                 {
110                         *sample = private->rlut[ *sample ];
111                         sample++;
112                         *sample = private->glut[ *sample ];
113                         sample++;
114                         *sample = private->blut[ *sample ];
115                         sample++;
116                 }
117                 break;
118         case mlt_image_rgb24a:
119                 while( --total )
120                 {
121                         *sample = private->rlut[ *sample ];
122                         sample++;
123                         *sample = private->glut[ *sample ];
124                         sample++;
125                         *sample = private->blut[ *sample ];
126                         sample++;
127                         sample++; // Skip alpha
128                 }
129                 break;
130         default:
131                 mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid image format: %s\n", mlt_image_format_name( format ) );
132                 break;
133         }
134 }
135
136 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
137 {
138         mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame );
139         int error = 0;
140
141         mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
142
143         // Regenerate the LUT if necessary
144         refresh_lut( filter, frame );
145
146         // Make sure the format is acceptable
147         if( *format != mlt_image_rgb24 && *format != mlt_image_rgb24a )
148         {
149                 *format = mlt_image_rgb24;
150         }
151
152         // Get the image
153         writable = 1;
154         error = mlt_frame_get_image( frame, image, format, width, height, writable );
155
156         // Apply the LUT
157         if( !error )
158         {
159                 apply_lut( filter, *image, *format, *width, *height );
160         }
161
162         mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
163
164         return error;
165 }
166
167 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
168 {
169         mlt_frame_push_service( frame, filter );
170         mlt_frame_push_get_image( frame, filter_get_image );
171         return frame;
172 }
173
174 static void filter_close( mlt_filter filter )
175 {
176         private_data* private = (private_data*)filter->child;
177
178         if ( private )
179         {
180                 free( private );
181         }
182         filter->child = NULL;
183         filter->close = NULL;
184         filter->parent.close = NULL;
185         mlt_service_close( &filter->parent );
186 }
187
188 mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
189 {
190         mlt_filter filter = mlt_filter_new();
191         private_data* private = (private_data*)calloc( 1, sizeof(private_data) );
192         int i = 0;
193
194         if ( filter && private )
195         {
196                 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
197
198                 // Initialize private data
199                 for( i = 0; i < 256; i++ )
200                 {
201                         private->rlut[i] = i;
202                         private->glut[i] = i;
203                         private->blut[i] = i;
204                 }
205                 private->rlift = private->glift = private->blift = 0.0;
206                 private->rgamma = private->ggamma = private->bgamma = 1.0;
207                 private->rgain = private->ggain = private->bgain = 1.0;
208
209                 // Initialize filter properties
210                 mlt_properties_set_double( properties, "lift_r", private->rlift );
211                 mlt_properties_set_double( properties, "lift_g", private->glift );
212                 mlt_properties_set_double( properties, "lift_b", private->blift );
213                 mlt_properties_set_double( properties, "gamma_r", private->rgamma );
214                 mlt_properties_set_double( properties, "gamma_g", private->ggamma );
215                 mlt_properties_set_double( properties, "gamma_b", private->bgamma );
216                 mlt_properties_set_double( properties, "gain_r", private->rgain );
217                 mlt_properties_set_double( properties, "gain_g", private->ggain );
218                 mlt_properties_set_double( properties, "gain_b", private->bgain );
219
220                 filter->close = filter_close;
221                 filter->process = filter_process;
222                 filter->child = private;
223         }
224         else
225         {
226                 mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter lift_gamma_gain init failed\n" );
227
228                 if( filter )
229                 {
230                         mlt_filter_close( filter );
231                         filter = NULL;
232                 }
233
234                 if( private )
235                 {
236                         free( private );
237                 }
238         }
239
240         return filter;
241 }