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