1 /*
2  * filter_brightness.c -- brightness, fade, and opacity filter
3  * Copyright (C) 2003-2021 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_filter.h>
21 #include <framework/mlt_frame.h>
22 #include <framework/mlt_slices.h>
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 
28 
29 struct sliced_desc
30 {
31 	uint8_t *image;
32 	int rgba;
33 	int width, height;
34 	double level;
35 	double alpha_level;
36 	uint8_t* alpha;
37 };
38 
sliced_proc(int id,int index,int jobs,void * cookie)39 static int sliced_proc(int id, int index, int jobs, void* cookie)
40 {
41 	(void) id; // unused
42 	struct sliced_desc ctx = *((struct sliced_desc*) cookie);
43 	int slice_height = (ctx.height + jobs - 1) / jobs;
44 	int slice_offset = index * slice_height * ctx.width;
45 	slice_height = MIN(slice_height, ctx.height - index * slice_height);
46 
47 	// Only process if level is something other than 1
48 	if (ctx.level != 1.0) {
49 		int i = ctx.width * slice_height + 1;
50 		uint8_t *p = ctx.image + (slice_offset * 2);
51 		int32_t m = ctx.level * (1 << 16);
52 		int32_t n = 128 * ((1 << 16 ) - m);
53 
54 		for (; --i; p += 2) {
55 			p[0] = CLAMP((p[0] * m) >> 16, 16, 235);
56 			p[1] = CLAMP((p[1] * m + n) >> 16, 16, 240);
57 		}
58 	}
59 
60 	// Process the alpha channel if requested.
61 	if (ctx.alpha_level != 1.0) {
62 		int32_t m = ctx.alpha_level * (1 << 16);
63 		int i = ctx.width * slice_height + 1;
64 
65 		if (ctx.rgba) {
66 			uint8_t *p = ctx.image + (slice_offset * 4) + 3;
67 			for (; --i; p += 4) {
68 				p[0] = (p[0] * m) >> 16;
69 			}
70 		} else {
71 			uint8_t *p = ctx.alpha + slice_offset;
72 			for (; --i; ++p) {
73 				p[0] = (p[0] * m) >> 16;
74 			}
75 		}
76 	}
77 	return 0;
78 }
79 
80 /** Do it :-).
81 */
82 
filter_get_image(mlt_frame frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int writable)83 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
84 {
85 	mlt_filter filter =  (mlt_filter) mlt_frame_pop_service( frame );
86 	mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
87 	mlt_position position = mlt_filter_get_position( filter, frame );
88 	mlt_position length = mlt_filter_get_length2( filter, frame );
89 	double level = 1.0;
90 
91 	// Use animated "level" property only if it has been set since init
92 	char* level_property = mlt_properties_get( properties, "level" );
93 	if ( level_property != NULL )
94 	{
95 		level = mlt_properties_anim_get_double( properties, "level", position, length );
96 	}
97 	else
98 	{
99 		// Get level using old "start,"end" mechanics
100 		// Get the starting brightness level
101 		level = fabs( mlt_properties_get_double( properties, "start" ) );
102 
103 		// If there is an end adjust gain to the range
104 		if ( mlt_properties_get( properties, "end" ) != NULL )
105 		{
106 			// Determine the time position of this frame in the transition duration
107 			double end = fabs( mlt_properties_get_double( properties, "end" ) );
108 			level += ( end - level ) * mlt_filter_get_progress( filter, frame );
109 		}
110 	}
111 
112 	// Do not cause an image conversion unless there is real work to do.
113 	if ( level != 1.0 )
114 		*format = mlt_image_yuv422;
115 
116 	// Get the image
117 	int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
118 
119 	// Only process if we have no error.
120 	if (!error) {
121 		int threads = mlt_properties_get_int(properties, "threads");
122 		threads = CLAMP(threads, 0, mlt_slices_count_normal());
123 		double alpha = mlt_properties_get(properties, "alpha")? MIN(mlt_properties_anim_get_double(properties, "alpha", position, length), 1.0) : 1.0;
124 		struct sliced_desc desc = {
125 			.image = *image,
126 			.rgba = (*format == mlt_image_rgb24a),
127 			.width = *width,
128 			.height = *height,
129 			.level = (*format == mlt_image_yuv422)? level : 1.0,
130 			.alpha_level = alpha >= 0.0 ? alpha : level,
131 			.alpha = mlt_frame_get_alpha_mask(frame)
132 		};
133 		if (threads == 1) {
134 			sliced_proc(0, 0, 1, &desc);
135 		} else {
136 			mlt_slices_run_normal(threads, sliced_proc, &desc);
137 		}
138 	}
139 
140 	return error;
141 }
142 
143 /** Filter processing.
144 */
145 
filter_process(mlt_filter filter,mlt_frame frame)146 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
147 {
148 	mlt_frame_push_service( frame, filter );
149 	mlt_frame_push_get_image( frame, filter_get_image );
150 
151 	return frame;
152 }
153 
154 /** Constructor for the filter.
155 */
156 
filter_brightness_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)157 mlt_filter filter_brightness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
158 {
159 	mlt_filter filter = mlt_filter_new( );
160 	if ( filter != NULL )
161 	{
162 		filter->process = filter_process;
163 		mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "start", arg == NULL ? "1" : arg );
164 		mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "level", NULL );
165 	}
166 	return filter;
167 }
168 
169