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