]> git.sesse.net Git - mlt/blob - src/modules/plus/filter_lift_gamma_gain.c
Clamp out of range values.
[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.0 / 2.2 );
60                         double r = gamma22;
61                         double g = gamma22;
62                         double b = gamma22;
63
64                         // Apply lift
65                         r += rlift * ( 1.0 - r );
66                         g += glift * ( 1.0 - g );
67                         b += blift * ( 1.0 - b );
68
69                         // Apply gamma
70                         r = pow( r, 2.2 / rgamma );
71                         g = pow( g, 2.2 / ggamma );
72                         b = pow( b, 2.2 / bgamma );
73
74                         // Apply gain
75                         r *= pow( rgain, 1.0 / rgamma );
76                         g *= pow( ggain, 1.0 / ggamma );
77                         b *= pow( bgain, 1.0 / bgamma );
78
79                         // Clamp values
80                         r = r < 0.0 ? 0.0 : r > 1.0 ? 1.0 : r;
81                         g = g < 0.0 ? 0.0 : g > 1.0 ? 1.0 : g;
82                         b = b < 0.0 ? 0.0 : b > 1.0 ? 1.0 : b;
83
84                         // Update LUT
85                         private->rlut[ i ] = (int)(r * 255.0);
86                         private->glut[ i ] = (int)(g * 255.0);
87                         private->blut[ i ] = (int)(b * 255.0);
88                 }
89
90                 // Store the values that created the LUT so that
91                 // changes can be detected.
92                 private->rlift = rlift;
93                 private->glift = glift;
94                 private->blift = blift;
95                 private->rgamma = rgamma;
96                 private->ggamma = ggamma;
97                 private->bgamma = bgamma;
98                 private->rgain = rgain;
99                 private->ggain = ggain;
100                 private->bgain = bgain;
101         }
102 }
103
104 static void apply_lut( mlt_filter filter, uint8_t* image, mlt_image_format format, int width, int height )
105 {
106         private_data* private = (private_data*)filter->child;
107         int total = width * height + 1;
108         uint8_t* sample = image;
109
110         switch( format )
111         {
112         case mlt_image_rgb24:
113                 while( --total )
114                 {
115                         *sample = private->rlut[ *sample ];
116                         sample++;
117                         *sample = private->glut[ *sample ];
118                         sample++;
119                         *sample = private->blut[ *sample ];
120                         sample++;
121                 }
122                 break;
123         case mlt_image_rgb24a:
124                 while( --total )
125                 {
126                         *sample = private->rlut[ *sample ];
127                         sample++;
128                         *sample = private->glut[ *sample ];
129                         sample++;
130                         *sample = private->blut[ *sample ];
131                         sample++;
132                         sample++; // Skip alpha
133                 }
134                 break;
135         default:
136                 mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid image format: %s\n", mlt_image_format_name( format ) );
137                 break;
138         }
139 }
140
141 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
142 {
143         mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame );
144         int error = 0;
145
146         mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
147
148         // Regenerate the LUT if necessary
149         refresh_lut( filter, frame );
150
151         // Make sure the format is acceptable
152         if( *format != mlt_image_rgb24 && *format != mlt_image_rgb24a )
153         {
154                 *format = mlt_image_rgb24;
155         }
156
157         // Get the image
158         writable = 1;
159         error = mlt_frame_get_image( frame, image, format, width, height, writable );
160
161         // Apply the LUT
162         if( !error )
163         {
164                 apply_lut( filter, *image, *format, *width, *height );
165         }
166
167         mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
168
169         return error;
170 }
171
172 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
173 {
174         mlt_frame_push_service( frame, filter );
175         mlt_frame_push_get_image( frame, filter_get_image );
176         return frame;
177 }
178
179 static void filter_close( mlt_filter filter )
180 {
181         private_data* private = (private_data*)filter->child;
182
183         if ( private )
184         {
185                 free( private );
186         }
187         filter->child = NULL;
188         filter->close = NULL;
189         filter->parent.close = NULL;
190         mlt_service_close( &filter->parent );
191 }
192
193 mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
194 {
195         mlt_filter filter = mlt_filter_new();
196         private_data* private = (private_data*)calloc( 1, sizeof(private_data) );
197         int i = 0;
198
199         if ( filter && private )
200         {
201                 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
202
203                 // Initialize private data
204                 for( i = 0; i < 256; i++ )
205                 {
206                         private->rlut[i] = i;
207                         private->glut[i] = i;
208                         private->blut[i] = i;
209                 }
210                 private->rlift = private->glift = private->blift = 0.0;
211                 private->rgamma = private->ggamma = private->bgamma = 1.0;
212                 private->rgain = private->ggain = private->bgain = 1.0;
213
214                 // Initialize filter properties
215                 mlt_properties_set_double( properties, "lift_r", private->rlift );
216                 mlt_properties_set_double( properties, "lift_g", private->glift );
217                 mlt_properties_set_double( properties, "lift_b", private->blift );
218                 mlt_properties_set_double( properties, "gamma_r", private->rgamma );
219                 mlt_properties_set_double( properties, "gamma_g", private->ggamma );
220                 mlt_properties_set_double( properties, "gamma_b", private->bgamma );
221                 mlt_properties_set_double( properties, "gain_r", private->rgain );
222                 mlt_properties_set_double( properties, "gain_g", private->ggain );
223                 mlt_properties_set_double( properties, "gain_b", private->bgain );
224
225                 filter->close = filter_close;
226                 filter->process = filter_process;
227                 filter->child = private;
228         }
229         else
230         {
231                 mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter lift_gamma_gain init failed\n" );
232
233                 if( filter )
234                 {
235                         mlt_filter_close( filter );
236                         filter = NULL;
237                 }
238
239                 if( private )
240                 {
241                         free( private );
242                 }
243         }
244
245         return filter;
246 }