1 /*
2 * filter_lift_gamma_gain.cpp
3 * Copyright (C) 2014 Meltytech, LLC
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <framework/mlt.h>
21 #include <stdlib.h>
22 #include <math.h>
23 #include <string.h>
24
25 typedef struct
26 {
27 uint8_t rlut[256];
28 uint8_t glut[256];
29 uint8_t blut[256];
30 double rlift, glift, blift;
31 double rgamma, ggamma, bgamma;
32 double rgain, ggain, bgain;
33 } private_data;
34
refresh_lut(mlt_filter filter,mlt_frame frame)35 static void refresh_lut( mlt_filter filter, mlt_frame frame )
36 {
37 private_data* self = (private_data*)filter->child;
38 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
39 mlt_position position = mlt_filter_get_position( filter, frame );
40 mlt_position length = mlt_filter_get_length2( filter, frame );
41 double rlift = mlt_properties_anim_get_double( properties, "lift_r", position, length );
42 double glift = mlt_properties_anim_get_double( properties, "lift_g", position, length );
43 double blift = mlt_properties_anim_get_double( properties, "lift_b", position, length );
44 double rgamma = mlt_properties_anim_get_double( properties, "gamma_r", position, length );
45 double ggamma = mlt_properties_anim_get_double( properties, "gamma_g", position, length );
46 double bgamma = mlt_properties_anim_get_double( properties, "gamma_b", position, length );
47 double rgain = mlt_properties_anim_get_double( properties, "gain_r", position, length );
48 double ggain = mlt_properties_anim_get_double( properties, "gain_g", position, length );
49 double bgain = mlt_properties_anim_get_double( properties, "gain_b", position, length );
50
51 // Only regenerate the LUT if something changed.
52 if( self->rlift != rlift || self->glift != glift || self->blift != blift ||
53 self->rgamma != rgamma || self->ggamma != ggamma || self->bgamma != bgamma ||
54 self->rgain != rgain || self->ggain != ggain || self->bgain != bgain )
55 {
56 int i = 0;
57 for( i = 0; i < 256; i++ )
58 {
59 // Convert to gamma 2.2
60 double gamma22 = pow( (double)i / 255.0, 1.0 / 2.2 );
61 double r = gamma22;
62 double g = gamma22;
63 double b = gamma22;
64
65 // Apply lift
66 r += rlift * ( 1.0 - r );
67 g += glift * ( 1.0 - g );
68 b += blift * ( 1.0 - b );
69
70 // Clamp negative values
71 r = MAX( r, 0.0 );
72 g = MAX( g, 0.0 );
73 b = MAX( b, 0.0 );
74
75 // Apply gamma
76 r = pow( r, 2.2 / rgamma );
77 g = pow( g, 2.2 / ggamma );
78 b = pow( b, 2.2 / bgamma );
79
80 // Apply gain
81 r *= pow( rgain, 1.0 / rgamma );
82 g *= pow( ggain, 1.0 / ggamma );
83 b *= pow( bgain, 1.0 / bgamma );
84
85 // Clamp values
86 r = CLAMP( r, 0.0, 1.0 );
87 g = CLAMP( g, 0.0, 1.0 );
88 b = CLAMP( b, 0.0, 1.0 );
89
90 // Update LUT
91 self->rlut[ i ] = lrint(r * 255.0);
92 self->glut[ i ] = lrint(g * 255.0);
93 self->blut[ i ] = lrint(b * 255.0);
94 }
95
96 // Store the values that created the LUT so that
97 // changes can be detected.
98 self->rlift = rlift;
99 self->glift = glift;
100 self->blift = blift;
101 self->rgamma = rgamma;
102 self->ggamma = ggamma;
103 self->bgamma = bgamma;
104 self->rgain = rgain;
105 self->ggain = ggain;
106 self->bgain = bgain;
107 }
108 }
109
apply_lut(mlt_filter filter,uint8_t * image,mlt_image_format format,int width,int height)110 static void apply_lut( mlt_filter filter, uint8_t* image, mlt_image_format format, int width, int height )
111 {
112 private_data* self = (private_data*)filter->child;
113 uint8_t* rlut = malloc( sizeof(self->rlut) );
114 uint8_t* glut = malloc( sizeof(self->glut) );
115 uint8_t* blut = malloc( sizeof(self->blut) );
116 int total = width * height + 1;
117 uint8_t* sample = image;
118
119 // Copy the LUT so that we can be frame-thread safe.
120 mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
121 memcpy( rlut, self->rlut, sizeof(self->rlut) );
122 memcpy( glut, self->glut, sizeof(self->glut) );
123 memcpy( blut, self->blut, sizeof(self->blut) );
124 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
125
126 switch( format )
127 {
128 case mlt_image_rgb:
129 while( --total )
130 {
131 *sample = rlut[ *sample ];
132 sample++;
133 *sample = glut[ *sample ];
134 sample++;
135 *sample = blut[ *sample ];
136 sample++;
137 }
138 break;
139 case mlt_image_rgba:
140 while( --total )
141 {
142 *sample = rlut[ *sample ];
143 sample++;
144 *sample = glut[ *sample ];
145 sample++;
146 *sample = blut[ *sample ];
147 sample++;
148 sample++; // Skip alpha
149 }
150 break;
151 default:
152 mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid image format: %s\n", mlt_image_format_name( format ) );
153 break;
154 }
155 free( rlut );
156 free( glut );
157 free( blut );
158 }
159
filter_get_image(mlt_frame frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int writable)160 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
161 {
162 mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame );
163 int error = 0;
164
165
166 // Regenerate the LUT if necessary
167 mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
168 refresh_lut( filter, frame );
169 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
170
171 // Make sure the format is acceptable
172 if( *format != mlt_image_rgb && *format != mlt_image_rgba )
173 {
174 *format = mlt_image_rgb;
175 }
176
177 // Get the image
178 writable = 1;
179 error = mlt_frame_get_image( frame, image, format, width, height, writable );
180
181 // Apply the LUT
182 if( !error )
183 {
184 apply_lut( filter, *image, *format, *width, *height );
185 }
186
187 return error;
188 }
189
filter_process(mlt_filter filter,mlt_frame frame)190 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
191 {
192 mlt_frame_push_service( frame, filter );
193 mlt_frame_push_get_image( frame, filter_get_image );
194 return frame;
195 }
196
filter_close(mlt_filter filter)197 static void filter_close( mlt_filter filter )
198 {
199 private_data* self = (private_data*)filter->child;
200
201 free( self );
202 filter->child = NULL;
203 filter->close = NULL;
204 filter->parent.close = NULL;
205 mlt_service_close( &filter->parent );
206 }
207
filter_lift_gamma_gain_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)208 mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
209 {
210 mlt_filter filter = mlt_filter_new();
211 private_data* self = (private_data*)calloc( 1, sizeof(private_data) );
212 int i = 0;
213
214 if ( filter && self )
215 {
216 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
217
218 // Initialize self data
219 for( i = 0; i < 256; i++ )
220 {
221 self->rlut[i] = i;
222 self->glut[i] = i;
223 self->blut[i] = i;
224 }
225 self->rlift = self->glift = self->blift = 0.0;
226 self->rgamma = self->ggamma = self->bgamma = 1.0;
227 self->rgain = self->ggain = self->bgain = 1.0;
228
229 // Initialize filter properties
230 mlt_properties_set_double( properties, "lift_r", self->rlift );
231 mlt_properties_set_double( properties, "lift_g", self->glift );
232 mlt_properties_set_double( properties, "lift_b", self->blift );
233 mlt_properties_set_double( properties, "gamma_r", self->rgamma );
234 mlt_properties_set_double( properties, "gamma_g", self->ggamma );
235 mlt_properties_set_double( properties, "gamma_b", self->bgamma );
236 mlt_properties_set_double( properties, "gain_r", self->rgain );
237 mlt_properties_set_double( properties, "gain_g", self->ggain );
238 mlt_properties_set_double( properties, "gain_b", self->bgain );
239
240 filter->close = filter_close;
241 filter->process = filter_process;
242 filter->child = self;
243 }
244 else
245 {
246 mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter lift_gamma_gain init failed\n" );
247 mlt_filter_close( filter );
248 filter = NULL;
249 free( self );
250 }
251
252 return filter;
253 }
254